diff --git a/nixos/boxes/home/routing-common/default.nix b/nixos/boxes/home/routing-common/default.nix index 5f17401..7322457 100644 --- a/nixos/boxes/home/routing-common/default.nix +++ b/nixos/boxes/home/routing-common/default.nix @@ -11,12 +11,6 @@ in { nixos.systems."${name}" = { assignments = { - modem = { - ipv4 = { - address = net.cidr.host (254 - index) prefixes.modem.v4; - gateway = null; - }; - }; core = { name = "${name}-core"; inherit domain; @@ -100,9 +94,11 @@ in configuration = { lib, pkgs, config, assignments, allAssignments, ... }: let - inherit (lib) mkIf mkMerge mkForce; - inherit (lib.my) networkdAssignment; + inherit (lib) mkIf mkMerge mkForce optionalString concatStringsSep; + inherit (lib.my) mkOpt' networkdAssignment; inherit (lib.my.c) networkd; + + cfg = config.my.homeRouter; in { imports = map (m: import m index) [ @@ -112,6 +108,20 @@ in ./kea.nix ]; + # Per-box WAN-management specifics: the Virgin Media modem on stream lives on the `wan` + # interface itself, whereas river's ONT sits on its own interface. Declared as options the + # box sets so routing-common itself carries no modem/ONT knowledge. + options.my.homeRouter = with lib.types; { + dns.wanSkipBroadcasts = mkOpt' (listOf str) [ ] '' + Broadcast addresses to exclude when auto-selecting the router's own `wan` A record, + for extra static subnets that share the `wan` interface. + ''; + firewall.untrustedRejectV4 = mkOpt' (listOf str) [ ] '' + IPv4 prefixes untrusted clients must be explicitly rejected from reaching. Only needed + for subnets sharing the `wan` interface, since `wan` egress is otherwise accepted. + ''; + }; + config = { environment = { systemPackages = with pkgs; [ @@ -338,7 +348,7 @@ in return } chain filter-untrusted { - ip daddr ${prefixes.modem.v4} reject + ${optionalString (cfg.firewall.untrustedRejectV4 != [ ]) "ip daddr { ${concatStringsSep ", " cfg.firewall.untrustedRejectV4} } reject"} oifname wan accept return } diff --git a/nixos/boxes/home/routing-common/dns.nix b/nixos/boxes/home/routing-common/dns.nix index 540691f..3bb3b60 100644 --- a/nixos/boxes/home/routing-common/dns.nix +++ b/nixos/boxes/home/routing-common/dns.nix @@ -206,7 +206,7 @@ in ${name} IN LUA ${lib.my.dns.ifaceA { inherit pkgs; iface = "wan"; - skipBroadcasts = [ (lib.my.netBroadcast prefixes.modem.v4) ]; + skipBroadcasts = config.my.homeRouter.dns.wanSkipBroadcasts; }} ${otherName} IN LUA ${lib.my.dns.lookupIP { inherit pkgs; diff --git a/nixos/boxes/home/stream.nix b/nixos/boxes/home/stream.nix index 20f1b7c..912ca45 100644 --- a/nixos/boxes/home/stream.nix +++ b/nixos/boxes/home/stream.nix @@ -6,11 +6,16 @@ nixpkgs = "mine"; home-manager = "mine"; - configuration = { lib, pkgs, config, assignments, ... }: + configuration = { lib, pkgs, config, ... }: let inherit (lib) mkMerge; - inherit (lib.my) networkdAssignment; + inherit (lib.my) net; inherit (lib.my.c) networkd; + inherit (lib.my.c.home) prefixes; + + # Static address on the Virgin Media modem's management subnet. Kept as a plain interface + # address (not a network assignment) since it's local to this box's WAN uplink. + modemV4 = net.cidr.host 100 prefixes.modem.v4; in { imports = [ ./routing-common/mstpd.nix ]; @@ -77,7 +82,7 @@ }; }; - # wan carries a permanent static modem-management address (assignments.modem) + # wan carries a permanent static modem-management address (modemV4) # 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. @@ -190,26 +195,28 @@ CompensationMode=none ''; }; - "50-wan" = mkMerge [ - (networkdAssignment "wan" assignments.modem) - { - matchConfig.Name = "wan"; - DHCP = "ipv4"; - dns = [ "127.0.0.1" "::1" ]; - dhcpV4Config.UseDNS = false; + "50-wan" = { + matchConfig.Name = "wan"; + # Static modem-management address alongside the DHCP public lease. It has no + # gateway, so the wan-wait-online gate keys off the DHCP default route instead. + address = [ "${modemV4}/24" ]; + DHCP = "ipv4"; + dns = [ "127.0.0.1" "::1" ]; + dhcpV4Config.UseDNS = false; + # IPv4-only WAN (public IPv6 arrives over the tunnel, not this link). + networkConfig.IPv6AcceptRA = false; - qdiscConfig = { - Parent = "ingress"; - Handle = "0xffff"; - }; - extraConfig = '' - [CAKE] - Parent=root - Bandwidth=48M - RTTSec=50ms - ''; - } - ]; + qdiscConfig = { + Parent = "ingress"; + Handle = "0xffff"; + }; + extraConfig = '' + [CAKE] + Parent=root + Bandwidth=48M + RTTSec=50ms + ''; + }; }; }; @@ -218,6 +225,12 @@ key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb"; }; server.enable = true; + # The modem's management subnet shares the `wan` interface: skip its address when + # picking our own wan A record, and reject untrusted clients from reaching it. + homeRouter = { + dns.wanSkipBroadcasts = [ (lib.my.netBroadcast prefixes.modem.v4) ]; + firewall.untrustedRejectV4 = [ prefixes.modem.v4 ]; + }; # deploy.node.hostname = "192.168.68.2"; }; };