nixos/home: De-share Virgin Media modem specifics from routing-common
The modem management subnet is stream-specific (it shares stream's `wan`
interface), so it no longer belongs in the shared routing-common. Drop the
`modem` network assignment and configure the address directly on stream's
`wan` (modemV4). The firewall reject and DNS wan-A-record skipBroadcasts that
existed only because of the modem move to per-box options
`my.homeRouter.{firewall.untrustedRejectV4,dns.wanSkipBroadcasts}`, set solely
by stream. river needs neither: its ONT sits on a separate interface already
blocked by the interface-based forward rules.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,12 +11,6 @@ in
|
|||||||
{
|
{
|
||||||
nixos.systems."${name}" = {
|
nixos.systems."${name}" = {
|
||||||
assignments = {
|
assignments = {
|
||||||
modem = {
|
|
||||||
ipv4 = {
|
|
||||||
address = net.cidr.host (254 - index) prefixes.modem.v4;
|
|
||||||
gateway = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
core = {
|
core = {
|
||||||
name = "${name}-core";
|
name = "${name}-core";
|
||||||
inherit domain;
|
inherit domain;
|
||||||
@@ -100,9 +94,11 @@ in
|
|||||||
|
|
||||||
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
|
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) mkIf mkMerge mkForce;
|
inherit (lib) mkIf mkMerge mkForce optionalString concatStringsSep;
|
||||||
inherit (lib.my) networkdAssignment;
|
inherit (lib.my) mkOpt' networkdAssignment;
|
||||||
inherit (lib.my.c) networkd;
|
inherit (lib.my.c) networkd;
|
||||||
|
|
||||||
|
cfg = config.my.homeRouter;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = map (m: import m index) [
|
imports = map (m: import m index) [
|
||||||
@@ -112,6 +108,20 @@ in
|
|||||||
./kea.nix
|
./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 = {
|
config = {
|
||||||
environment = {
|
environment = {
|
||||||
systemPackages = with pkgs; [
|
systemPackages = with pkgs; [
|
||||||
@@ -338,7 +348,7 @@ in
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
chain filter-untrusted {
|
chain filter-untrusted {
|
||||||
ip daddr ${prefixes.modem.v4} reject
|
${optionalString (cfg.firewall.untrustedRejectV4 != [ ]) "ip daddr { ${concatStringsSep ", " cfg.firewall.untrustedRejectV4} } reject"}
|
||||||
oifname wan accept
|
oifname wan accept
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ in
|
|||||||
${name} IN LUA ${lib.my.dns.ifaceA {
|
${name} IN LUA ${lib.my.dns.ifaceA {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
iface = "wan";
|
iface = "wan";
|
||||||
skipBroadcasts = [ (lib.my.netBroadcast prefixes.modem.v4) ];
|
skipBroadcasts = config.my.homeRouter.dns.wanSkipBroadcasts;
|
||||||
}}
|
}}
|
||||||
${otherName} IN LUA ${lib.my.dns.lookupIP {
|
${otherName} IN LUA ${lib.my.dns.lookupIP {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
|
|||||||
+35
-22
@@ -6,11 +6,16 @@
|
|||||||
nixpkgs = "mine";
|
nixpkgs = "mine";
|
||||||
home-manager = "mine";
|
home-manager = "mine";
|
||||||
|
|
||||||
configuration = { lib, pkgs, config, assignments, ... }:
|
configuration = { lib, pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) mkMerge;
|
inherit (lib) mkMerge;
|
||||||
inherit (lib.my) networkdAssignment;
|
inherit (lib.my) net;
|
||||||
inherit (lib.my.c) networkd;
|
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
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./routing-common/mstpd.nix ];
|
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
|
# 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
|
# 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.
|
# public IP, so gating on wait-online lets it start unoriented and never connect.
|
||||||
@@ -190,26 +195,28 @@
|
|||||||
CompensationMode=none
|
CompensationMode=none
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
"50-wan" = mkMerge [
|
"50-wan" = {
|
||||||
(networkdAssignment "wan" assignments.modem)
|
matchConfig.Name = "wan";
|
||||||
{
|
# Static modem-management address alongside the DHCP public lease. It has no
|
||||||
matchConfig.Name = "wan";
|
# gateway, so the wan-wait-online gate keys off the DHCP default route instead.
|
||||||
DHCP = "ipv4";
|
address = [ "${modemV4}/24" ];
|
||||||
dns = [ "127.0.0.1" "::1" ];
|
DHCP = "ipv4";
|
||||||
dhcpV4Config.UseDNS = false;
|
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 = {
|
qdiscConfig = {
|
||||||
Parent = "ingress";
|
Parent = "ingress";
|
||||||
Handle = "0xffff";
|
Handle = "0xffff";
|
||||||
};
|
};
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
[CAKE]
|
[CAKE]
|
||||||
Parent=root
|
Parent=root
|
||||||
Bandwidth=48M
|
Bandwidth=48M
|
||||||
RTTSec=50ms
|
RTTSec=50ms
|
||||||
'';
|
'';
|
||||||
}
|
};
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -218,6 +225,12 @@
|
|||||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb";
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb";
|
||||||
};
|
};
|
||||||
server.enable = true;
|
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";
|
# deploy.node.hostname = "192.168.68.2";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user