Add the home gateway config and tunnel source addresses

- wireguard.<n>.sourceAddresses: source for router-originated traffic
  into a tunnel without an address of its own (PreferredSource on the
  routes); checked at build time.
- examples/home: antigua's routing role ported to the router module
  (PPPoE with IPv6 inside the session, LAN with ULA and reservation,
  local DNS and forward zones, the freifunk/family/mobile tunnels with
  explicit zone rules, ddclient following the PPP device).
  nixosConfigurations.home, packages.installer-home and a flake check
  that builds it.
- PLAN.md: the remaining antigua gaps and the open question about
  services that stay behind the router.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 16:53:14 +01:00
co-authored by Claude Sonnet 5.5
parent c395a43a78
commit 406196adce
8 changed files with 322 additions and 5 deletions
+35
View File
@@ -999,6 +999,40 @@ let
router.lans.main = lan { ipv6.address = "192.168.1.1/24"; };
expect = [ "not an IPv6 address in CIDR" ];
};
wgSourceOk = {
router = {
lans.main = lan { };
wireguard.s2s = wg { sourceAddresses = [ "192.168.10.1" ]; };
};
expect = [ ];
};
wgSourceTwoV4 = {
router = {
lans.main = lan { };
wireguard.s2s = wg {
addresses = [ "10.100.0.1/24" ];
sourceAddresses = [
"192.168.10.1"
"10.100.0.1"
];
};
};
expect = [ "at most one address per family" ];
};
wgSourceForeign = {
router = {
lans.main = lan { };
wireguard.s2s = wg { sourceAddresses = [ "192.168.99.9" ]; };
};
expect = [ "must be the router's address on a LAN or a tunnel" ];
};
wgSourceGarbage = {
router = {
lans.main = lan { };
wireguard.s2s = wg { sourceAddresses = [ "nonsense" ]; };
};
expect = [ "sourceAddresses must be IPv4/IPv6 addresses" ];
};
nonOverlappingLans = {
router.lans = {
a = lan { };
@@ -1049,6 +1083,7 @@ let
"sharedLinkValid"
"pppTransportValid"
"ulaValid"
"wgSourceOk"
"dnsValid"
];
+7
View File
@@ -226,6 +226,8 @@ pkgs.testers.runNixOSTest {
# No address on the tunnel: the site queries the LAN address and
# is allowed because it routes 192.168.50.0/24 through the tunnel.
dns = true;
# Router-originated traffic to the site uses the LAN address.
sourceAddresses = [ "192.168.10.1" ];
peers.siteb = {
publicKey = key.siteb.pub;
endpoint = "203.0.113.60:51821";
@@ -386,6 +388,11 @@ pkgs.testers.runNixOSTest {
siteb.succeed("ping -c 1 -W 2 -I 192.168.50.1 192.168.10.1")
siteb.fail("ping -c 1 -W 2 -I 192.168.50.1 192.168.10.100")
with subtest("site-to-site: router-originated traffic uses the configured source"):
# Without it the kernel would pick an address by itself (the tunnel has none).
router.succeed("ip route get 192.168.50.1 | grep -q 'src 192.168.10.1'")
router.succeed("ping -c 1 -W 2 192.168.50.1")
with subtest("site-to-site: the remote site can use the router's resolver"):
siteb.succeed("dig +short +time=2 +tries=1 -b 192.168.50.1 @192.168.10.1 example.test | grep -qx 203.0.113.1")