- 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>
214 lines
6.1 KiB
Nix
214 lines
6.1 KiB
Nix
# 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;
|
|
};
|
|
}
|