Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions modules/nixos/hosts/masthead/conntrack/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
config,
lib,
pkgs,
namespace,
...
}:

with lib;
let
cfg = config.${namespace}.hosts.masthead;
in
{
config = mkIf cfg.enable {
environment.etc."conntrackd/conntrackd.conf".text = ''
Sync {
Mode FTFW {
ResendQueueSize 131072
CommitTimeout 180
PurgeTimeout 5
ACKWindowSize 300
DisableExternalCache Off
}

Multicast {
IPv4_address 225.0.0.50
IPv4_interface vlan40
Port 3780
Group 3780
}
}

General {
HashSize 32768
HashLimit 131072
Syslog on
LockFile /var/lock/conntrack.lock
UNIX {
Path /var/run/conntrackd.ctl
Backlog 20
}
NetlinkBufferSize 2097152
NetlinkBufferSizeMaxGrowth 8388608
Filter From Userspace {
Protocol Accept {
TCP
SCTP
DCCP
}
Address Ignore {
IPv4_address 127.0.0.1
}
}
}
'';

systemd.services.conntrackd = {
description = "Connection tracking state synchronization daemon";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
path = [
pkgs.conntrack-tools
pkgs.iproute2
];
serviceConfig = {
ExecStart = "${pkgs.conntrack-tools}/bin/conntrackd -C /etc/conntrackd/conntrackd.conf";
Restart = "always";
};
};
};
}
72 changes: 72 additions & 0 deletions modules/nixos/hosts/masthead/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,26 @@ let
cfg = config.${namespace}.hosts.masthead;
in
{
imports = [
./vrrp
./multi-wan
./conntrack
];

options.${namespace}.hosts.masthead = with types; {
enable = mkBoolOpt false "Whether or not to enable the masthead router base config.";
role = mkOpt (types.enum [
"primary"
"backup"
]) "primary" "The role of the masthead router.";
wanMac = mkOpt types.str "00:00:00:00:00:00" "MAC address for WAN spoofing.";
wanVip = mkOpt types.str "203.0.113.100" "Virtual IP for WAN interface.";
lanVip = mkOpt types.str "172.16.1.1" "Virtual IP for LAN interface.";
vlan10Vip = mkOpt types.str "192.168.10.1" "Virtual IP for VLAN 10.";
vlan21Vip = mkOpt types.str "192.168.21.1" "Virtual IP for VLAN 21.";
vlan22Vip = mkOpt types.str "192.168.22.1" "Virtual IP for VLAN 22.";
vlan30Vip = mkOpt types.str "192.168.30.1" "Virtual IP for VLAN 30.";
healthCheckIp = mkOpt types.str "8.8.8.8" "IP address for Multi-WAN health check.";
};

config = mkIf cfg.enable {
Expand All @@ -29,6 +43,60 @@ in
};
};

networking.interfaces.lan0 = {
ipv4.addresses = [
{
address = if cfg.role == "primary" then "172.16.1.2" else "172.16.1.3";
prefixLength = 24;
}
];
};

networking.interfaces.vlan10 = {
ipv4.addresses = [
{
address = if cfg.role == "primary" then "192.168.10.2" else "192.168.10.3";
prefixLength = 24;
}
];
};

networking.interfaces.vlan21 = {
ipv4.addresses = [
{
address = if cfg.role == "primary" then "192.168.21.2" else "192.168.21.3";
prefixLength = 24;
}
];
};

networking.interfaces.vlan22 = {
ipv4.addresses = [
{
address = if cfg.role == "primary" then "192.168.22.2" else "192.168.22.3";
prefixLength = 24;
}
];
};

networking.interfaces.vlan30 = {
ipv4.addresses = [
{
address = if cfg.role == "primary" then "192.168.30.2" else "192.168.30.3";
prefixLength = 24;
}
];
};

networking.interfaces.vlan40 = {
ipv4.addresses = [
{
address = if cfg.role == "primary" then "169.254.255.1" else "169.254.255.2";
prefixLength = 24;
}
];
};

networking.vlans = {
vlan1 = {
id = 1;
Expand All @@ -50,6 +118,10 @@ in
id = 30;
interface = "lan0";
};
vlan40 = {
id = 40;
interface = "lan0";
};
};

# Configure DNS resolvers
Expand Down
53 changes: 53 additions & 0 deletions modules/nixos/hosts/masthead/multi-wan/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
config,
lib,
pkgs,
namespace,
...
}:

with lib;
let
cfg = config.${namespace}.hosts.masthead;
in
{
config = mkIf cfg.enable {
systemd.services.multi-wan-healthcheck = {
description = "Multi-WAN Health Check and Failover Service";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
path = [
pkgs.iputils
pkgs.iproute2
pkgs.gawk
];

script = ''
set -euo pipefail

while true; do
# Dynamically resolve gateway via wan0
GATEWAY=$(ip -4 route show default dev wan0 2>/dev/null | awk '{print $3}' | head -n 1 || true)

if [ -n "$GATEWAY" ]; then
if ping -I wan0 -c 1 -W 2 "${cfg.healthCheckIp}" > /dev/null 2>&1; then
# Ping succeeded. Ensure default route exists with appropriate metric
ip route replace default via "$GATEWAY" dev wan0 metric 10
else
# Ping failed. Set metric to 1000 so it acts as disabled but gateway remains discoverable
ip route replace default via "$GATEWAY" dev wan0 metric 1000 || true
fi
fi

sleep 10
done
'';

serviceConfig = {
Restart = "always";
RestartSec = "5s";
};
};
};
}
83 changes: 83 additions & 0 deletions modules/nixos/hosts/masthead/vrrp/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
config,
lib,
pkgs,
namespace,
...
}:

with lib;
let
cfg = config.${namespace}.hosts.masthead;
priority = if cfg.role == "primary" then 255 else 100;
in
{
config = mkIf cfg.enable {
networking.vrrp.enable = true;

networking.vrrp.vrrpInstances = {
lan0 = {
virtualRouterId = 20;
priority = priority;
interface = "lan0";
virtualIPs = [
{
address = cfg.lanVip;
prefixLength = 24;
}
];
notifyMaster = "${pkgs.writeShellScript "notify-master-lan0" ''
${pkgs.iproute2}/bin/ip link set dev wan0 address ${cfg.wanMac}
${pkgs.iproute2}/bin/ip link set dev wan0 up
''}";
notifyBackup = "${pkgs.writeShellScript "notify-backup-lan0" ''
${pkgs.iproute2}/bin/ip link set dev wan0 down
''}";
};
vlan10 = {
virtualRouterId = 30;
priority = priority;
interface = "vlan10";
virtualIPs = [
{
address = cfg.vlan10Vip;
prefixLength = 24;
}
];
};
vlan21 = {
virtualRouterId = 40;
priority = priority;
interface = "vlan21";
virtualIPs = [
{
address = cfg.vlan21Vip;
prefixLength = 24;
}
];
};
vlan22 = {
virtualRouterId = 50;
priority = priority;
interface = "vlan22";
virtualIPs = [
{
address = cfg.vlan22Vip;
prefixLength = 24;
}
];
};
vlan30 = {
virtualRouterId = 60;
priority = priority;
interface = "vlan30";
virtualIPs = [
{
address = cfg.vlan30Vip;
prefixLength = 24;
}
];
};
};
};
}