services/router: init
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# router
|
||||
|
||||
Turns a machine with several NICs into a site gateway: PPPoE WAN (ISP
|
||||
credentials via vars prompts), a VLAN-filtering bridge over the LAN ports with
|
||||
one L3 interface per VLAN, Kea DHCP and Blocky DNS per VLAN, nftables
|
||||
firewall/NAT, DHCPv6-PD, CrowdSec, an iperf3 server and a WAN speed-test
|
||||
timer. Optional: a Wi-Fi access point on the router's own radios (hostapd),
|
||||
the TP-Link Omada controller (podman) and an internal Caddy reverse proxy
|
||||
with a real wildcard certificate (ACME DNS-01).
|
||||
|
||||
Addressing convention: a site owns `10.<siteId>.0.0/16`; VLAN `<id>` defaults
|
||||
to `10.<siteId>.<id>.0/24`, router at `.1`, DHCP pool `.100-.199`. The `mgmt`
|
||||
and `lan` VLANs are mandatory. Trust model: mgmt reaches everything; other
|
||||
VLANs get router DNS/DHCP and (with `allowWan`) the internet, no inter-VLAN;
|
||||
WAN nothing inbound; the admin mesh (`mesh.subnet`) gets SSH, metrics, iperf3
|
||||
and the Omada UI.
|
||||
|
||||
## Usage from another clan
|
||||
|
||||
```nix
|
||||
# flake.nix
|
||||
inputs.cnx-network.url = "git+https://<host>/B4L/cnx-network-clan";
|
||||
|
||||
# clan.nix
|
||||
inventory.instances.router = {
|
||||
module = { name = "router"; input = "cnx-network"; };
|
||||
roles.default.settings.mesh.subnet = "fd..::/88"; # your admin overlay
|
||||
roles.default.machines.gw-1.settings = {
|
||||
site = "ams";
|
||||
siteId = 1;
|
||||
wan.interface = "enp1s0";
|
||||
wan.vlanId = 10; # or null for untagged PPPoE
|
||||
trunkPorts = [ "enp2s0" ];
|
||||
accessPorts.enp4s0 = "mgmt"; # untagged on-site recovery port
|
||||
# stagingPort = "enp3s0"; # DHCP uplink into the old LAN until cutover
|
||||
vlans = {
|
||||
mgmt.id = 10;
|
||||
lan.id = 20;
|
||||
iot = { id = 40; allowWan = false; };
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Then `clan vars generate gw-1` prompts for the PPPoE username/password.
|
||||
|
||||
### Wi-Fi access point
|
||||
|
||||
If the box has wireless cards, the router can be the site's AP. An SSID is
|
||||
defined once and behaves like an untagged access port of its VLAN; radios
|
||||
choose what to broadcast, so a dual-band card serves the same SSID twice:
|
||||
|
||||
```nix
|
||||
wifi = {
|
||||
enable = true;
|
||||
countryCode = "TH";
|
||||
networks = {
|
||||
home.vlan = "lan"; # WPA3 with WPA2 fallback
|
||||
things = { vlan = "iot"; security = "wpa2"; }; # legacy IoT
|
||||
guest = { vlan = "guest"; isolateClients = true; };
|
||||
};
|
||||
radios = {
|
||||
wlp5s0 = { band = "2g"; channel = 6; macAddress = "…"; networks = [ "home" "things" ]; };
|
||||
wlp6s0 = { band = "5g"; channel = 36; networks = [ "home" ]; };
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Passphrases are vars prompts (`wifi-<name>-passphrase`), asked once at `clan
|
||||
vars generate`. A radio broadcasting more than one SSID needs its hardware
|
||||
`macAddress`: hostapd wants a fixed BSSID per extra SSID, derived from it.
|
||||
`security = "wpa3-transition"` (the default) offers SAE and WPA2-PSK-SHA256;
|
||||
devices that only speak classic WPA2-PSK need `security = "wpa2"`.
|
||||
|
||||
### Internal proxy
|
||||
|
||||
`proxy.enable` serves `<name>.<site><siteId>.<proxy.domain>` under a wildcard
|
||||
certificate obtained via RFC 2136 DNS-01 against `proxy.acme.nameserver`. The
|
||||
gateway signs updates with TSIG key `acme_<hostname with _>`, whose secret is
|
||||
the shared `dns-acme-<hostname>-secret` generator declared by this service.
|
||||
The nameserver machine must declare the same generator so both sides hold one
|
||||
secret — import `acme-secret.nix` from this directory with the gateway's name:
|
||||
|
||||
```nix
|
||||
imports = [ (import "${inputs.cnx-network}/modules/clan/router/acme-secret.nix" "gw-1") ];
|
||||
```
|
||||
|
||||
and load the key with an acl scoped to `_acme-challenge.<site><siteId>`.
|
||||
|
||||
The service does not open the WAN to anything; reach gateways over your mesh.
|
||||
One instance per machine.
|
||||
Reference in New Issue
Block a user