diff --git a/nixos/boxes/home/routing-common/default.nix b/nixos/boxes/home/routing-common/default.nix index db228fd..5f17401 100644 --- a/nixos/boxes/home/routing-common/default.nix +++ b/nixos/boxes/home/routing-common/default.nix @@ -161,7 +161,12 @@ in systemd.services = { ipsec = { after = [ "wan-online.target" ]; - wantedBy = [ "wan-online.target" ]; + # strongswan/libreswan force wantedBy=multi-user.target; drop it so the + # target is a true gate rather than mere ordering. This matters most on + # river, where the target is hook-driven and not in the boot transaction, + # so plain ordering wouldn't hold ipsec back at all. partOf re-loads ipsec + # (re-orienting its connections) whenever the WAN drops and returns. + wantedBy = mkForce [ "wan-online.target" ]; partOf = [ "wan-online.target" ]; }; diff --git a/nixos/boxes/home/routing-common/dns.nix b/nixos/boxes/home/routing-common/dns.nix index 1a80c61..04b8368 100644 --- a/nixos/boxes/home/routing-common/dns.nix +++ b/nixos/boxes/home/routing-common/dns.nix @@ -13,6 +13,13 @@ let in { config = { + # Let pdns-recursor bind the VRRP VIPs even on the backup, where the addresses + # aren't present locally + boot.kernel.sysctl = { + "net.ipv4.ip_nonlocal_bind" = 1; + "net.ipv6.ip_nonlocal_bind" = 1; + }; + my = { secrets.files = { "home/pdns/auth.conf" = { @@ -40,6 +47,10 @@ in "127.0.0.1" "::1" assignments.hi.ipv4.address assignments.hi.ipv6.address assignments.lo.ipv4.address assignments.lo.ipv6.address + # VRRP VIPs: DNS follows the master, so clients only ever have one + # (always-live) resolver address and never hang on a dead router + vips.hi.v4 vips.hi.v6 + vips.lo.v4 vips.lo.v6 ]; allow_from = [ "127.0.0.0/8" "::1/128" diff --git a/nixos/boxes/home/routing-common/kea.nix b/nixos/boxes/home/routing-common/kea.nix index 7aa6438..091f42d 100644 --- a/nixos/boxes/home/routing-common/kea.nix +++ b/nixos/boxes/home/routing-common/kea.nix @@ -83,7 +83,8 @@ in } { name = "domain-name-servers"; - data = "${net.cidr.host 1 prefixes.hi.v4}, ${net.cidr.host 2 prefixes.hi.v4}"; + # VRRP VIP so DNS follows the master and clients never hit a dead router + data = vips.hi.v4; } { name = "interface-mtu"; @@ -116,7 +117,8 @@ in } { name = "domain-name-servers"; - data = "${net.cidr.host 1 prefixes.lo.v4}, ${net.cidr.host 2 prefixes.lo.v4}"; + # VRRP VIP so DNS follows the master and clients never hit a dead router + data = vips.lo.v4; } ]; pools = [ diff --git a/nixos/boxes/home/routing-common/radvd.nix b/nixos/boxes/home/routing-common/radvd.nix index fe186b5..1764ed0 100644 --- a/nixos/boxes/home/routing-common/radvd.nix +++ b/nixos/boxes/home/routing-common/radvd.nix @@ -2,7 +2,14 @@ index: { lib, pkgs, ... }: let inherit (lib) mkForce concatMapStringsSep; inherit (lib.my) net; - inherit (lib.my.c.home) domain prefixes; + inherit (lib.my.c.home) domain prefixes vips; + + # untrusted uses external (Cloudflare) resolvers, matching the v4 kea config; + # trusted VLANs use the internal recursor via its floating VRRP VIP + rdnss = name: + if name == "untrusted" + then "2606:4700:4700::1111 2606:4700:4700::1001" + else vips."${name}".v6; mkInterface = name: '' interface lan-${name} { @@ -10,7 +17,7 @@ let AdvRASrcAddress { fe80::1; }; AdvLinkMTU ${toString prefixes."${name}".mtu}; prefix ${prefixes."${name}".v6} {}; - RDNSS ${net.cidr.host 1 prefixes."${name}".v6} ${net.cidr.host 2 prefixes."${name}".v6} {}; + RDNSS ${rdnss name} {}; DNSSL ${domain} dyn.${domain} ${lib.my.c.colony.domain} ${lib.my.c.britway.domain} {}; }; ''; diff --git a/nixos/boxes/home/stream.nix b/nixos/boxes/home/stream.nix index 0ec849b..20f1b7c 100644 --- a/nixos/boxes/home/stream.nix +++ b/nixos/boxes/home/stream.nix @@ -77,11 +77,29 @@ }; }; - # WAN is a plain networkd-managed DHCP link here; gate the shared wan-online - # target on networkd reporting it online. + # wan carries a permanent static modem-management address (assignments.modem) + # alongside the DHCP public IP, so wait-online@wan reports "online" as soon as + # the static address is up - before the DHCP lease arrives. ipsec's left= is the + # public IP, so gating on wait-online lets it start unoriented and never connect. + # Gate instead on the DHCP default route, which only exists once the public lease + # is up (the static modem address has no gateway). + systemd.services.wan-wait-online = { + description = "Wait for the wan default route (public DHCP lease)"; + after = [ "systemd-networkd.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + TimeoutStartSec = "300"; + }; + script = '' + until [ -n "$(${pkgs.iproute2}/bin/ip -4 route show default dev wan)" ]; do + sleep 1 + done + ''; + }; systemd.targets.wan-online = { - requires = [ "systemd-networkd-wait-online@wan.service" ]; - after = [ "systemd-networkd-wait-online@wan.service" ]; + requires = [ "wan-wait-online.service" ]; + after = [ "wan-wait-online.service" ]; wantedBy = [ "multi-user.target" ]; };