# 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; }; }