From 822386877445ccb795c38034e4046e5f1ad5d720 Mon Sep 17 00:00:00 2001 From: heather Date: Sun, 27 Sep 2026 00:09:28 +0000 Subject: [PATCH] heather: fix BGP default precedence, YC fallback and WAN BGP exposure --- hosts/heather/astrnet.nix | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/hosts/heather/astrnet.nix b/hosts/heather/astrnet.nix index 3b4e6d3..0780b46 100644 --- a/hosts/heather/astrnet.nix +++ b/hosts/heather/astrnet.nix @@ -55,6 +55,11 @@ let }]; }; in { + # DHCP keeps the WAN address/DNS but not a competing kernel default route. + # FRR otherwise prefers the existing kernel route over the BGP default; + # WAN failback is provided by policy table 100 below. + networking.dhcpcd.extraConfig = "nogateway"; + networking.wireguard.interfaces = { # The fe80:: link-locals are required: without one FRR's IPv6 BGP # next-hop selection fails and the v6 session never leaves Connect. @@ -92,6 +97,8 @@ in { type filter hook input priority -150; policy accept; # Full inbound access from AstrNet to Heather, IPv4 and IPv6. iifname { "wg-mic-der", "wg-mic-bd8" } accept + # bgpd listens on 0.0.0.0 and ::; never expose BGP on public WAN. + iifname "enp7s0" tcp dport 179 drop } chain forward { type filter hook forward priority -150; policy accept; @@ -131,6 +138,7 @@ in { requiredBy = [ "frr.service" ]; path = [ pkgs.iproute2 ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; + unitConfig.OnFailure = "astrnet-wan-recovery.service"; script = '' ip route replace default via ${wanGw4} dev ${wanIf} table ${wanTable} '' + lib.optionalString (wanGw6 != "") '' @@ -149,8 +157,21 @@ in { '' + lib.concatMapStrings (r: '' ip ${r.fam} rule del ${r.sel} lookup ${wanTable} priority 1000 2>/dev/null || true ip ${r.fam} rule add ${r.sel} lookup ${wanTable} priority 1000 - '') wanRules; + '') wanRules + '' + # Main BGP default wins; WAN table 100 is used if BGP withdraws it. + ip -4 rule del lookup main priority 2000 2>/dev/null || true + ip -4 rule add lookup main priority 2000 + ip -4 rule del lookup ${wanTable} priority 2001 2>/dev/null || true + ip -4 rule add lookup ${wanTable} priority 2001 + # A kernel default in main outranks BGP in FRR itself, regardless of + # Linux route metric. Remove it only AFTER the fallback rule is live. + ip -4 route del default via ${wanGw4} dev ${wanIf} 2>/dev/null || true + ''; preStop = '' + # On rollback restore the plain WAN path first. + ip -4 route replace default via ${wanGw4} dev ${wanIf} metric 1002 || true + ip -4 rule del lookup main priority 2000 || true + ip -4 rule del lookup ${wanTable} priority 2001 || true ip -4 rule del to 169.254.169.254 lookup ${wanTable} priority 998 || true ip -4 rule del lookup main suppress_prefixlength 0 priority 999 || true ip -6 rule del lookup main suppress_prefixlength 0 priority 999 || true @@ -159,6 +180,14 @@ in { '') wanRules; }; + # If policy setup fails, leave a normal WAN default rather than a stranded host. + systemd.services.astrnet-wan-recovery = { + description = "Restore YC WAN route if AstrNet policy fails"; + path = [ pkgs.iproute2 ]; + serviceConfig.Type = "oneshot"; + script = "ip -4 route replace default via ${wanGw4} dev ${wanIf} metric 1002"; + }; + # BGP only inside the tunnels, never on the public interface. networking.firewall.interfaces."wg-mic-bd8".allowedTCPPorts = [ 179 ]; networking.firewall.interfaces."wg-mic-der".allowedTCPPorts = [ 179 ];