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:
@@ -152,10 +152,11 @@ Done: PPPoE on VLAN 1001 over the shared NIC (`eno1`, LAN untagged), DNS records
|
|||||||
Open (in rough order):
|
Open (in rough order):
|
||||||
1. ~~**IPv6 over the PPP session**~~ **done**: `wan.ipv6.transport = "ppp"` (default `native`) enables IPv6CP in pppd, RA + DHCPv6-PD via networkd on the PPP device, PD to the LAN, DHCPv6 replies accepted on the PPP device and the IPv6 MSS clamped to MTU-60 (1432). Allowed on a shared link (single NIC). Tested in `onestick` (ISP with radvd and a minimal DHCPv6-PD responder, since Kea cannot serve point-to-point links); mutation-tested (MSS clamp, DHCPv6 input rule). `ipv6cp-use-ipaddr` is set as on antigua.
|
1. ~~**IPv6 over the PPP session**~~ **done**: `wan.ipv6.transport = "ppp"` (default `native`) enables IPv6CP in pppd, RA + DHCPv6-PD via networkd on the PPP device, PD to the LAN, DHCPv6 replies accepted on the PPP device and the IPv6 MSS clamped to MTU-60 (1432). Allowed on a shared link (single NIC). Tested in `onestick` (ISP with radvd and a minimal DHCPv6-PD responder, since Kea cannot serve point-to-point links); mutation-tested (MSS clamp, DHCPv6 input rule). `ipv6cp-use-ipaddr` is set as on antigua.
|
||||||
2. ~~**ULA on the LAN**~~ **done**: `lans.<n>.ipv6.address` (a ULA /64, e.g. `fd2c:cdef:cde9::1/64`) is assigned, announced via SLAAC (explicit `[IPv6Prefix]`, because networkd announces only the delegated prefix when PD is on) and advertised as RDNSS; Unbound listens on it. Build-time checks: valid IPv6 CIDR, /64, fc00::/7. Tested in `onestick` (client SLAAC ULA, ping and DNS over the ULA, RA captured on the wire) and assertion cases; mutation-tested.
|
2. ~~**ULA on the LAN**~~ **done**: `lans.<n>.ipv6.address` (a ULA /64, e.g. `fd2c:cdef:cde9::1/64`) is assigned, announced via SLAAC (explicit `[IPv6Prefix]`, because networkd announces only the delegated prefix when PD is on) and advertised as RDNSS; Unbound listens on it. Build-time checks: valid IPv6 CIDR, /64, fc00::/7. Tested in `onestick` (client SLAAC ULA, ping and DNS over the ULA, RA captured on the wire) and assertion cases; mutation-tested.
|
||||||
3. **Tunnel trust and addressing**: antigua trusts `family`, `freifunk`, `mobile-clients` fully and gives two of them no address; check the schema covers this (`allowManagement`, `dns`, `internet` per tunnel; `addresses` may be empty?).
|
3. ~~**Tunnel trust and addressing**~~ **done**: tunnels may have no address; the new `wireguard.<n>.sourceAddresses` sets the source (`PreferredSource`) for router-originated traffic into an addressless tunnel (e.g. DNS to Troisdorf; tested, mutation-tested). Antigua's blanket `trustedInterfaces` is spelled out as per-tunnel `allowManagement`/`dns` plus zone rules in `examples/home`. The default device name `wg-<name>` must fit 15 characters (caught by an assertion: `mobile-clients` became `mobile`).
|
||||||
4. **PPP device name**: `wan` on antigua, `pppoe-wan` here; `ddclient` (Cloudflare, uses the interface address) must follow, or make the name configurable.
|
4. ~~**PPP device name**~~ **done**: `ddclient` in `examples/home` uses `config.router.wan.ipv4.interface` (`pppoe-wan`) instead of a hard-coded name. DDNS stays in the host config (not part of the module).
|
||||||
5. **Secrets**: the PPPoE credentials are plaintext in antigua's repo/store (rotate the password); here they are read from `/var/lib/router/pppoe-password`.
|
5. **Credentials**: the PPPoE password is assigned by the ISP (cannot be rotated) and is plaintext in antigua's repo/store; never copy it into a repo again. Here it is read from `/var/lib/router/pppoe-password` (delivered with `router-install --secrets`). The PPPoE username in `examples/home` is a placeholder.
|
||||||
6. DDNS (ddclient) is not part of the module yet; configure it in the host config for now.
|
6. **Services that stay on antigua** (k3s, Gitea, Syncthing, WoW, Traefik) were reachable from the WAN because antigua *was* the router. On the new router they need explicit port forwards to the host's address (`firewall.rules` with `from = "wan"`); antigua's address on the LAN also changes (k3s nodeIP and the `antigua.mch` record point at 192.168.168.254 today). **Open: needs the host's new address and the list of public ports.**
|
||||||
|
7. `examples/home` (`nixosConfigurations.home`, `packages.installer-home`) ports antigua's routing role; its toplevel builds as the `home` check. Not VM-testable as a whole (needs the real ISP); its pieces are covered by the component tests.
|
||||||
|
|
||||||
## Installer and image (in progress)
|
## Installer and image (in progress)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
# The home gateway that replaces antigua's routing role (ported from antigua's
|
||||||
|
# hand-written networkd/pppd/dnsmasq/WireGuard configuration).
|
||||||
|
#
|
||||||
|
# ISP -- switch port --+-- enp2s0 (untagged) LAN 192.168.168.0/24 (+ ULA, + delegated /64)
|
||||||
|
# +-- enp2s0.1001 PPPoE session: IPv4 and IPv6 (IPv6CP)
|
||||||
|
#
|
||||||
|
# Secrets are NOT in this repository. Put them in the directory handed to
|
||||||
|
# `router-install --secrets`; it lands in /var/lib/router on the box:
|
||||||
|
#
|
||||||
|
# pppoe-password the PPPoE password (from the ISP)
|
||||||
|
# cloudflare-ddns-token Cloudflare API token for ddclient
|
||||||
|
# wireguard/freifunk.key private keys (the ones antigua uses today keep the
|
||||||
|
# wireguard/family.key peers working without any change on their side;
|
||||||
|
# wireguard/family.psk never run both boxes with the same keys at once)
|
||||||
|
# wireguard/mobile-clients.key
|
||||||
|
# wireguard/mobile-clients.psk
|
||||||
|
#
|
||||||
|
# Before installing: fill in the PPPoE username below.
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
secrets = "/var/lib/router";
|
||||||
|
k8s = {
|
||||||
|
v4 = "10.43.162.50";
|
||||||
|
v6 = "fd32:66ac:e01f::f294";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ../../modules/installer/layout.nix ];
|
||||||
|
|
||||||
|
networking.hostName = "gateway";
|
||||||
|
time.timeZone = "Europe/Berlin";
|
||||||
|
system.stateVersion = "26.11";
|
||||||
|
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHqyMLgAhEdnrRrd2CeYbphGDMPq/cBDjFJ4q4ItZusn nils@Nilss-Mac-mini.mch.stinnesbeck.com"
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBFxbNNk87tvMxruuKvrryK7iAdf4xgS4xFqjKejNAI6 nils@nixtop"
|
||||||
|
];
|
||||||
|
|
||||||
|
router = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
wan = {
|
||||||
|
interface = "enp2s0";
|
||||||
|
ipv4 = {
|
||||||
|
type = "pppoe";
|
||||||
|
pppoe = {
|
||||||
|
vlan = 1001;
|
||||||
|
# From antigua's networking.nix (pppd `user`); kept out of this file.
|
||||||
|
username = "CHANGE-ME";
|
||||||
|
passwordFile = "${secrets}/pppoe-password";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# IPv6 runs inside the PPP session, with one delegated /64.
|
||||||
|
ipv6 = {
|
||||||
|
enable = true;
|
||||||
|
transport = "ppp";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
lans.lan = {
|
||||||
|
interface = "enp2s0";
|
||||||
|
internet = true;
|
||||||
|
ipv4.address = "192.168.168.254";
|
||||||
|
ipv6 = {
|
||||||
|
pd = true;
|
||||||
|
address = "fd2c:cdef:cde9::1/64";
|
||||||
|
};
|
||||||
|
dhcp = {
|
||||||
|
pool = {
|
||||||
|
start = "192.168.168.100";
|
||||||
|
end = "192.168.168.200";
|
||||||
|
};
|
||||||
|
reservations.truenas = {
|
||||||
|
mac = "a8:a1:59:0b:4b:97";
|
||||||
|
address = "192.168.168.251";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
dns = {
|
||||||
|
domain = "mch.stinnesbeck.com";
|
||||||
|
forwarders = [
|
||||||
|
"1.1.1.1"
|
||||||
|
"2606:4700:4700::1111"
|
||||||
|
];
|
||||||
|
hosts = {
|
||||||
|
gateway = [ "192.168.168.254" ];
|
||||||
|
"photos.stinnesbeck.com" = [
|
||||||
|
k8s.v4
|
||||||
|
k8s.v6
|
||||||
|
];
|
||||||
|
"git.stinnesbeck.com" = [
|
||||||
|
k8s.v4
|
||||||
|
k8s.v6
|
||||||
|
];
|
||||||
|
};
|
||||||
|
overrides."k8s.stinnesbeck.com" = [
|
||||||
|
k8s.v4
|
||||||
|
k8s.v6
|
||||||
|
];
|
||||||
|
forwardZones."tdf.stinnesbeck.com" = [ "10.1.0.254" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
wireguard = {
|
||||||
|
# Provider-style dial-out: only the treasure-maps.com hosts go through
|
||||||
|
# it (antigua accepts 0.0.0.0/0 but routes just these), masqueraded. The
|
||||||
|
# kill switch keeps them from leaking out of the WAN if the tunnel is down.
|
||||||
|
freifunk = {
|
||||||
|
privateKeyFile = "${secrets}/wireguard/freifunk.key";
|
||||||
|
zone = "freifunk";
|
||||||
|
masquerade = true;
|
||||||
|
addresses = [
|
||||||
|
"10.255.1.8/32"
|
||||||
|
"fd80:3ea2:e399:203a::8/128"
|
||||||
|
];
|
||||||
|
peers.supernode = {
|
||||||
|
publicKey = "5B/YTaDPVWVApUyHshJp899iXXlBy8rBqJUpYvKo+1s=";
|
||||||
|
endpoint = "5.9.220.114:42001";
|
||||||
|
persistentKeepalive = 15;
|
||||||
|
allowedIPs = [
|
||||||
|
"185.90.196.65/32"
|
||||||
|
"185.90.196.97/32"
|
||||||
|
"185.90.196.129/32"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Site-to-site with Troisdorf; no transfer network. The site may use the
|
||||||
|
# router (SSH, DNS), and the router reaches it from its LAN addresses.
|
||||||
|
family = {
|
||||||
|
privateKeyFile = "${secrets}/wireguard/family.key";
|
||||||
|
listenPort = 36432;
|
||||||
|
zone = "family";
|
||||||
|
allowManagement = true;
|
||||||
|
dns = true;
|
||||||
|
sourceAddresses = [
|
||||||
|
"192.168.168.254"
|
||||||
|
"fd2c:cdef:cde9::1"
|
||||||
|
];
|
||||||
|
peers.troisdorf = {
|
||||||
|
publicKey = "BsQODhcHnEN4tXn60t72iejZvgYvVNt5BDVK+69+RgM=";
|
||||||
|
endpoint = "fw-tdf.stinnesbeck.com:36432";
|
||||||
|
persistentKeepalive = 25;
|
||||||
|
presharedKeyFile = "${secrets}/wireguard/family.psk";
|
||||||
|
allowedIPs = [
|
||||||
|
"10.1.0.0/24"
|
||||||
|
"fd73:f65e:6b64::/48"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Road warrior.
|
||||||
|
mobile = {
|
||||||
|
privateKeyFile = "${secrets}/wireguard/mobile-clients.key";
|
||||||
|
listenPort = 36433;
|
||||||
|
zone = "mobile";
|
||||||
|
allowManagement = true;
|
||||||
|
dns = true;
|
||||||
|
peers.laptop = {
|
||||||
|
publicKey = "Jts5u8zIs28v6RIEYCCufDDiWXf+gbElT63wUFWQxX4=";
|
||||||
|
persistentKeepalive = 25;
|
||||||
|
presharedKeyFile = "${secrets}/wireguard/mobile-clients.psk";
|
||||||
|
allowedIPs = [ "10.2.0.2/32" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# antigua trusted these interfaces completely and forwarded between all of
|
||||||
|
# them. Here it is spelled out: the LAN and Troisdorf reach each other, the
|
||||||
|
# LAN reaches the freifunk hosts, road warriors reach the LAN. Nothing
|
||||||
|
# initiates from freifunk.
|
||||||
|
firewall.rules = [
|
||||||
|
{
|
||||||
|
from = "lan";
|
||||||
|
to = "family";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
from = "family";
|
||||||
|
to = "lan";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
from = "lan";
|
||||||
|
to = "freifunk";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
from = "mobile";
|
||||||
|
to = "lan";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Dynamic DNS from the PPP device's addresses.
|
||||||
|
services.ddclient = {
|
||||||
|
enable = true;
|
||||||
|
interval = "5min";
|
||||||
|
protocol = "cloudflare";
|
||||||
|
username = "token";
|
||||||
|
passwordFile = "${secrets}/cloudflare-ddns-token";
|
||||||
|
zone = "stinnesbeck.com";
|
||||||
|
domains = [
|
||||||
|
"fw-mch.stinnesbeck.com"
|
||||||
|
"car.k8s.stinnesbeck.com"
|
||||||
|
"photos.stinnesbeck.com"
|
||||||
|
"git.stinnesbeck.com"
|
||||||
|
"link.stinnesbeck.com"
|
||||||
|
"pw.k8s.stinnesbeck.com"
|
||||||
|
];
|
||||||
|
usev4 = "ifv4, ifv4=${config.router.wan.ipv4.interface}";
|
||||||
|
usev6 = "ifv6, ifv6=${config.router.wan.ipv4.interface}";
|
||||||
|
ssl = true;
|
||||||
|
verbose = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -40,6 +40,10 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
packages.x86_64-linux.installer-home =
|
||||||
|
(self.lib.mkInstaller { configuration = self.nixosConfigurations.home; })
|
||||||
|
.config.system.build.isoImage;
|
||||||
|
|
||||||
packages.x86_64-linux.installer-example =
|
packages.x86_64-linux.installer-example =
|
||||||
(self.lib.mkInstaller { configuration = self.nixosConfigurations.example; })
|
(self.lib.mkInstaller { configuration = self.nixosConfigurations.example; })
|
||||||
.config.system.build.isoImage;
|
.config.system.build.isoImage;
|
||||||
@@ -53,6 +57,15 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# The home gateway replacing antigua's router role (see examples/home).
|
||||||
|
nixosConfigurations.home = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
modules = [
|
||||||
|
self.nixosModules.router
|
||||||
|
./examples/home
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
# `nix run .#deploy -- <flake-attr> <user@host>`: activate a new
|
# `nix run .#deploy -- <flake-attr> <user@host>`: activate a new
|
||||||
# configuration with commit-confirm; see scripts/deploy.sh.
|
# configuration with commit-confirm; see scripts/deploy.sh.
|
||||||
apps.x86_64-linux.deploy =
|
apps.x86_64-linux.deploy =
|
||||||
@@ -82,6 +95,7 @@
|
|||||||
// {
|
// {
|
||||||
# The example must keep building as a complete system.
|
# The example must keep building as a complete system.
|
||||||
example = self.nixosConfigurations.example.config.system.build.toplevel;
|
example = self.nixosConfigurations.example.config.system.build.toplevel;
|
||||||
|
home = self.nixosConfigurations.home.config.system.build.toplevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
# `nix run .#lab`: boot the basic test topology in tmux, one console pane
|
# `nix run .#lab`: boot the basic test topology in tmux, one console pane
|
||||||
|
|||||||
@@ -380,6 +380,28 @@ let
|
|||||||
assertion = lib.stringLength w.interface <= 15;
|
assertion = lib.stringLength w.interface <= 15;
|
||||||
message = "router: ${where}: device name `${w.interface}` is longer than 15 characters.";
|
message = "router: ${where}: device name `${w.interface}` is longer than 15 characters.";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
assertion = lib.all anyIp w.sourceAddresses;
|
||||||
|
message = "router: ${where}.sourceAddresses must be IPv4/IPv6 addresses.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
lib.length (lib.filter isV6 w.sourceAddresses) <= 1
|
||||||
|
&& lib.length (lib.filter (s: !isV6 s) w.sourceAddresses) <= 1;
|
||||||
|
message = "router: ${where}.sourceAddresses may hold at most one address per family.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = lib.all (
|
||||||
|
s:
|
||||||
|
isV6 s
|
||||||
|
|| !(validIp s)
|
||||||
|
|| lib.elem s (
|
||||||
|
map (l: l.ipv4.address) lans
|
||||||
|
++ map (a: builtins.head (lib.splitString "/" a)) (lib.concatMap (x: x.addresses) wgs)
|
||||||
|
)
|
||||||
|
) w.sourceAddresses;
|
||||||
|
message = "router: ${where}.sourceAddresses: an IPv4 source must be the router's address on a LAN or a tunnel.";
|
||||||
|
}
|
||||||
{
|
{
|
||||||
assertion = lib.all validCidr w.addresses;
|
assertion = lib.all validCidr w.addresses;
|
||||||
message = "router: ${where}.addresses must be IPv4/IPv6 addresses in CIDR notation.";
|
message = "router: ${where}.addresses must be IPv4/IPv6 addresses in CIDR notation.";
|
||||||
|
|||||||
@@ -296,6 +296,19 @@ in
|
|||||||
empty means no transfer network (fine for site-to-site).
|
empty means no transfer network (fine for site-to-site).
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
sourceAddresses = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ ];
|
||||||
|
example = [ "192.168.10.1" ];
|
||||||
|
description = ''
|
||||||
|
Source address (at most one per family) for traffic the router
|
||||||
|
itself sends into this tunnel, e.g. DNS queries to the other
|
||||||
|
site. Needed when the tunnel has no address of its own, so the
|
||||||
|
other side sees a source it routes back instead of whatever the
|
||||||
|
kernel picks. IPv4 ones must be an address the router holds on
|
||||||
|
a LAN or a tunnel.
|
||||||
|
'';
|
||||||
|
};
|
||||||
zone = mkOption {
|
zone = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = name;
|
default = name;
|
||||||
|
|||||||
@@ -64,7 +64,19 @@ in
|
|||||||
IPv6AcceptRA = false;
|
IPv6AcceptRA = false;
|
||||||
};
|
};
|
||||||
# Cryptokey routing: a peer's allowedIPs are the destinations sent into the tunnel.
|
# Cryptokey routing: a peer's allowedIPs are the destinations sent into the tunnel.
|
||||||
routes = lib.concatMap (p: map (ip: { Destination = ip; }) p.allowedIPs) (lib.attrValues wg.peers);
|
routes = lib.concatMap (
|
||||||
|
p:
|
||||||
|
map (
|
||||||
|
ip:
|
||||||
|
let
|
||||||
|
sameFamily = lib.filter (s: lib.hasInfix ":" s == lib.hasInfix ":" ip) wg.sourceAddresses;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
Destination = ip;
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (sameFamily != [ ]) { PreferredSource = builtins.head sameFamily; }
|
||||||
|
) p.allowedIPs
|
||||||
|
) (lib.attrValues wg.peers);
|
||||||
}
|
}
|
||||||
) cfg.wireguard;
|
) cfg.wireguard;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -999,6 +999,40 @@ let
|
|||||||
router.lans.main = lan { ipv6.address = "192.168.1.1/24"; };
|
router.lans.main = lan { ipv6.address = "192.168.1.1/24"; };
|
||||||
expect = [ "not an IPv6 address in CIDR" ];
|
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 = {
|
nonOverlappingLans = {
|
||||||
router.lans = {
|
router.lans = {
|
||||||
a = lan { };
|
a = lan { };
|
||||||
@@ -1049,6 +1083,7 @@ let
|
|||||||
"sharedLinkValid"
|
"sharedLinkValid"
|
||||||
"pppTransportValid"
|
"pppTransportValid"
|
||||||
"ulaValid"
|
"ulaValid"
|
||||||
|
"wgSourceOk"
|
||||||
"dnsValid"
|
"dnsValid"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -226,6 +226,8 @@ pkgs.testers.runNixOSTest {
|
|||||||
# No address on the tunnel: the site queries the LAN address and
|
# 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.
|
# is allowed because it routes 192.168.50.0/24 through the tunnel.
|
||||||
dns = true;
|
dns = true;
|
||||||
|
# Router-originated traffic to the site uses the LAN address.
|
||||||
|
sourceAddresses = [ "192.168.10.1" ];
|
||||||
peers.siteb = {
|
peers.siteb = {
|
||||||
publicKey = key.siteb.pub;
|
publicKey = key.siteb.pub;
|
||||||
endpoint = "203.0.113.60:51821";
|
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.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")
|
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"):
|
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")
|
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")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user