0aade09d7e
New bare-metal box headed for Nikhef, intended to take over most of estuary's colony edge routing. This is the bootstrap config only: the hardware, the single-NVMe ESP + LVM layout, and enough networking to boot and be reachable. It is being staged at home before it is racked, so it has no colony assignments yet. Every 2.5G port takes DHCP and whichever one is patched in brings the box up; kea registers the DHCP hostname, so the deploy node points at portcullis.dyn.h.nul.ie until there is a real colony FQDN for it. The host key was adopted from the installer session and seeded onto the persist volume before first boot, so my.secrets.key could be set up front -- which makes portcullis a recipient of the user-passwd secret that my.user declares for every box. Documented with a box page, a row in the colony site index, and a note in the colony section of networking.md that the topology is expected to change once portcullis takes over from estuary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
445 lines
23 KiB
Markdown
445 lines
23 KiB
Markdown
# Networking
|
|
|
|
This page describes how addressing works across the boxes: the assignment mechanism, the
|
|
per-site domains and prefixes, the home router HA pair, and the overlays/tunnels that tie the
|
|
sites together. Switch-level home topology (jim/dave/brian, the ONT path) lives in
|
|
[sites/home/switches.md](sites/home/switches.md).
|
|
|
|
## Assignments
|
|
|
|
Every box declares `nixos.systems.<name>.assignments`, an attrset of *assignments* (one per
|
|
network the box is attached to). The option definition (`assignmentOpts` in
|
|
[`nixos/default.nix`](../nixos/default.nix)):
|
|
|
|
- `name` (defaults to the attribute name) and `altNames` — DNS names for the assignment.
|
|
- `visible` (default `true`) — whether DNS helpers include it.
|
|
- `domain` — DNS suffix for this assignment.
|
|
- `mtu` — interface MTU (applied via the network's `linkConfig.MTUBytes`).
|
|
- `ipv4.address` / `ipv4.mask` (default 24) / `ipv4.gateway` (defaults to host 1 of the
|
|
prefix; set explicitly to `null` when there is no gateway) / `ipv4.genPTR`.
|
|
- `ipv6.address` (nullable — an assignment can be v4-only) / `ipv6.mask` (default 64) /
|
|
`ipv6.iid` (SLAAC static token instead of a full address) / `ipv6.gateway` / `ipv6.genPTR`.
|
|
|
|
`extraAssignments` is a second, nested level for addresses that belong *to* a network but not
|
|
to any single box — the home routers use it for their floating VIP entries (`router-hi`,
|
|
`router-lo`, `router-ut`).
|
|
|
|
All assignments are aggregated into `nixos.allAssignments` — every system's `assignments`
|
|
merged with every system's `extraAssignments` — and passed to every module as the
|
|
`allAssignments` argument, so any box can route to any other box's addresses without
|
|
hardcoding. A flake-wide assertion fails evaluation if any IPv4 or IPv6 address appears in
|
|
more than one assignment. Each box also receives its own assignments as the `assignments`
|
|
module argument.
|
|
|
|
Two pieces of machinery consume assignments:
|
|
|
|
- `lib.my.networkdAssignment` ([`lib/default.nix`](../lib/default.nix)) renders an assignment
|
|
as a `systemd.network` network: static `address`/`gateway`, MTU, LLDP, and IPv6 RA handling
|
|
(`IPv6AcceptRA` when there's no static gateway or a static `iid` is set, with
|
|
`Token = static:<iid>`).
|
|
- `mkSystem` defaults `networking.hostName` to `assignments.internal.name` (falling back to
|
|
the system name) and `networking.domain` to `assignments.internal.domain`. The shared
|
|
`network` module sets a fallback domain of `int.nul.ie` for boxes without one.
|
|
|
|
## Box assignments
|
|
|
|
Every box's `assignments` (plus the routers' floating VIP `extraAssignments`), grouped first by site
|
|
and then by assignment key. Generated from `nixos.allAssignments` by
|
|
`nix run .#update-docs-assignments`; CI keeps it current. Only the **Notes** column is hand-written —
|
|
edit prose there, never the other generated cells.
|
|
|
|
### colony
|
|
|
|
<!-- assignments: colony -->
|
|
<!-- assignments-start -->
|
|
#### `internal`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`chatterbox`](sites/colony/shill/containers/chatterbox.md) | `10.100.2.5/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::5/64` | ams1.int.nul.ie | |
|
|
| [`colony`](sites/colony/colony.md) | `94.142.241.224/32` | `2a0e:97c0:4d2:10::2/64` | ams1.int.nul.ie | |
|
|
| [`colony-psql`](sites/colony/shill/containers/colony-psql.md) | `10.100.2.4/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::4/64` | ams1.int.nul.ie | |
|
|
| `enshrouded-oci` | `10.100.3.5/24 gw 10.100.3.1` | `2a0e:97c0:4d2:13::5/64` | ams1.int.nul.ie | Enshrouded OCI container on [`whale2`](sites/colony/whale2.md#game-servers); disabled |
|
|
| [`estuary`](sites/colony/estuary.md) | `94.142.240.44/24 gw 94.142.240.254` | `2a02:898:0:20::329:1/64 gw 2a02:898:0:20::1` | ams1.int.nul.ie | |
|
|
| [`gam`](sites/colony/shill/containers/gam.md) | `10.100.2.11/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::b/64` | ams1.int.nul.ie | |
|
|
| [`git`](sites/colony/git.md) | `94.142.241.117/32` | `2a0e:97c0:4d2:11::4/64` | ams1.int.nul.ie | |
|
|
| `graeme-oci` | `10.100.3.8/24 gw 10.100.3.1` | `2a0e:97c0:4d2:13::8/64` | ams1.int.nul.ie | Minecraft OCI container on [`whale2`](sites/colony/whale2.md#game-servers) |
|
|
| [`jackflix`](sites/colony/shill/containers/jackflix.md) | `10.100.2.6/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::6/64` | ams1.int.nul.ie | |
|
|
| `kevcraft-oci` | `10.100.3.6/24 gw 10.100.3.1` | `2a0e:97c0:4d2:13::6/64` | ams1.int.nul.ie | Minecraft OCI container on [`whale2`](sites/colony/whale2.md#game-servers) |
|
|
| `kinkcraft-oci` | `10.100.3.7/24 gw 10.100.3.1` | `2a0e:97c0:4d2:13::7/64` | ams1.int.nul.ie | Minecraft OCI container on [`whale2`](sites/colony/whale2.md#game-servers) |
|
|
| [`middleman`](sites/colony/shill/containers/middleman.md) | `10.100.2.2/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::2/64` | ams1.int.nul.ie | |
|
|
| [`object`](sites/colony/shill/containers/object.md) | `10.100.2.7/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::7/64` | ams1.int.nul.ie | |
|
|
| [`qclk`](sites/colony/shill/containers/qclk.md) | `10.100.2.10/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::a/64` | ams1.int.nul.ie | |
|
|
| [`shill`](sites/colony/shill/README.md) | `94.142.241.225/32` | `2a0e:97c0:4d2:11::2/64` | ams1.int.nul.ie | |
|
|
| `simpcraft-oci` | `10.100.3.3/24 gw 10.100.3.1` | `2a0e:97c0:4d2:13::3/64` | ams1.int.nul.ie | Minecraft OCI container on [`whale2`](sites/colony/whale2.md#game-servers) |
|
|
| `simpcraft-staging-oci` | `10.100.3.4/24 gw 10.100.3.1` | `2a0e:97c0:4d2:13::4/64` | ams1.int.nul.ie | Minecraft staging OCI container on [`whale2`](sites/colony/whale2.md#game-servers); disabled |
|
|
| [`toot`](sites/colony/shill/containers/toot.md) | `10.100.2.8/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::8/64` | ams1.int.nul.ie | |
|
|
| `valheim-oci` | `10.100.3.2/24 gw 10.100.3.1` | `2a0e:97c0:4d2:13::2/64` | ams1.int.nul.ie | Valheim OCI container on [`whale2`](sites/colony/whale2.md#game-servers) |
|
|
| [`vaultwarden`](sites/colony/shill/containers/vaultwarden.md) | `10.100.2.3/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::3/64` | ams1.int.nul.ie | |
|
|
| [`waffletail`](sites/colony/shill/containers/waffletail.md) | `10.100.2.9/24 gw 10.100.2.1` | `2a0e:97c0:4d2:12::9/64` | ams1.int.nul.ie | |
|
|
| [`whale2`](sites/colony/whale2.md) | `94.142.241.226/32` | `2a0e:97c0:4d2:11::3/64` | ams1.int.nul.ie | |
|
|
|
|
#### `as211024`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`estuary`](sites/colony/estuary.md) | `10.100.50.1/24` | `2a0e:97c0:4df::1/64` | — | |
|
|
|
|
#### `base`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`estuary`](sites/colony/estuary.md) | `10.100.0.1/24` | `2a0e:97c0:4d2:10::1/64` | ams1.int.nul.ie | |
|
|
|
|
#### `ctrs`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`shill`](sites/colony/shill/README.md) | `10.100.2.1/24` | `2a0e:97c0:4d2:12::1/64` | ams1.int.nul.ie | |
|
|
|
|
#### `oci`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`whale2`](sites/colony/whale2.md) | `10.100.3.1/24` | `2a0e:97c0:4d2:13::1/64` | ams1.int.nul.ie | |
|
|
|
|
#### `qclk`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`qclk`](sites/colony/shill/containers/qclk.md) | `10.100.4.1/24` | — | — | |
|
|
|
|
#### `routing`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`colony`](sites/colony/colony.md) | `10.100.0.2/24 gw 10.100.0.1` | — | ams1.int.nul.ie | |
|
|
| [`git`](sites/colony/git.md) | `10.100.1.4/24 gw 10.100.1.1` | — | ams1.int.nul.ie | |
|
|
| [`shill`](sites/colony/shill/README.md) | `10.100.1.2/24 gw 10.100.1.1` | — | ams1.int.nul.ie | |
|
|
| [`whale2`](sites/colony/whale2.md) | `10.100.1.3/24 gw 10.100.1.1` | — | ams1.int.nul.ie | |
|
|
|
|
#### `tailscale`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`waffletail`](sites/colony/shill/containers/waffletail.md) | `100.64.0.5/32` | `fd7a:115c:a1e0::5/128` | — | |
|
|
|
|
#### `vms`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`colony`](sites/colony/colony.md) | `10.100.1.1/24` | `2a0e:97c0:4d2:11::1/64` | ams1.int.nul.ie | |
|
|
<!-- assignments-end -->
|
|
|
|
### home
|
|
|
|
<!-- assignments: home -->
|
|
<!-- assignments-start -->
|
|
#### `as211024`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`river`](sites/home/river.md) | `10.100.50.2/24` | `2a0e:97c0:4df:0:1::1/64 gw 2a0e:97c0:4df:0:2::1` | — | |
|
|
| [`stream`](sites/home/stream.md) | `10.100.50.3/24` | `2a0e:97c0:4df:0:1::2/64 gw 2a0e:97c0:4df:0:2::1` | — | |
|
|
|
|
#### `core`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`palace`](sites/home/palace.md) | `192.168.64.20/24` | — | h.nul.ie | |
|
|
| [`river`](sites/home/river.md) | `192.168.64.1/24` | — | h.nul.ie | |
|
|
| [`stream`](sites/home/stream.md) | `192.168.64.2/24` | — | h.nul.ie | |
|
|
| [`unifi`](sites/home/sfh/containers/unifi.md) | `192.168.64.21/24` | — | h.nul.ie | |
|
|
|
|
#### `hi`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`castle`](sites/home/castle.md) | `192.168.68.40/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::3:1/64` | h.nul.ie | |
|
|
| [`cellar`](sites/home/cellar.md) | `192.168.68.80/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::4:1/64` | h.nul.ie | |
|
|
| [`hass`](sites/home/sfh/containers/hass.md) | `192.168.68.103/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::5:3/64` | h.nul.ie | |
|
|
| [`palace`](sites/home/palace.md) | `192.168.68.22/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::2:1/64` | h.nul.ie | |
|
|
| [`river`](sites/home/river.md) | `192.168.68.1/22` | `2a0e:97c0:4d0:1::1/64` | h.nul.ie | |
|
|
| `router-hi` | `192.168.71.254/22 gw 192.168.68.1` | `2a0e:97c0:4d0:1::ffff/64` | h.nul.ie | Floating VIP shared by [`river`](sites/home/river.md) and [`stream`](sites/home/stream.md) |
|
|
| [`sfh`](sites/home/sfh/README.md) | `192.168.68.81/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::4:2/64` | h.nul.ie | |
|
|
| [`stream`](sites/home/stream.md) | `192.168.68.2/22` | `2a0e:97c0:4d0:1::2/64` | h.nul.ie | |
|
|
| [`unifi`](sites/home/sfh/containers/unifi.md) | `192.168.68.100/22 gw 192.168.71.254` | `2a0e:97c0:4d0:1::5:1/64` | h.nul.ie | |
|
|
|
|
#### `lo`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`hass`](sites/home/sfh/containers/hass.md) | `192.168.72.103/21` | `2a0e:97c0:4d0:2::5:3/64` | h.nul.ie | |
|
|
| [`river`](sites/home/river.md) | `192.168.72.1/21` | `2a0e:97c0:4d0:2::1/64` | h.nul.ie | |
|
|
| `router-lo` | `192.168.79.254/21 gw 192.168.72.1` | `2a0e:97c0:4d0:2::ffff/64` | h.nul.ie | Floating VIP shared by [`river`](sites/home/river.md) and [`stream`](sites/home/stream.md) |
|
|
| [`stream`](sites/home/stream.md) | `192.168.72.2/21` | `2a0e:97c0:4d0:2::2/64` | h.nul.ie | |
|
|
|
|
#### `untrusted`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`river`](sites/home/river.md) | `192.168.80.1/24` | `2a0e:97c0:4d0:3::1/64` | h.nul.ie | |
|
|
| `router-ut` | `192.168.80.254/24 gw 192.168.80.1` | `2a0e:97c0:4d0:3::ffff/64` | h.nul.ie | Floating VIP shared by [`river`](sites/home/river.md) and [`stream`](sites/home/stream.md) |
|
|
| [`stream`](sites/home/stream.md) | `192.168.80.2/24` | `2a0e:97c0:4d0:3::2/64` | h.nul.ie | |
|
|
<!-- assignments-end -->
|
|
|
|
### remote
|
|
|
|
<!-- assignments: remote -->
|
|
<!-- assignments-start -->
|
|
#### `internal`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`kelder-acquisition`](remote/kelder/containers/kelder-acquisition.md) | `172.16.64.2/24 gw 172.16.64.1` | — | hentai.engineer | |
|
|
| [`kelder-spoder`](remote/kelder/containers/kelder-spoder.md) | `172.16.64.3/24 gw 172.16.64.1` | — | hentai.engineer | |
|
|
|
|
#### `allhost`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`britnet`](remote/britnet.md) | `77.74.199.67/24 gw 77.74.199.1` | `2a12:ab46:5344:99::a/64 gw 2a12:ab46:5344::1` | bhx1.int.nul.ie | |
|
|
|
|
#### `as211024`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`britway`](remote/britway.md) | `10.100.50.5/24` | `2a0e:97c0:4df:0:2::1/64` | — | |
|
|
|
|
#### `ctrs`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`kelder`](remote/kelder/README.md) | `172.16.64.1/24` | — | hentai.engineer | |
|
|
|
|
#### `estuary`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`kelder`](remote/kelder/README.md) | `94.142.242.254/32` | — | — | |
|
|
|
|
#### `vpn`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`britnet`](remote/britnet.md) | `10.200.0.1/24` | `fdfb:5ebf:6e84::1/64` | — | |
|
|
|
|
#### `vultr`
|
|
|
|
| Box | IPv4 | IPv6 | Domain | Notes |
|
|
|---|---|---|---|---|
|
|
| [`britway`](remote/britway.md) | `45.76.141.188/23 gw 45.76.140.1` | `2001:19f0:7402:128b::1/64` | lon1.int.nul.ie | |
|
|
<!-- assignments-end -->
|
|
|
|
## Domains
|
|
|
|
The public domain is `nul.ie` (`lib.my.c.pubDomain`). Each site has its own internal domain
|
|
(constants in [`lib/constants.nix`](../lib/constants.nix)):
|
|
|
|
| Site | Domain |
|
|
|---|---|
|
|
| colony | `ams1.int.nul.ie` |
|
|
| home | `h.nul.ie` |
|
|
| britway | `lon1.int.nul.ie` |
|
|
| britnet | `bhx1.int.nul.ie` |
|
|
| kelder | `hentai.engineer` |
|
|
|
|
## colony
|
|
|
|
The colony box is a hosted server in Amsterdam (`ams1`); its public edge is the `estuary` VM
|
|
(`94.142.240.44`, `2a02:898:0:20::329:1`), which NATs and filters for everything behind it.
|
|
The internal prefixes (`lib.my.c.colony.prefixes`) are carved from `10.100.0.0/16` and
|
|
`2a0e:97c0:4d2:10::/60`:
|
|
|
|
| Network | IPv4 | IPv6 | Purpose |
|
|
|---|---|---|---|
|
|
| `base` | `10.100.0.0/24` | `2a0e:97c0:4d2:10::/64` | Base/management LAN (bridge on the host; estuary is `.1`) |
|
|
| `vms` | `10.100.1.0/24` | `2a0e:97c0:4d2:11::/64` | VM network (host is `.1`, hands out RAs) |
|
|
| `ctrs` | `10.100.2.0/24` | `2a0e:97c0:4d2:12::/64` | `systemd-nspawn` containers on the `shill` VM |
|
|
| `oci` | `10.100.3.0/24` | `2a0e:97c0:4d2:13::/64` | Podman/OCI workloads on the `whale2` VM |
|
|
| `qclk` | `10.100.4.0/24` | — | WireGuard endpoint instances in the `qclk` container |
|
|
|
|
On top of that: `p2pTunnels` (`10.100.5.0/24`) holds point-to-point tunnel /30s (see
|
|
[WireGuard tunnels](#wireguard-point-to-point-tunnels)); the `as211024` mesh gets
|
|
`10.100.50.0/24` + `2a0e:97c0:4df::/64` (see [the L2 mesh](#the-as211024-l2-mesh)); and the
|
|
`cust` block (`10.100.100.0/24`, `2a0e:97c0:4d2:2000::/56`) plus the `vip1`/`vip2`/`vip3`
|
|
public blocks and the per-customer `mail` / `darts` / `jam` prefixes carry customer-facing
|
|
services with their own public addresses (announced by BGP, routed via the host).
|
|
|
|
This layout is expected to change: [`portcullis`](sites/colony/portcullis.md) is bare-metal edge
|
|
hardware headed for Nikhef that will take over most of `estuary`'s routing. It has no assignments
|
|
yet and the replacement topology is still being designed.
|
|
|
|
## home
|
|
|
|
The home site prefixes (`lib.my.c.home.prefixes`) come from `192.168.64.0/18` and
|
|
`2a0e:97c0:4d0::/60`, with VLAN IDs from `lib.my.c.home.vlans`:
|
|
|
|
| Network | VLAN | IPv4 | IPv6 | MTU | Purpose |
|
|
|---|---|---|---|---|---|
|
|
| `core` | — (macvlan) | `192.168.64.0/24` | — | 1500 | Router-to-router/core link |
|
|
| `hi` | 100 | `192.168.68.0/22` | `2a0e:97c0:4d0:1::/64` | 9000 | High-speed LAN (jumbo frames) |
|
|
| `lo` | 110 | `192.168.72.0/21` | `2a0e:97c0:4d0:2::/64` | 1500 | General LAN |
|
|
| `untrusted` | 120 | `192.168.80.0/24` | `2a0e:97c0:4d0:3::/64` | 1500 | Untrusted / IoT |
|
|
| `modem` | 130 (`wan`) | `192.168.0.0/24` | — | — | Virgin Media modem management (stream) |
|
|
| `ont` | 140 (`wan-pon-ont`) | `192.168.100.0/24` | — | — | Digiweb ONT management (river) |
|
|
|
|
Two more WAN-side VLANs exist: `pon-isp` (10), the ISP VLAN Digiweb delivers at the ONT and
|
|
which is trunked untranslated to river, and `wan-pon-isp` (141), reserved for a future
|
|
multi-ONT translation scheme — see
|
|
[sites/home/switches.md](sites/home/switches.md) for the fabric side.
|
|
|
|
The routers themselves (`river` = host 1, `stream` = host 2 in each prefix) are built from one
|
|
definition, [`nixos/boxes/home/routing-common`](../nixos/boxes/home/routing-common/default.nix),
|
|
parameterised by an index (`0` = river, `1` = stream) that derives per-box addresses, DHCP
|
|
pool splits, VRRP state/priority and DNS `ns` numbering.
|
|
|
|
### Router VIPs
|
|
|
|
Clients never use a router's real address: each client VLAN has a floating VIP
|
|
(`lib.my.c.home.vips`) that follows the VRRP master. The VIPs are also declared as
|
|
`extraAssignments` (`router-hi`/`router-lo`/`router-ut`) so they appear in `allAssignments`
|
|
and DNS:
|
|
|
|
| Assignment | IPv4 | IPv6 |
|
|
|---|---|---|
|
|
| `router-hi` | `192.168.71.254/22` | `2a0e:97c0:4d0:1::ffff/64` |
|
|
| `router-lo` | `192.168.79.254/21` | `2a0e:97c0:4d0:2::ffff/64` |
|
|
| `router-ut` | `192.168.80.254/24` | `2a0e:97c0:4d0:3::ffff/64` |
|
|
|
|
There is also a mesh-side VIP (`as211024`): `10.100.50.4` and `2a0e:97c0:4df:0:1::ffff`,
|
|
which the other sites use as their next-hop into the home prefixes.
|
|
|
|
### Router HA
|
|
|
|
#### VRRP
|
|
|
|
[`routing-common/keepalived.nix`](../nixos/boxes/home/routing-common/keepalived.nix) defines separate
|
|
v4/v6 instances (router IDs 51/52) on `lan-core`. Index 0 (`river`) starts as `MASTER`, priorities
|
|
are `255 - index`, and track scripts demote a router whose WAN checks fail. All VIPs of an address
|
|
family move together.
|
|
|
|
#### Client gateway and DNS
|
|
|
|
`kea` hands out `vips.<vlan>.v4` as both `routers` and `domain-name-servers`, with the two routers
|
|
serving disjoint pool halves. `radvd` advertises the v6 VIP as RDNSS (`untrusted` gets Cloudflare)
|
|
and keepalived's `notify_master`/`notify_backup` hooks ensure that only the master sends RAs.
|
|
|
|
Statically-addressed boxes (the servers on `hi`) don't run DHCP, so they'd otherwise learn a
|
|
resolver only from the v6 RA RDNSS — which vanishes when v6 is disabled, taking DNS with it. They
|
|
instead anchor DNS on the VIPs via the shared `lib.my.c.home.vlanDns "<vlan>"` fragment, which sets
|
|
`DNS` to `vips.<vlan>.{v4,v6}` and `Domains` to the advertised search list; the always-present
|
|
static v4 VIP keeps resolution working even with v6 down.
|
|
|
|
#### DNS binding
|
|
|
|
`pdns-recursor` binds the VIPs directly; see
|
|
[`routing-common/dns.nix`](../nixos/boxes/home/routing-common/dns.nix). The
|
|
`net.ipv4.ip_nonlocal_bind` / `net.ipv6.ip_nonlocal_bind` settings let the backup listen before it
|
|
owns the addresses, so failover does not depend on client resolver timeouts. The recursor forwards
|
|
the site's zones to authoritative PowerDNS on `127.0.0.1:5353`. The generated
|
|
[DNS reference](reference/dns.md) lists the live forward and reverse records; the authoritative
|
|
servers allow its AXFRs from the shared internal prefixes and the colony site's egress address.
|
|
|
|
#### `wan-online.target`
|
|
|
|
This shared, initially inert systemd target means "the public IPv4 WAN route is up".
|
|
`routing-common` only declares it: `stream` gates it on a oneshot that waits for the DHCP default
|
|
route on `wan`, while `river`'s `pppd` hooks start and stop it. Consumers such as `ipsec` attach with
|
|
`wantedBy` + `partOf` + `after`, never `requires`/`wants`, so they cannot pull the target in early
|
|
and they reload on WAN flap.
|
|
|
|
### WAN paths (summary)
|
|
|
|
#### `river`
|
|
|
|
The VM on `palace` runs Digiweb PPPoE directly on VLAN 10 (`vlans.pon-isp`), trunked untranslated
|
|
through the switches. The carrying interface is named `wan-pon-isp`; VLAN ID 141 with that name is
|
|
reserved for a future translation scheme. MTU 1508 preserves a 1500-byte PPP session, IPCP requests
|
|
the static address, and the `pppd` hooks own `wan-online.target`. `wan-pon-ont` (VLAN 140) reaches
|
|
the ONT management subnet at `192.168.100.0/24`, where `river` takes `.100`.
|
|
|
|
#### `stream`
|
|
|
|
The bare-metal backup uses DHCP from the Virgin Media modem on `wan` (VLAN 130), keeps a static
|
|
`192.168.0.100/24` management address beside the public lease, and applies CAKE shaping through
|
|
`wan-ifb`.
|
|
|
|
The full fabric story — which switch port carries what, why VLAN 10 is trunked untranslated,
|
|
and the multi-ONT plan — is in [sites/home/switches.md](sites/home/switches.md); the
|
|
`my.homeRouter.*` options (`dns.wanSkipBroadcasts`, `firewall.untrustedRejectV4`) let each box
|
|
tell `routing-common` about subnets sharing its WAN interface.
|
|
|
|
## The AS211024 L2 mesh
|
|
|
|
The edge routers are joined by a layer-2 mesh, defined once as `nixos.vpns.l2.as211024` in
|
|
[`nixos/boxes/colony/vms/estuary/default.nix`](../nixos/boxes/colony/vms/estuary/default.nix)
|
|
and realised on each member by the [`l2mesh` module](../nixos/modules/l2mesh.nix):
|
|
|
|
### Members
|
|
|
|
`estuary`, `river`, `stream` and `britway` peer on their public addresses.
|
|
|
|
### Transport
|
|
|
|
VXLAN (VNI 211024, UDP port 4789) uses static per-peer FDB entries and UDP-encapsulated IPsec in
|
|
Libreswan transport mode. It authenticates without encryption by default; `security.encrypt`
|
|
switches ESP from `null-sha256` to AES-GCM. The shared `l2mesh/as211024.key` PSK is expanded into
|
|
`/run/l2mesh.secrets` when `ipsec` starts.
|
|
|
|
### Overlay addressing
|
|
|
|
The overlay uses `10.100.50.0/24` / `2a0e:97c0:4df::/64`. Each router holds `10.100.50.<n>`:
|
|
`estuary` `.1`, `river` `.2`, `stream` `.3`, and `britway` `.5`. Interface MTU is calculated from
|
|
the physical MTU minus VXLAN/UDP/IPsec overhead.
|
|
|
|
### Routing
|
|
|
|
The home routers route colony prefixes via `estuary` and Tailscale prefixes via `britway`;
|
|
`estuary` and `britway` route home prefixes through the `10.100.50.4` VIP. The home IPv6 default
|
|
route also crosses the mesh through `britway`, which is why the recursor pins its upstreams to IPv4
|
|
as noted in [`routing-common/dns.nix`](../nixos/boxes/home/routing-common/dns.nix). The `nftTrust`
|
|
snippet in `lib.my.c.as211024` admits trusted colony, home, mesh and Tailscale prefixes at the edge
|
|
firewalls.
|
|
|
|
## BGP
|
|
|
|
Both edge routers run `bird2` as AS211024.
|
|
|
|
### `estuary`
|
|
|
|
The colony edge takes a full table from ColoClue, IPv6 transit from iFog and Hurricane Electric,
|
|
and peers through the Frys-IX, NL-ix and FogIXP route servers. It also has direct and monitoring
|
|
sessions; the complete peer table and originated routes live in [estuary.md](sites/colony/estuary.md#bgp).
|
|
|
|
### `britway`
|
|
|
|
The London edge uses secret-backed MD5 authentication for Vultr transit (AS64515), connects to
|
|
`bgp.tools`, and originates the internal, colony and home IPv6 prefixes. See
|
|
[britway.md](remote/britway.md) for its box-specific routing detail.
|
|
|
|
## WireGuard point-to-point tunnels
|
|
|
|
Separate from the mesh, a few boxes run their own WireGuard (private keys in per-box secrets):
|
|
|
|
- **estuary** terminates point-to-point tunnels to `kelder`, `hillcrest` and `john-valorant`,
|
|
addressed out of `p2pTunnels` — see
|
|
[estuary.md](sites/colony/estuary.md#wireguard-tunnels) for the per-tunnel ports and prefixes.
|
|
- **`qclk`** (container on `shill`) runs its own WireGuard on port 51821 out of `10.100.4.0/24`
|
|
— see [qclk.md](sites/colony/shill/containers/qclk.md).
|
|
- **`britnet`** hosts a road-warrior WireGuard VPN on port 51820 serving `10.200.0.0/24` /
|
|
`fdfb:5ebf:6e84::/64` — see [britnet.md](remote/britnet.md).
|
|
|
|
## Tailscale / headscale
|
|
|
|
Tailscale runs against a self-hosted **headscale** control plane on britway at `https://hs.nul.ie`
|
|
(OIDC, MagicDNS, split DNS — see [britway.md](remote/britway.md)). The tailnet prefixes are
|
|
`100.64.0.0/10` / `fd7a:115c:a1e0::/48`.
|
|
|
|
Notable nodes:
|
|
|
|
- **waffletail** (container on `shill`) — the colony subnet router: advertises the colony
|
|
prefixes, acts as an exit node, and SNATs tailnet traffic into the colony networks.
|
|
- **britway** — advertises the home prefixes (routed via the mesh) and is also an exit node.
|
|
- Other boxes join with the shared `tailscale-auth.key` auth-key secret.
|