docs/boxes: Document deployed boxes
Add per-site and per-box inventories, consolidate shared network design, and relocate the switch and access-point references under the home site.
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
# Mobile boxes
|
||||||
|
|
||||||
|
Portable workstations that move between networks — currently just `tower`. Mobile boxes have
|
||||||
|
no static network assignments; they use DHCP/NetworkManager and reach other boxes over the
|
||||||
|
tailnet.
|
||||||
|
|
||||||
|
| Box | What it is |
|
||||||
|
| --- | --- |
|
||||||
|
| [`tower`](tower.md) | Framework Laptop 13 workstation |
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# tower
|
||||||
|
|
||||||
|
Portable workstation — a Framework Laptop 13 (Intel), running the full GUI environment.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/tower/default.nix`](../../nixos/boxes/tower/default.nix)
|
||||||
|
- **Host:** physical (laptop)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- Personal portable workstation: `my.gui.enable`, with Sway managed by home-manager.
|
||||||
|
- Joins the tailnet through the headscale on [`britway`](../remote/britway.md) (fish abbr
|
||||||
|
`tsup` = `doas tailscale up --login-server=https://hs.nul.ie --accept-routes`).
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
`tower` has no static assignment; it uses DHCP through NetworkManager and reaches the other boxes
|
||||||
|
over Tailscale.
|
||||||
|
|
||||||
|
## Hardware / platform
|
||||||
|
|
||||||
|
| Component | Inventory |
|
||||||
|
|---|---|
|
||||||
|
| Platform | Framework Laptop 13 (12th Gen Intel Core) |
|
||||||
|
| CPU | Intel Core i5-1240P (12 cores / 16 threads) |
|
||||||
|
| Memory | 32 GiB |
|
||||||
|
| Storage | 2 TB WD Black SN850 NVMe SSD |
|
||||||
|
| Graphics | Integrated Intel Iris Xe |
|
||||||
|
| Connectivity | Intel AX210 Wi-Fi 6E, Bluetooth and Thunderbolt 4 |
|
||||||
|
|
||||||
|
The configuration enables Intel microcode updates, `kvm-intel`, `intel_iommu=on`,
|
||||||
|
`intel-media-driver` and the latest kernel (`lib.my.c.kernel.latest`). Thunderbolt security
|
||||||
|
(`bolt`), the fingerprint reader (`fprintd`) and `tlp` power management are also enabled.
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
- Two LUKS-encrypted partitions, `persist` and `home` (both `allowDiscards`); `/nix` is a
|
||||||
|
separate ext4 filesystem, `/boot` is vfat.
|
||||||
|
- Persistent `/home` (`my.user.tmphome = false`) with a size-limited tmpfs root (`my.tmproot.size`).
|
||||||
|
|
||||||
|
## Networking / services
|
||||||
|
|
||||||
|
- NetworkManager (`wpa_supplicant` backend) with `systemd-resolved`; networkd `wait-online`
|
||||||
|
is disabled. The Wi-Fi interface is renamed to `wifi` by MAC.
|
||||||
|
- Steam and Wireshark enabled; `fstrim`, LVM thin provisioning.
|
||||||
|
- `nix.gc.automatic = false` — GC is run manually on the laptop.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/tower/default.nix`](../../nixos/boxes/tower/default.nix) — the whole box (single file).
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
# 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 -->
|
||||||
|
<!-- assignments-end -->
|
||||||
|
|
||||||
|
### home
|
||||||
|
|
||||||
|
<!-- assignments: home -->
|
||||||
|
<!-- assignments-start -->
|
||||||
|
<!-- assignments-end -->
|
||||||
|
|
||||||
|
### remote
|
||||||
|
|
||||||
|
<!-- assignments: remote -->
|
||||||
|
<!-- assignments-start -->
|
||||||
|
<!-- 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).
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
#### 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`.
|
||||||
|
|
||||||
|
#### `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.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Remote boxes
|
||||||
|
|
||||||
|
The "remote" group covers the boxes that live outside the `colony` and `home` sites: the two
|
||||||
|
edge VPSes (`britway` in London, `britnet` in Birmingham) and the `kelder` site — a secondary
|
||||||
|
server at a remote location, linked back to colony over WireGuard and acting as a NixOS
|
||||||
|
container host.
|
||||||
|
|
||||||
|
| Box | What it is |
|
||||||
|
| --- | --- |
|
||||||
|
| [`britway`](britway.md) | Vultr VPS (London, `lon1`): Headscale control plane, Tailscale exit node, BGP edge, nginx |
|
||||||
|
| [`britnet`](britnet.md) | VPS (Birmingham, `bhx1`): Tailscale exit node / WireGuard hub |
|
||||||
|
| [`kelder`](kelder/README.md) | Secondary home server (`hentai.engineer`): container host, Samba, DDNS (containers on its page) |
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# britnet
|
||||||
|
|
||||||
|
A small VPS in Birmingham (`bhx1`) acting as a second Tailscale/WireGuard egress point — a
|
||||||
|
narrower gateway role than [`britway`](britway.md) (no control plane, no BGP).
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/britnet.nix`](../../nixos/boxes/britnet.nix)
|
||||||
|
- **Host:** VPS (Birmingham, `bhx1`; provider uplink assignment `allhost`)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- **Tailscale exit node** — logs into the headscale on [`britway`](britway.md)
|
||||||
|
(`--login-server=https://hs.nul.ie`) with `--advertise-exit-node`.
|
||||||
|
- **WireGuard hub** — `wg0` listens on UDP 51820 on the `vpn` assignment, with a single static
|
||||||
|
peer.
|
||||||
|
- **NAT gateway** — traffic arriving on `tailscale0`/`wg0` is forwarded out `veth0` and SNATed
|
||||||
|
to the `allhost` v4/v6 addresses.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../networking.md#box-assignments) table (this box: `britnet`).
|
||||||
|
|
||||||
|
## Platform
|
||||||
|
|
||||||
|
| Component | Allocation |
|
||||||
|
|---|---|
|
||||||
|
| Virtualisation | KVM/QEMU guest |
|
||||||
|
| Compute | 2 vCPUs and 2 GiB RAM |
|
||||||
|
| Storage | 32 GiB virtio disk with separate ext4 filesystems for `/boot`, `/nix` and `/persist`; root is tmpfs |
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
- The provider interface is renamed to `veth0` by MAC. Its IPv6 default gateway sits off-subnet, so
|
||||||
|
a link-scope route is added to reach it.
|
||||||
|
- `wg0` is a networkd WireGuard netdev keyed from `britnet/wg.key`; RA is disabled on it.
|
||||||
|
- Upstream DNS is hardcoded to Cloudflare (`1.1.1.1` / `1.0.0.1`).
|
||||||
|
- `iperf3` runs with an open port for bandwidth testing.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/britnet.nix`](../../nixos/boxes/britnet.nix) — the whole box (single file).
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# britway
|
||||||
|
|
||||||
|
A Vultr VPS in London (`lon1`) acting as the network edge node: the self-hosted Tailscale
|
||||||
|
control plane, a tailnet exit node, and the BGP speaker for AS211024.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/britway/`](../../nixos/boxes/britway)
|
||||||
|
- **Host:** VPS at Vultr (London, `lon1`)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- **Headscale** — the self-hosted Tailscale control plane at `hs.nul.ie`; every other box's
|
||||||
|
`tailscaled` logs in here (`--login-server=https://hs.nul.ie`). Google OIDC for auth,
|
||||||
|
SQLite state, MagicDNS under `ts.nul.ie`, and split DNS pointing the colony/home domains
|
||||||
|
at their internal resolvers.
|
||||||
|
- **Tailscale exit node** — advertises `--advertise-exit-node` plus routes to the home v4/v6
|
||||||
|
prefixes; tailnet traffic is SNATed out `veth0` (v4 to the Vultr public IP, v6 to the
|
||||||
|
`as211024` mesh address).
|
||||||
|
- **BGP edge** — `bird2` speaks BGP as AS211024 to Vultr transit (AS64515, separate v4/v6
|
||||||
|
sessions authenticated with a password from `britway/bgp-password-vultr.conf`) and exports
|
||||||
|
everything to a `bgp.tools` monitoring session. It originates the internal, colony and home
|
||||||
|
IPv6 prefixes documented in [networking](../networking.md#domains).
|
||||||
|
- **nginx** — reverse proxy fronting headscale (`hs.nul.ie` → `localhost` headscale port),
|
||||||
|
with a wildcard ACME cert for `nul.ie` issued via Cloudflare DNS.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../networking.md#box-assignments) table (this box: `britway`).
|
||||||
|
|
||||||
|
## Platform
|
||||||
|
|
||||||
|
| Component | Allocation |
|
||||||
|
|---|---|
|
||||||
|
| Virtualisation | Vultr VC2 virtual guest on a QEMU-compatible platform |
|
||||||
|
| Compute | 2 vCPUs and 2 GiB RAM |
|
||||||
|
| Storage | 65 GiB virtio disk with separate ext4 filesystems for `/boot`, `/nix` and `/persist`; root is tmpfs |
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
- Two assignments: `vultr` on the provider interface `veth0` (renamed by MAC), and `as211024`
|
||||||
|
on the `l2mesh` VXLAN interface (`my.vpns.l2`) — member of the shared mesh (see
|
||||||
|
[The AS211024 L2 mesh](../networking.md#the-as211024-l2-mesh)).
|
||||||
|
- Static routes steer colony/home v4 traffic over the `as211024` mesh. A separate `ts-extra`
|
||||||
|
routing table with a policy rule on `tailscale0` ingress sends Tailscale-sourced v6
|
||||||
|
traffic for colony via `estuary`, while the box's own v6 uses WAN.
|
||||||
|
- The firewall trusts the `as211024` prefixes (`lib.my.c.as211024.nftTrust`) and
|
||||||
|
`tailscale0`; `iperf3` runs with an open port for bandwidth testing.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/britway/default.nix`](../../nixos/boxes/britway/default.nix) — system, assignments, networkd, firewall/SNAT.
|
||||||
|
- [`nixos/boxes/britway/bgp.nix`](../../nixos/boxes/britway/bgp.nix) — `bird2` config (Vultr transit, `bgp.tools`).
|
||||||
|
- [`nixos/boxes/britway/nginx.nix`](../../nixos/boxes/britway/nginx.nix) — nginx vhosts + ACME.
|
||||||
|
- [`nixos/boxes/britway/tailscale.nix`](../../nixos/boxes/britway/tailscale.nix) — headscale + the tailnet node itself.
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# kelder
|
||||||
|
|
||||||
|
Secondary home server at a remote site, domain `hentai.engineer`. Linked back to colony over
|
||||||
|
WireGuard and acting as a NixOS container host (like `shill`/`sfh`).
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/kelder/`](../../../nixos/boxes/kelder)
|
||||||
|
- **Host:** physical (Intel; LTS kernel, `kvm-intel`, IOMMU on)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- **Container host** — runs two NixOS containers on the `ctrs` bridge
|
||||||
|
(`my.containers.instances`): `kelder-acquisition` and `kelder-spoder` (below).
|
||||||
|
- **Public services via colony** — a WireGuard tunnel (`estuary` netdev) connects to colony's
|
||||||
|
`estuary` box, which DNATs public traffic to kelder's tunneled assignment; connection-mark-based
|
||||||
|
policy routing sends replies back through the tunnel while ordinary traffic uses the LAN.
|
||||||
|
kelder's own NAT forwards `http`/`https` on to `kelder-spoder`.
|
||||||
|
- **Nextcloud host** — served from the `kelder-spoder` container.
|
||||||
|
- **Samba** — the `storage` share backed by `/mnt/storage`, with `nmbd` and `samba-wsdd` for
|
||||||
|
Windows discovery.
|
||||||
|
- **DDNS** — a `ddns-update` timer runs `dns_update.py` periodically to sync the
|
||||||
|
`hentai.engineer` and `kelder-local.hentai.engineer` Cloudflare records with the address on
|
||||||
|
`et1g0`.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `kelder`).
|
||||||
|
|
||||||
|
## Containers
|
||||||
|
|
||||||
|
| Container | Role |
|
||||||
|
| --- | --- |
|
||||||
|
| [`kelder-acquisition`](containers/kelder-acquisition.md) | Media stack (Transmission over AirVPN, Jackett/Radarr/Sonarr, Jellyfin) |
|
||||||
|
| [`kelder-spoder`](containers/kelder-spoder.md) | Nextcloud + nginx reverse proxy |
|
||||||
|
|
||||||
|
The containers are not deploy targets (`my.deploy.enable = false`); they're managed through
|
||||||
|
the host.
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
- LAN on `et1g0` (renamed by MAC) with DHCP and MTU 1460 (`lib.my.c.kelder.ipv4MTU`); the
|
||||||
|
kelder v4 prefixes are masqueraded out of it.
|
||||||
|
- The `estuary` WireGuard peer is combined with rules that keep LAN traffic on the main table and
|
||||||
|
only route tunnel-marked or owned traffic through the tunnel's dedicated table.
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
- `netdata` (proxied as `monitor.hentai.engineer` by `kelder-spoder`), `smartd`, `fstrim`,
|
||||||
|
LVM thin provisioning.
|
||||||
|
- `minecraft-server` is present but **disabled** (`enable = false`); the firewall still opens
|
||||||
|
25565 tcp/udp.
|
||||||
|
- Primary user `kontent` (in the `storage`/`media` groups).
|
||||||
|
- Sets `system.nixos.distroName = "KelderOS"`, a custom Plymouth theme and an `amogus-beep`
|
||||||
|
boot jingle ([`boot.nix`](../../../nixos/boxes/kelder/boot.nix)).
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/kelder/default.nix`](../../../nixos/boxes/kelder/default.nix) — system, assignments, tunnel, NAT, containers.
|
||||||
|
- [`nixos/boxes/kelder/boot.nix`](../../../nixos/boxes/kelder/boot.nix) — Plymouth theme + boot beep.
|
||||||
|
- [`nixos/boxes/kelder/containers/`](../../../nixos/boxes/kelder/containers) — the two container definitions.
|
||||||
|
- [`nixos/boxes/kelder/dns_update.py`](../../../nixos/boxes/kelder/dns_update.py) — Cloudflare DDNS script.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# kelder-acquisition
|
||||||
|
|
||||||
|
The media acquisition stack for the kelder site — Transmission (forced over VPN), the *arrs
|
||||||
|
and Jellyfin in one NixOS container.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/kelder/containers/acquisition/`](../../../../nixos/boxes/kelder/containers/acquisition)
|
||||||
|
- **Host:** NixOS container on [`kelder`](../README.md)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### Transmission
|
||||||
|
|
||||||
|
`transmission_4` is bound to the VPN wait-online unit and uses AirVPN's forwarded peer port. Upload,
|
||||||
|
download and seed-ratio limits are configured in the service source. Downloads use
|
||||||
|
`/mnt/media/downloads/torrents`, backed by the host's `/mnt/storage/media`.
|
||||||
|
|
||||||
|
### Media services
|
||||||
|
|
||||||
|
Jackett, Radarr and Sonarr share the `media` group with a group-writable umask. Jellyfin uses the
|
||||||
|
host's bind-mounted `/dev/dri` with `intel-vaapi-driver` / `intel-ocl`; its user belongs to
|
||||||
|
`render`.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../networking.md#box-assignments) table (this box: `kelder-acquisition`).
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
- `internal` assignment (name `acquisition-ctr`) on the host's `ctrs` bridge, MTU 1460 to
|
||||||
|
match the site WAN.
|
||||||
|
- All non-site traffic goes over an AirVPN WireGuard tunnel (`vpn` netdev, AirVPN IE endpoint).
|
||||||
|
Policy rules keep traffic to and from the kelder prefixes on the main table and push everything
|
||||||
|
else through the VPN's dedicated table.
|
||||||
|
- An nftables input chain drops new TCP connections from the VPN interface except the
|
||||||
|
Transmission peer port; the web UI ports (9091 Transmission, 9117 Jackett, 7878 Radarr,
|
||||||
|
8989 Sonarr, 8096 Jellyfin) are accepted from the site. When built as a dev VM, those ports
|
||||||
|
are forwarded to the host.
|
||||||
|
- Sonarr still needs the EOL .NET 6 runtime, allowed via
|
||||||
|
`nixpkgs.config.permittedInsecurePackages`.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/kelder/containers/acquisition/default.nix`](../../../../nixos/boxes/kelder/containers/acquisition/default.nix) — services and users.
|
||||||
|
- [`nixos/boxes/kelder/containers/acquisition/networking.nix`](../../../../nixos/boxes/kelder/containers/acquisition/networking.nix) — AirVPN tunnel + firewall.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# kelder-spoder
|
||||||
|
|
||||||
|
The web container for the kelder site: Nextcloud plus an nginx (OpenResty) reverse proxy for
|
||||||
|
the site's services.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/kelder/containers/spoder/`](../../../../nixos/boxes/kelder/containers/spoder)
|
||||||
|
- **Host:** NixOS container on [`kelder`](../README.md)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- **Nextcloud** (`nextcloud32`) at `cloud.hentai.engineer` (trusted alias
|
||||||
|
`cloud-local.hentai.engineer`), SQLite backend, data in `/mnt/storage/nextcloud`
|
||||||
|
(`/mnt/storage` is bind-mounted from the host).
|
||||||
|
- **nginx reverse proxy** (`openresty`) terminating TLS for the site's public vhosts, with a
|
||||||
|
wildcard ACME cert for `hentai.engineer` via Cloudflare DNS. The kelder host forwards
|
||||||
|
`http`/`https` to this container; uploads are unlimited (`clientMaxBodySize = 0`) for
|
||||||
|
Nextcloud's sake.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../networking.md#box-assignments) table (this box: `kelder-spoder`).
|
||||||
|
|
||||||
|
## Proxy vhosts
|
||||||
|
|
||||||
|
All under `hentai.engineer`, each with a `*-local` alias:
|
||||||
|
|
||||||
|
| vhost | Target | Auth |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `monitor` | `netdata` on the kelder host (:19999) | `htpasswd` |
|
||||||
|
| `kontent` | Jellyfin on `kelder-acquisition` (:8096, incl. websocket) | — |
|
||||||
|
| `torrents` | Transmission on `kelder-acquisition` (:9091) | `htpasswd` |
|
||||||
|
| `jackett` | Jackett on `kelder-acquisition` (:9117) | `htpasswd` |
|
||||||
|
| `radarr` | Radarr on `kelder-acquisition` (:7878) | `htpasswd` |
|
||||||
|
| `sonarr` | Sonarr on `kelder-acquisition` (:8989) | `htpasswd` |
|
||||||
|
| `cloud` | Nextcloud (local) | — |
|
||||||
|
|
||||||
|
An `init_worker_by_lua` timer polls `v4.ident.me` periodically to track the site's public IP; it
|
||||||
|
feeds `localRedirect` rewrites (bounce public-IP clients to the `*-local` name) that
|
||||||
|
are currently **disabled** (commented out — Virgin Media filters DNS answers containing local
|
||||||
|
IPs, so the split doesn't work as intended).
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
- `internal` assignment (name `spoder-ctr`) on the host's `ctrs` bridge, MTU 1420.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/kelder/containers/spoder/default.nix`](../../../../nixos/boxes/kelder/containers/spoder/default.nix) — Nextcloud + ACME.
|
||||||
|
- [`nixos/boxes/kelder/containers/spoder/nginx.nix`](../../../../nixos/boxes/kelder/containers/spoder/nginx.nix) — reverse proxy vhosts.
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# colony
|
||||||
|
|
||||||
|
The hosted dedicated server in Amsterdam (`ams1`) and the public-facing half of
|
||||||
|
the boxes: almost everything reachable from the internet lives here.
|
||||||
|
|
||||||
|
- **Internal domain:** `ams1.int.nul.ie` (`lib.my.c.colony.domain`)
|
||||||
|
- **Public domain:** `nul.ie` — public services are published as `*.nul.ie`
|
||||||
|
- **Source:** [`nixos/boxes/colony/`](../../../nixos/boxes/colony)
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
`colony` separates the host, VMs, `shill` containers and `whale2` OCI workloads onto dedicated
|
||||||
|
networks behind [`estuary`](estuary.md), which terminates the public addressing. The canonical
|
||||||
|
prefixes and routing overview are in the [`colony` section of networking.md](../../networking.md#colony).
|
||||||
|
|
||||||
|
## Boxes
|
||||||
|
|
||||||
|
| Box | Role |
|
||||||
|
|---|---|
|
||||||
|
| [`colony`](colony.md) | Physical VM host (AMD, KVM, LVM-thin, `borgthin` backups → rsync.net) |
|
||||||
|
| [`estuary`](estuary.md) | Edge router: WAN, firewall/NAT, DNS, BGP (AS211024), WireGuard |
|
||||||
|
| [`shill`](shill/README.md) | NixOS container host (most applications; per-container pages under `shill/`) |
|
||||||
|
| [`whale2`](whale2.md) | podman/OCI game-server host |
|
||||||
|
| [`git`](git.md) | Gitea + Gitea Actions runner |
|
||||||
|
| [`mail`](mail.md) | Debian VM running mailcow (not NixOS) |
|
||||||
|
| [`darts`](darts.md) | Third-party/customer VM (not NixOS) |
|
||||||
|
|
||||||
|
The applications running on `shill` are listed on its own page — see
|
||||||
|
[shill/README.md](shill/README.md#containers).
|
||||||
|
|
||||||
|
`mail` and `darts` are host-defined VMs whose guest operating systems are managed out of band; their
|
||||||
|
pages document only what this repository controls.
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# colony
|
||||||
|
|
||||||
|
The physical dedicated server in Amsterdam (`ams1`) and the VM host for
|
||||||
|
everything at the colony site.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/colony/default.nix`](../../../nixos/boxes/colony/default.nix)
|
||||||
|
(VM instances in [`nixos/boxes/colony/vms/default.nix`](../../../nixos/boxes/colony/vms/default.nix))
|
||||||
|
- **Host:** bare metal (this *is* the physical box)
|
||||||
|
- **nixpkgs:** `mine-stable`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
Bare-metal AMD host. It does little application work itself — its job is to run
|
||||||
|
the VMs and provide them with storage, networking and backups.
|
||||||
|
|
||||||
|
### Virtualisation
|
||||||
|
|
||||||
|
The `my.vms` module drives QEMU/KVM with `kvm-amd` and IOMMU enabled. Each `my.vms.instances` entry
|
||||||
|
becomes a `vm@<name>` systemd service with UEFI, QMP/monitor sockets under `/run/vms/<name>/`, TAP
|
||||||
|
networking and optional `hostDevices` passthrough through `vfio-pci`. `estuary` receives the WAN NIC
|
||||||
|
this way.
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
All three NVMe SSDs and all four SATA HDDs are physical volumes in one `main` VG. Separate pools
|
||||||
|
and standalone RAID LVs keep workloads on the appropriate media:
|
||||||
|
|
||||||
|
| Layer | Physical layout | Main consumers |
|
||||||
|
|---|---|---|
|
||||||
|
| `nvme-tpool` | Thin-pool data is RAID 0 across the three NVMe SSDs; thin metadata is RAID 1 on two of them. The data itself has no redundancy. | Host `/nix` and `/persist`; VM system and persistence disks; fast data LVs such as `minio`, `oci`, `git`, `gitea-actions-cache`, `nix-cache` and `jam` |
|
||||||
|
| `hdds-tpool` | Thin-pool data is RAID 5 across the three 12 TB HDDs; thin metadata is RAID 1 on two NVMe SSDs. | `media`, passed to `shill`, and `backup`, mounted by the host at `/mnt/backup` |
|
||||||
|
| `darts-media` | Standalone RAID 5 LV spanning all four HDDs. | Bulk storage passed to `darts` |
|
||||||
|
| `darts-ext` | Standalone linear LV using the remaining capacity of the 18 TB HDD. | Expansion storage passed to `darts` |
|
||||||
|
|
||||||
|
`media` and `backup` are thin-provisioned, so their virtual capacities are not additive physical
|
||||||
|
capacity and overcommit `hdds-tpool`. VM disks built with `lib.my.vm.disk` are named
|
||||||
|
`vm-<name>-<disk>` in `main`; `lib.my.vm.lvmDisk` attaches the named data LVs.
|
||||||
|
|
||||||
|
The initrd activates only `colony-nix` and `colony-persist`. This avoids checking and activating all
|
||||||
|
of the storage before switching root; `lvm-activate-main.service` activates the remaining LVs before
|
||||||
|
local filesystems and VMs need them.
|
||||||
|
|
||||||
|
### Backups
|
||||||
|
|
||||||
|
`my.borgthin` job `main` snapshots host and VM persistence/data LVs into `/mnt/backup/main`.
|
||||||
|
`borgthin-rsync.service` copies the repository to rsync.net and `rsync-lvm-meta.service` sends the
|
||||||
|
LVM metadata; both run at idle priority after the Borg job.
|
||||||
|
|
||||||
|
### Monitoring
|
||||||
|
|
||||||
|
Netdata uses FreeIPMI while ignoring the VCCM sensor. The box also runs `smartd` with logs under
|
||||||
|
`/var/log/smartd`, `rasdaemon`, and `fstrim` before the backup job.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `colony`).
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
| Component | Inventory |
|
||||||
|
|---|---|
|
||||||
|
| Platform | ASRock Rack X570D4U server board |
|
||||||
|
| CPU | AMD Ryzen 9 5950X (16 cores / 32 threads) |
|
||||||
|
| Memory | 128 GiB |
|
||||||
|
| NVMe storage | Three 2 TB Samsung SSD 980 PRO devices providing the NVMe-backed LVM thin pool and data LVs |
|
||||||
|
| Bulk storage | Three 12 TB WD120EDBZ disks and one 18 TB WD180EDGZ disk for the bulk LVM volumes |
|
||||||
|
| Boot | SanDisk USB device holding the EFI system partition |
|
||||||
|
| Network / management | Two Intel I210 Gigabit Ethernet controllers, one passed through to `estuary`; ASPEED BMC graphics and console |
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
- Two bridges: `base` (the colony base network, shared with `estuary`) and
|
||||||
|
`vms` (the VM network). Dummy interfaces (`base0`, `vms0`) keep the bridges
|
||||||
|
up in networkd's eyes so dependent VMs can start.
|
||||||
|
- `colony` sends RAs on `vms` (DNS = `estuary`'s base address) and carries
|
||||||
|
static routes for the downstream prefixes: `ctrs` via `shill`, `oci` via
|
||||||
|
`whale2`, plus the Tailscale, `qclk` and `jam` prefixes via `shill`.
|
||||||
|
- `estuary` is the default gateway (via the `base` bridge); `colony`'s own public-facing address is
|
||||||
|
its `internal` assignment (alt name `vm`).
|
||||||
|
- The customer VMs attach to dedicated TAP devices (`vm-mail`, `vm-darts`)
|
||||||
|
which are **not** bridged: networkd puts the point-to-point /32
|
||||||
|
(`lib.my.c.colony.custRouting`) and the customer's IPv6 /64 on each, sends
|
||||||
|
RAs, and link-routes the customer's public /32 down the tap.
|
||||||
|
- `my.firewall` trusts the `vms` bridge, DNATs the shared
|
||||||
|
`lib.my.c.colony.firewallForwards` list for traffic addressed to `estuary`'s
|
||||||
|
public IP (so the port forwards also work from inside), and forwards the
|
||||||
|
customer prefixes through with minimal filtering ("trust for now").
|
||||||
|
|
||||||
|
## VMs
|
||||||
|
|
||||||
|
Declared in `my.vms.instances` (`cpus`/`threads` are QEMU `smp` values):
|
||||||
|
|
||||||
|
| VM | Cores | Threads | Memory | Network | Disks |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| `estuary` | 2 | 2 | 3 GiB | `base` | `esp` / `nix` / `persist` LVs + WAN NIC passthrough |
|
||||||
|
| `shill` | 12 | 2 | 40 GiB | `vms` | `esp` / `nix` / `persist` + `media` / `minio` / `nix-cache` / `jam` LVs |
|
||||||
|
| `whale2` | 8 | 2 | 16 GiB | `vms` | `esp` / `nix` / `persist` + `oci` LV |
|
||||||
|
| `git` | 12 | 2 | 40 GiB | `vms` | `esp` / `nix` / `persist` / `oci` + `git` / `gitea-actions-cache` LVs |
|
||||||
|
| `mail` | 3 | 2 | 6 GiB | `vm-mail` tap | `root` / `data` LVs |
|
||||||
|
| `darts` | 4 | 2 | 16 GiB | `vm-darts` tap | `root` LV + `darts-media` / `darts-ext` LVs |
|
||||||
|
|
||||||
|
`estuary`, `shill`, `whale2` and `git` are NixOS systems with their own pages
|
||||||
|
(see the [README](README.md#boxes)); [`mail`](mail.md) and [`darts`](darts.md) have host-side
|
||||||
|
definitions here, but their guest operating systems are managed out of band.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/default.nix`](../../../nixos/boxes/colony/default.nix) — host hardware, networkd, firewall, backups.
|
||||||
|
- [`nixos/boxes/colony/vms/default.nix`](../../../nixos/boxes/colony/vms/default.nix) — `my.vms.instances` for all six VMs.
|
||||||
|
- [`nixos/modules/vms.nix`](../../../nixos/modules/vms.nix) — the `my.vms` module itself.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# darts
|
||||||
|
|
||||||
|
An opaque third-party/customer VM. Declared in `colony`'s `my.vms.instances`
|
||||||
|
but **not a NixOS system**: this repo knows nothing about what runs inside it
|
||||||
|
and doesn't manage it.
|
||||||
|
|
||||||
|
- **Source (host-side only):** the `darts` instance in
|
||||||
|
[`nixos/boxes/colony/vms/default.nix`](../../../nixos/boxes/colony/vms/default.nix)
|
||||||
|
and the `90-vm-darts` network in
|
||||||
|
[`nixos/boxes/colony/default.nix`](../../../nixos/boxes/colony/default.nix)
|
||||||
|
- **Host:** VM on `colony`
|
||||||
|
- **nixpkgs:** not applicable (unmanaged guest; host-side definition uses `colony`'s `mine-stable`)
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- Customer/dedicated VM, left alone beyond hosting and connectivity.
|
||||||
|
- A thin-provisioned `root` LV on the NVMe pool, plus `darts-media` (RAID 5 across the four HDDs)
|
||||||
|
and `darts-ext` (linear storage on the 18 TB HDD) in the `main` VG. See
|
||||||
|
[`colony`'s storage layout](colony.md#storage).
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
This guest is not a NixOS system, so its host-routed addresses are not rows in the generated
|
||||||
|
[network assignments](../../networking.md#box-assignments) table.
|
||||||
|
|
||||||
|
- Same customer-VM pattern as [`mail`](mail.md): dedicated unbridged TAP
|
||||||
|
(`vm-darts`), point-to-point address
|
||||||
|
(`custRouting.darts-vm`) on the host side, link-routed public /32
|
||||||
|
`94.142.242.255`, and the IPv6 /64 `2a0e:97c0:4d2:2001::/64` with RAs.
|
||||||
|
- DNS: `darts-cust.ams1.int.nul.ie`. Like the other customer prefixes, its
|
||||||
|
inbound traffic is accepted by `estuary` without per-port filtering and
|
||||||
|
forwarded on by `colony`.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/default.nix`](../../../nixos/boxes/colony/vms/default.nix) — VM definition.
|
||||||
|
- [`nixos/boxes/colony/default.nix`](../../../nixos/boxes/colony/default.nix) — host-side network and routing.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
# estuary
|
||||||
|
|
||||||
|
The colony edge router and firewall — the box that holds colony's public IPs
|
||||||
|
and connects everything else at the site to the internet.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/colony/vms/estuary/`](../../../nixos/boxes/colony/vms/estuary)
|
||||||
|
(`default.nix`, `bgp.nix`, `dns.nix`, `bandwidth.nix`)
|
||||||
|
- **Host:** VM on `colony` (gets the WAN NIC by PCI passthrough)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- **Edge routing / firewall / NAT** — owns the colony public IPv4/IPv6 assignments, NATs outbound
|
||||||
|
traffic, and port-forwards inbound services (see [Firewall and NAT](#firewall-and-nat)).
|
||||||
|
- **DNS** — PowerDNS authoritative server *and* recursor (see [DNS](#dns)).
|
||||||
|
- **BGP** — BIRD2 speaking AS211024 with upstreams, IXP route servers and
|
||||||
|
direct peers (see [BGP](#bgp)).
|
||||||
|
- **VPNs** — the `as211024` L2 VXLAN mesh plus three point-to-point WireGuard
|
||||||
|
tunnels (see [VPNs](#vpns)).
|
||||||
|
- **Misc** — `iperf3` server, netdata.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `estuary`).
|
||||||
|
|
||||||
|
## WAN and IXP VLANs
|
||||||
|
|
||||||
|
- `wan` — the passed-through `igb` NIC (9000 MTU, enlarged rings). It carries
|
||||||
|
the plain upstream uplink (static v4/v6 with gateways from the `internal`
|
||||||
|
assignment) plus the tagged `ifog` VLAN.
|
||||||
|
- `ifog` (VLAN 409) is an iFog QinQ transport that carries the IXP VLANs as
|
||||||
|
nested tags:
|
||||||
|
|
||||||
|
| Interface | VLAN | IPv4 | IPv6 | Purpose |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `frys-ix` | 701 | `185.1.160.196/23` | `2001:7f8:10f::3:3850:196/64` | Frys-IX peering LAN |
|
||||||
|
| `nl-ix` | 1845 | `193.239.116.145/22` | `2001:7f8:13::a521:1024:1/64` | NL-ix peering LAN |
|
||||||
|
| `fogixp` | 1147 | `185.1.147.159/24` | `2001:7f8:ca:1::159/64` | FogIXP peering LAN |
|
||||||
|
| `ifog-transit` | 702 | — | `2a0c:9a40:100f:370::2/64` | iFog IPv6 transit |
|
||||||
|
|
||||||
|
The IXP interfaces run at 1500 MTU with DHCP/RA/LLDP off; an nftables `ixp`
|
||||||
|
chain rejects non-IP/ARP ethertypes in both directions.
|
||||||
|
|
||||||
|
- `base` — colony base network; sends RAs and serves DNS to the site, and
|
||||||
|
routes the `vms`/`ctrs`/`oci`, Tailscale, `qclk`, `vip*` and customer
|
||||||
|
prefixes back via `colony`.
|
||||||
|
- `as211024` — the L2 mesh interface (see VPNs).
|
||||||
|
|
||||||
|
## Firewall and NAT
|
||||||
|
|
||||||
|
`my.firewall` (nftables). Inbound port forwards (`my.firewall.nat.forwardPorts`,
|
||||||
|
driven by the shared `lib.my.c.colony.firewallForwards` list):
|
||||||
|
|
||||||
|
| Service | Forwarded to |
|
||||||
|
|---|---|
|
||||||
|
| HTTP/S, Matrix federation | `middleman` |
|
||||||
|
| Git | `git` |
|
||||||
|
| Game servers | OCI servers on `whale2`, `gam` |
|
||||||
|
| Tailscale | `waffletail` |
|
||||||
|
| `qclk` WireGuard | `qclk` |
|
||||||
|
|
||||||
|
Besides the forwards, `extraRules` defines:
|
||||||
|
|
||||||
|
- `routing-tcp` / `routing-udp` chains — the inbound allow-list for new
|
||||||
|
connections from `wan`/`as211024`/IXPs towards internal services (SSH
|
||||||
|
anywhere, otherwise per-service v4/v6 rules mirroring `firewallForwards`).
|
||||||
|
- `filter-routing` — applied to `wan`/`as211024`/IXPs → `base` forwards;
|
||||||
|
customer prefixes (`mail`/`darts` v4, `cust.v6`) are accepted wholesale, the
|
||||||
|
rest goes through the `routing-*` chains.
|
||||||
|
- SNAT: everything from `prefixes.all.v4` leaving non-`as211024` interfaces is
|
||||||
|
NATed to the public IP; the WireGuard tunnel prefixes get their own SNAT
|
||||||
|
addresses.
|
||||||
|
- DNS redirect: DNS traffic arriving at estuary's own public addresses is
|
||||||
|
redirected to port 5353 (the authoritative server) — see below.
|
||||||
|
|
||||||
|
## DNS
|
||||||
|
|
||||||
|
Both halves are PowerDNS ([`dns.nix`](../../../nixos/boxes/colony/vms/estuary/dns.nix)).
|
||||||
|
|
||||||
|
### Authoritative
|
||||||
|
|
||||||
|
`my.pdns.auth`, listening on `0.0.0.0:5353` / `[::]:5353`. Primary for
|
||||||
|
`ams1.int.nul.ie`, `100.10.in-addr.arpa` and the `2a0e:97c0:4d2::/48` reverse
|
||||||
|
zone.
|
||||||
|
|
||||||
|
- Zone contents are largely generated from `allAssignments`
|
||||||
|
(`lib.my.dns.fwdRecords` / `ptrRecords` / `ptr6Records`); `ALIAS` records
|
||||||
|
(with `expand-alias`) point the zone apex at estuary itself.
|
||||||
|
- AXFR is allowed to HE.net's secondary (`216.218.133.2` / `2001:470:600::2`).
|
||||||
|
- `_acme-challenge` is a LUA `TXT` record answered from a file (DNS-01 issuance).
|
||||||
|
- Reached publicly via the NAT redirect of port 53 → 5353; the `base` side also
|
||||||
|
accepts DNS directly.
|
||||||
|
|
||||||
|
### Recursor
|
||||||
|
|
||||||
|
`my.pdns.recursor` (`pdns-recursor`), listening on localhost and the `base`
|
||||||
|
addresses, serving `prefixes.all` and the Tailscale prefixes.
|
||||||
|
|
||||||
|
- Authoritative zones are forwarded back to `127.0.0.1:5353` (with NOTIFY
|
||||||
|
support, so changes show up immediately).
|
||||||
|
- A small Lua `preresolve` hook rewrites `nix-cache.nul.ie` →
|
||||||
|
`http.ams1.int.nul.ie` so cache traffic stays on-site.
|
||||||
|
|
||||||
|
## BGP
|
||||||
|
|
||||||
|
BIRD2 ([`bgp.nix`](../../../nixos/boxes/colony/vms/estuary/bgp.nix)) speaking **AS211024**:
|
||||||
|
|
||||||
|
| Peer | ASN | Role | Where / notes |
|
||||||
|
|---|---|---|---|
|
||||||
|
| ColoClue | AS8283 | Upstream | `euNetworks` 2/3, v4+v6 |
|
||||||
|
| iFog | AS34927 | Upstream | IPv6 transit |
|
||||||
|
| Hurricane Electric | AS6939 | Upstream | IPv6 over Frys-IX |
|
||||||
|
| Frys-IX | AS56393 | IXP route server | |
|
||||||
|
| NL-ix | AS34307 | IXP route server | lower preference |
|
||||||
|
| FogIXP | AS47498 | IXP route server | |
|
||||||
|
| LUJE.net | AS212855 | Direct peer | ColoClue/Frys-IX/FogIXP + multihop labs |
|
||||||
|
| jurrian | AS212635 | Direct peer | |
|
||||||
|
| Meta | AS32934 | Direct peer | Frys-IX/NL-ix |
|
||||||
|
| Cloudflare | AS13335 | Direct peer | Frys-IX |
|
||||||
|
| Apple | AS714 | Direct peer | NL-ix |
|
||||||
|
| HE | AS6939 | Direct peer | Frys-IX (v4) |
|
||||||
|
| bgp.tools | AS212232 | Monitoring | multihop collector, `add paths` |
|
||||||
|
|
||||||
|
Statics originate the site prefixes: the `vip*` ranges via `base`, the
|
||||||
|
ams1/internal v6 prefixes via `base`/`as211024`, and the home /48 towards the
|
||||||
|
home routers' `as211024` VIP.
|
||||||
|
|
||||||
|
Currently disabled (commented out): efero transit over FogIXP ("not working so
|
||||||
|
well lately") and the NL-ix Cloudflare sessions.
|
||||||
|
|
||||||
|
## VPNs
|
||||||
|
|
||||||
|
### `as211024` L2 mesh
|
||||||
|
|
||||||
|
Member alongside `river`/`stream`/`britway` (`my.vpns.l2`, the `l2mesh` module).
|
||||||
|
The mesh transport, crypto and addressing are shared fabric — see
|
||||||
|
[The AS211024 L2 mesh](../../networking.md#the-as211024-l2-mesh) in networking.md.
|
||||||
|
|
||||||
|
### WireGuard tunnels
|
||||||
|
|
||||||
|
Point-to-point tunnels terminated here as networkd `wireguard` netdevs (private
|
||||||
|
keys from agenix); each SNATs out its own interface address:
|
||||||
|
|
||||||
|
| Tunnel | Port | Prefix | Notes |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `kelder` | 51820 | — | to the remote `kelder` site; kelder's public `estuary` assignment is routed over the tunnel |
|
||||||
|
| `hillcrest` | 51822 | `prefixes.hillcrest.v4` (`10.100.5.0/30`) | /32 pair, estuary `.1` ↔ remote `.2` |
|
||||||
|
| `john-valorant` | 51823 | `prefixes.john-valorant.v4` (`10.100.5.4/30`) | /32 pair, estuary `.1` ↔ remote `.2` |
|
||||||
|
|
||||||
|
## Bandwidth management
|
||||||
|
|
||||||
|
[`bandwidth.nix`](../../../nixos/boxes/colony/vms/estuary/bandwidth.nix) implements a WAN shaper: a
|
||||||
|
token-bucket filter on `wan` (outbound) and on an `ifb-wan` IFB device that ingress traffic is
|
||||||
|
mirrored into (inbound), with
|
||||||
|
[`bandwidth.py`](../../../nixos/boxes/colony/vms/estuary/bandwidth.py) as a `bandwidth-limiter`
|
||||||
|
service that watches utilisation and can adjust the configured rate. **Currently disabled** — the
|
||||||
|
file is not in estuary's `imports` (only `dns.nix` and `bgp.nix` are), so no shaping is applied.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/estuary/default.nix`](../../../nixos/boxes/colony/vms/estuary/default.nix) — system, networkd, firewall, WireGuard, mesh membership.
|
||||||
|
- [`nixos/boxes/colony/vms/estuary/bgp.nix`](../../../nixos/boxes/colony/vms/estuary/bgp.nix) — BIRD2 config.
|
||||||
|
- [`nixos/boxes/colony/vms/estuary/dns.nix`](../../../nixos/boxes/colony/vms/estuary/dns.nix) — PowerDNS auth + recursor.
|
||||||
|
- [`nixos/boxes/colony/vms/estuary/bandwidth.nix`](../../../nixos/boxes/colony/vms/estuary/bandwidth.nix) — WAN shaper (disabled, not imported).
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# git
|
||||||
|
|
||||||
|
The Gitea VM — source hosting and CI for the boxes (`git.nul.ie`).
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/colony/vms/git/`](../../../nixos/boxes/colony/vms/git)
|
||||||
|
(`default.nix`, `gitea.nix`, `gitea-actions.nix`)
|
||||||
|
- **Host:** VM on `colony`
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### Gitea
|
||||||
|
|
||||||
|
The Git forge at `git.nul.ie` (self-registration disabled), configured in
|
||||||
|
[`gitea.nix`](../../../nixos/boxes/colony/vms/git/gitea.nix).
|
||||||
|
|
||||||
|
- Backed by PostgreSQL on [`colony-psql`](shill/containers/colony-psql.md) (waited on via
|
||||||
|
`lib.my.systemdAwaitPostgres`).
|
||||||
|
- LFS enabled; all object storage (incl. LFS and packages) is on MinIO at `s3.nul.ie` (bucket
|
||||||
|
`gitea`, on [`object`](shill/containers/object.md)) — the secret is spliced into `app.ini` at
|
||||||
|
startup.
|
||||||
|
- Mail goes out via `mail.nul.ie`, including the issue-reply incoming-mail poller.
|
||||||
|
|
||||||
|
### Gitea Actions runner
|
||||||
|
|
||||||
|
One Docker-mode instance (`main-docker`) on podman (privileged, `podman` network), configured in
|
||||||
|
[`gitea-actions.nix`](../../../nixos/boxes/colony/vms/git/gitea-actions.nix).
|
||||||
|
|
||||||
|
- Labels for `node:24-trixie` and the self-built `git.nul.ie/dev/actions-ubuntu:26.04` images.
|
||||||
|
- Runs as a fixed `gitea-runner` user (not `DynamicUser`) so it can read its token; jobs have a
|
||||||
|
configured timeout.
|
||||||
|
- The action cache lives on a dedicated disk (`/var/cache/gitea-runner`).
|
||||||
|
- Executes the repo's own `.gitea/workflows/ci.yaml`.
|
||||||
|
|
||||||
|
### nginx
|
||||||
|
|
||||||
|
Terminates TLS for `git.nul.ie` (and a default vhost) and proxies to Gitea on `:3000`. ACME
|
||||||
|
(Let's Encrypt, production) issues `nul.ie` + `*.nul.ie` via the Cloudflare DNS-01 challenge.
|
||||||
|
|
||||||
|
### podman
|
||||||
|
|
||||||
|
Local container backend for the runner; `/var/lib/containers` is an XFS data disk, and the default
|
||||||
|
`10.88.0.0/16` podman subnet is allowed to forward.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `git`).
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
- `/var/lib/gitea` — the `git` LV (repositories, config).
|
||||||
|
- `/var/cache/gitea-runner` — the `gitea-actions-cache` LV.
|
||||||
|
- `/var/lib/containers` — the `oci` LV (XFS with project quotas). Despite the
|
||||||
|
name this is local to the `git` VM and unrelated to `whale2`'s `oci`
|
||||||
|
network.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/git/default.nix`](../../../nixos/boxes/colony/vms/git/default.nix) — VM config, nginx + ACME, podman, firewall.
|
||||||
|
- [`nixos/boxes/colony/vms/git/gitea.nix`](../../../nixos/boxes/colony/vms/git/gitea.nix) — Gitea itself.
|
||||||
|
- [`nixos/boxes/colony/vms/git/gitea-actions.nix`](../../../nixos/boxes/colony/vms/git/gitea-actions.nix) — the Actions runner.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# mail
|
||||||
|
|
||||||
|
A Debian VM running [mailcow](https://mailcow.email/) (`mail.nul.ie`) — the
|
||||||
|
mail server for `nul.ie`. Declared in `colony`'s `my.vms.instances` but **not
|
||||||
|
a NixOS system**: everything inside the VM is configured out of band.
|
||||||
|
|
||||||
|
- **Source (host-side only):** the `mail` instance in
|
||||||
|
[`nixos/boxes/colony/vms/default.nix`](../../../nixos/boxes/colony/vms/default.nix)
|
||||||
|
and the `90-vm-mail` network in
|
||||||
|
[`nixos/boxes/colony/default.nix`](../../../nixos/boxes/colony/default.nix)
|
||||||
|
- **Host:** VM on `colony`
|
||||||
|
- **nixpkgs:** not applicable (Debian guest; host-side definition uses `colony`'s `mine-stable`)
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- Runs the full mailcow stack (Postfix/Dovecot/SOGo/Rspamd) for `nul.ie`.
|
||||||
|
Other colony services send through it as `mail.nul.ie` (e.g. Gitea, and the
|
||||||
|
disabled Mastodon config).
|
||||||
|
- `root` and `data` LVM disks; the `vm-mail-data` LV is included in `colony`'s `borgthin` backups.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
This guest is not a NixOS system, so its host-routed addresses are not rows in the generated
|
||||||
|
[network assignments](../../networking.md#box-assignments) table.
|
||||||
|
|
||||||
|
### Link and addressing
|
||||||
|
|
||||||
|
The VM attaches to a dedicated, unbridged TAP (`vm-mail`). `colony` puts the point-to-point
|
||||||
|
`custRouting.mail-vm` address on the host side, link-routes public
|
||||||
|
`94.142.241.227/32` down the TAP, and advertises `2a0e:97c0:4d2:2000::/64` with RAs.
|
||||||
|
|
||||||
|
- DNS: `mail-vm.ams1.int.nul.ie` (and `mail.nul.ie` publicly, incl. the PTR in
|
||||||
|
estuary's reverse zone). `estuary` accepts traffic to the customer prefixes
|
||||||
|
without per-port filtering; `colony` forwards it on ("trust for now").
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- ACME certificates are issued on `middleman` and pushed to the VM over SSH
|
||||||
|
(`acme@mail.nul.ie mailcow-ssl-reload`, key `middleman/mailcow-ssh.key`);
|
||||||
|
the VM's SSH host key is pinned at `.keys/mail-vm-host.pub`.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/default.nix`](../../../nixos/boxes/colony/vms/default.nix) — VM definition.
|
||||||
|
- [`nixos/boxes/colony/default.nix`](../../../nixos/boxes/colony/default.nix) — host-side network and routing.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# shill
|
||||||
|
|
||||||
|
The colony NixOS container host — most colony applications run as
|
||||||
|
`systemd-nspawn` containers on this VM.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/colony/vms/shill/`](../../../../nixos/boxes/colony/vms/shill)
|
||||||
|
(`default.nix`, `containers-ext.nix`, `containers/`)
|
||||||
|
- **Host:** VM on `colony`
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### Container hosting
|
||||||
|
|
||||||
|
`my.containers.instances` runs the colony containers on the `ctrs` bridge. Each is a full NixOS
|
||||||
|
system rendered through `my.asContainer` and deployed as a profile on `shill`; containers are not
|
||||||
|
standalone deploy targets. The shared container module supplies the nspawn units, `/persist` and
|
||||||
|
store binds.
|
||||||
|
|
||||||
|
### Shared storage
|
||||||
|
|
||||||
|
LVM-backed host volumes are bind-mounted into the consumers: `/mnt/media` is read-only in
|
||||||
|
`middleman` and read-write in `jackflix`; `/mnt/minio` and `/mnt/nix-cache` are read-write in
|
||||||
|
`object`.
|
||||||
|
|
||||||
|
### Routing
|
||||||
|
|
||||||
|
`shill` routes between `vms` and `ctrs`, advertises `estuary` as DNS on `ctrs`, and routes Tailscale
|
||||||
|
through `waffletail` and the `qclk` prefix through `qclk`. It applies the shared `firewallForwards`
|
||||||
|
DNAT for `estuary`'s public IP; a connection-mark-based SNAT rule keeps replies symmetric.
|
||||||
|
|
||||||
|
### Host tuning
|
||||||
|
|
||||||
|
The box has a larger conntrack table and ephemeral-port range for high connection counts. Netdata
|
||||||
|
listens on port 19999.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../networking.md#box-assignments) table (this box: `shill`).
|
||||||
|
|
||||||
|
## Containers
|
||||||
|
|
||||||
|
Defined under
|
||||||
|
[`shill/containers/`](../../../../nixos/boxes/colony/vms/shill/containers) and
|
||||||
|
wired up in `shill`'s `my.containers.instances`. The generated
|
||||||
|
[network assignments](../../../networking.md#box-assignments) table is the source of truth for
|
||||||
|
their current addresses. Each container has its own page:
|
||||||
|
|
||||||
|
| Container | Role |
|
||||||
|
|---|---|
|
||||||
|
| [`middleman`](containers/middleman.md) | Reverse proxy, ACME, nginx-sso, librespeed |
|
||||||
|
| [`vaultwarden`](containers/vaultwarden.md) | Password manager |
|
||||||
|
| [`colony-psql`](containers/colony-psql.md) | Shared PostgreSQL (14) |
|
||||||
|
| [`chatterbox`](containers/chatterbox.md) | Matrix Synapse + bridges |
|
||||||
|
| [`jackflix`](containers/jackflix.md) | Media stack |
|
||||||
|
| [`object`](containers/object.md) | MinIO, Harmonia Nix cache, Sharry, HedgeDoc, wastebin |
|
||||||
|
| [`toot`](containers/toot.md) | Bluesky PDS (Mastodon disabled) |
|
||||||
|
| [`waffletail`](containers/waffletail.md) | Tailscale subnet router / exit node |
|
||||||
|
| [`qclk`](containers/qclk.md) | WireGuard management appliance |
|
||||||
|
| [`gam`](containers/gam.md) | Terraria server |
|
||||||
|
|
||||||
|
### `jam`
|
||||||
|
|
||||||
|
A one-off: [`containers-ext.nix`](../../../../nixos/boxes/colony/vms/shill/containers-ext.nix)
|
||||||
|
runs a raw `systemd-nspawn` container (not a `my.containers` instance, not
|
||||||
|
NixOS) with its root on the `jam` LV, private user namespaces and a `ve-jam`
|
||||||
|
veth. It gets the `jam` customer prefix (`prefixes.jam`, `jam-cust` in DNS)
|
||||||
|
and SSH is forwarded to it from `shill`'s public IP port 60022.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `nix.settings.substituters` is forced to just `https://cache.nixos.org` —
|
||||||
|
`shill` sits next to the S3 cache on `object`, so it doesn't use it.
|
||||||
|
- [`hercules.nix`](../../../../nixos/boxes/colony/vms/shill/hercules.nix)
|
||||||
|
(Hercules CI agent + the `nix-cache-gc` timer for the S3 binary cache)
|
||||||
|
exists but is **currently disabled**: the file is not imported by
|
||||||
|
`shill/default.nix`.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/default.nix`](../../../../nixos/boxes/colony/vms/shill/default.nix) — VM config, networkd, firewall, `my.containers.instances`.
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/default.nix`](../../../../nixos/boxes/colony/vms/shill/containers/default.nix) — container imports.
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers-ext.nix`](../../../../nixos/boxes/colony/vms/shill/containers-ext.nix) — the `jam` nspawn container.
|
||||||
|
- [`nixos/modules/containers.nix`](../../../../nixos/modules/containers.nix) — the `my.containers` module.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# chatterbox
|
||||||
|
|
||||||
|
The Matrix homeserver for `nul.ie` (Synapse) and its bridges to other chat networks.
|
||||||
|
[middleman](middleman.md) fronts it as `matrix.nul.ie` for clients and on `:8448` for
|
||||||
|
federation.
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/chatterbox.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/chatterbox.nix)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### Synapse
|
||||||
|
|
||||||
|
`matrix-synapse` serves `nul.ie` at `https://matrix.nul.ie`, with Element at `element.nul.ie`.
|
||||||
|
Port 8008 carries client and federation traffic with forwarded headers, while a localhost manhole
|
||||||
|
uses port 9000. Registration and guest access are disabled; uploads, dynamic thumbnails and URL
|
||||||
|
previews are enabled, with preview fetching restricted to [middleman](middleman.md).
|
||||||
|
|
||||||
|
### Bridges
|
||||||
|
|
||||||
|
The box runs heisenbridge for IRC, `mautrix-whatsapp`, and two `mautrix-meta` instances for
|
||||||
|
Messenger and Instagram. The mautrix bridges use [colony-psql](colony-psql.md), default to
|
||||||
|
end-to-end encryption, and share the secret `doublepuppet.yaml` registration for double puppeting
|
||||||
|
onto `nul.ie`. The firewall admits ports 8008 and 8009 in addition to netdata.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `chatterbox`).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Synapse's real database config lives in the `chatterbox/synapse.yaml` age secret — options
|
||||||
|
only merge at the top level, so the base config carries a dummy `sqlite3` block to satisfy
|
||||||
|
the module defaults. The signing key is also an age secret.
|
||||||
|
- `olm-3.2.16` is allowed via `permittedInsecurePackages` (a nixpkgs E2EE library issue).
|
||||||
|
- The bridge services get `ffmpeg` on their `PATH` for GIF→video conversion.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/chatterbox.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/chatterbox.nix) — container definition, Synapse settings and all bridge configuration
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# colony-psql
|
||||||
|
|
||||||
|
The shared PostgreSQL instance for colony services. Rather than each service running its own
|
||||||
|
database, the containers (and the `git` VM) connect here over the `ctrs` network.
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/colony-psql.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/colony-psql.nix)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- **PostgreSQL 14** with TCP/IP enabled, reachable from the whole colony address space using `md5`
|
||||||
|
authentication. The firewall allows `5432`.
|
||||||
|
- Local `peer` auth maps `postgres`, `root`, `netdata` and `dev` to the `postgres` superuser via
|
||||||
|
the ident map.
|
||||||
|
- **netdata** with the Python PostgreSQL collector.
|
||||||
|
- Consumers wait for the database to accept connections with the `lib.my.systemdAwaitPostgres`
|
||||||
|
helper (e.g. `sharry`, `atticd`, `mastodon-init-db`, and `middleman`'s nginx as a DNS
|
||||||
|
bootstrap hack).
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `colony-psql`).
|
||||||
|
|
||||||
|
The assignment also has the alt name `colony-psql` (no `-ctr` suffix), which is what consumers
|
||||||
|
use as the database hostname.
|
||||||
|
|
||||||
|
## Consumers
|
||||||
|
|
||||||
|
- [object](object.md) — `sharry` and `hedgedoc` (and `atticd` when enabled) over
|
||||||
|
`colony-psql:5432`
|
||||||
|
- [toot](toot.md) — Mastodon's database (Mastodon currently disabled)
|
||||||
|
- [chatterbox](chatterbox.md) — the mautrix bridges (WhatsApp, Messenger, Instagram) via
|
||||||
|
Postgres URIs in their secret env files
|
||||||
|
- `git` VM — Gitea
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/colony-psql.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/colony-psql.nix) — container definition and PostgreSQL configuration
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# gam
|
||||||
|
|
||||||
|
A game-server container currently dedicated to Terraria.
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/gam.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/gam.nix)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
Runs lightweight game servers directly as NixOS services, rather than as OCI containers on
|
||||||
|
[`whale2`](../../whale2.md).
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `gam`).
|
||||||
|
|
||||||
|
## Terraria server
|
||||||
|
|
||||||
|
`services.terraria` runs the dedicated server with its world at
|
||||||
|
`/var/lib/terraria/NotWorld.wld`. It creates large worlds automatically, uses the MOTD
|
||||||
|
"sup gamers", and disables UPnP. Additional settings such as the password come from the
|
||||||
|
`gam/terraria.conf` age secret used as the service configuration file.
|
||||||
|
|
||||||
|
`openFirewall` is enabled, and `estuary` forwards TCP and UDP port `7777` to the container.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/gam.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/gam.nix) — container definition and the Terraria service
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# jackflix
|
||||||
|
|
||||||
|
The media stack — acquisition, library, streaming and photos. Torrent traffic is routed through
|
||||||
|
an AirVPN WireGuard tunnel so downloads only flow while the VPN is up.
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/jackflix/`](../../../../../nixos/boxes/colony/vms/shill/containers/jackflix)
|
||||||
|
(`default.nix`, `networking.nix`)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md) (bind-mounts `/mnt/media` read-write)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
| Service | Port | Purpose |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Jellyfin | `8096` | streaming, published as `jackflix.nul.ie` |
|
||||||
|
| Transmission | `9091` | BitTorrent client (`transmission_4`), published as `torrents.nul.ie` (SSO) |
|
||||||
|
| Jackett | `9117` | indexer aggregator, `jackett.nul.ie` (SSO) |
|
||||||
|
| FlareSolverr | — | Cloudflare challenge solver for Jackett |
|
||||||
|
| Radarr | `7878` | movies, `radarr.nul.ie` (SSO) |
|
||||||
|
| Sonarr | `8989` | TV, `sonarr.nul.ie` (SSO) |
|
||||||
|
| Jellyseerr (`seerr`) | `5055` | request portal, `gib.nul.ie` (`openFirewall` on) |
|
||||||
|
| PhotoPrism | `2342` | photos, `photos.nul.ie`; password auth, sqlite DB, originals/import under `/mnt/media/photoprism` |
|
||||||
|
| copyparty | `3923` | file sharing, `stuff.nul.ie`; serves `/mnt/media/public` (read-only to everyone) and `/priv` → `/mnt/media/stuff` (admin for `dev`), share creation, indexing (`e2dsa`/`e2t`), file-magic checks |
|
||||||
|
|
||||||
|
All published through [middleman](middleman.md) as shown. A shared `media` group plus a group-writable
|
||||||
|
umask on Radarr/Sonarr gives the apps coordinated access to the media volume.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `jackflix`).
|
||||||
|
|
||||||
|
## VPN download path
|
||||||
|
|
||||||
|
[`networking.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/jackflix/networking.nix)
|
||||||
|
defines a `vpn` WireGuard netdev to **AirVPN NL** using a key and PSK from age secrets:
|
||||||
|
|
||||||
|
- Policy routing keeps colony traffic on the main table while everything else falls through to the
|
||||||
|
VPN's dedicated table, so the services stay reachable on the `ctrs` network while outbound
|
||||||
|
torrent traffic exits via AirVPN. `DNSDefaultRoute` is disabled on `host0`; the VPN provides DNS.
|
||||||
|
- `transmission` and `jackett` `bindsTo` `systemd-networkd-wait-online@vpn.service` — they only
|
||||||
|
run while the tunnel is up.
|
||||||
|
- AirVPN forwards a peer port to Transmission (`peer-port`); the firewall accepts it and drops
|
||||||
|
other new inbound TCP from `vpn`, while non-VPN input is limited to the service ports
|
||||||
|
(netdata, Transmission, Jackett, Radarr, Sonarr, Jellyfin, PhotoPrism) plus copyparty's `3923`
|
||||||
|
from the base config and Jellyseerr's `5055`.
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
Media lives on the shared `/mnt/media` volume (bind-mounted read-write from `shill`); Transmission
|
||||||
|
downloads into `/mnt/media/downloads/torrents` with a `.incomplete` directory and configured
|
||||||
|
bandwidth and seed-ratio limits.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/jackflix/default.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/jackflix/default.nix) — container definition and the media services
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/jackflix/networking.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/jackflix/networking.nix) — AirVPN WireGuard netdev, policy routing and VPN firewall rules
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# middleman
|
||||||
|
|
||||||
|
The front-end reverse proxy for the colony's public web services — the single ingress that
|
||||||
|
`estuary` DNATs HTTP/HTTPS (and Matrix federation on `:8448`) to. Terminates TLS with wildcard
|
||||||
|
certificates it issues itself, provides nginx-sso for gated vhosts, and runs a librespeed
|
||||||
|
backend.
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/middleman/`](../../../../../nixos/boxes/colony/vms/shill/containers/middleman)
|
||||||
|
(`default.nix`, `vhosts.nix`)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md) (`my.containers` ephemeral nspawn on the
|
||||||
|
`ctrs` bridge; bind-mounts `/mnt/media` read-only for the static file vhosts)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### nginx
|
||||||
|
|
||||||
|
The reverse proxy enables `vts`, `fancyindex`, Brotli, kTLS and a proxy cache. Its dynamic resolver
|
||||||
|
points at `estuary`, allowing upstreams named under `ams1.int.nul.ie` to resolve again at runtime.
|
||||||
|
All vhosts live in
|
||||||
|
[`vhosts.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/middleman/vhosts.nix). nginx
|
||||||
|
also waits for `colony-psql` through `systemdAwaitPostgres`, avoiding an early-boot DNS stall.
|
||||||
|
|
||||||
|
### ACME
|
||||||
|
|
||||||
|
`middleman` issues certificates for its own vhosts; it is not a shared CA for other boxes.
|
||||||
|
|
||||||
|
- `ams1.int.nul.ie` and its wildcard use a lego `exec` challenge that SSHes to
|
||||||
|
`pdns-file-records@estuary-vm`. This is the default `useACMEHost` certificate internally.
|
||||||
|
- `nul.ie`, `*.nul.ie` and `*.s3.nul.ie` use Cloudflare DNS. A `postRun` hook copies renewed
|
||||||
|
material to the `mail` VM and runs `mailcow-ssl-reload` there.
|
||||||
|
- Renewal reloads nginx; the `acme` group owns the secret files and includes the nginx user.
|
||||||
|
|
||||||
|
### nginx-sso
|
||||||
|
|
||||||
|
The `generic` SSO instance at `sso.nul.ie` uses Google OAuth by default and also offers a simple
|
||||||
|
username/password provider. Its cookie domain is `.nul.ie`; gated vhosts include the generated
|
||||||
|
`server-generic.conf` / `location-generic.conf` snippets from `/etc/nginx/includes/sso/`.
|
||||||
|
|
||||||
|
### librespeed
|
||||||
|
|
||||||
|
The frontend and backend are published as `speed.nul.ie` and `librespeed.ams1.int.nul.ie`, both
|
||||||
|
proxied to `localhost:8989`.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `middleman`).
|
||||||
|
|
||||||
|
The firewall allows `http`, `https` and `8448` (Matrix federation). A small nftables SNAT rule
|
||||||
|
rewrites outbound IPv6 to the container's own address on `host0`.
|
||||||
|
|
||||||
|
## Published vhosts
|
||||||
|
|
||||||
|
Everything is under `*.nul.ie` with the public wildcard cert unless noted; defaults applied to
|
||||||
|
all vhosts are `onlySSL`, kTLS and HTTP/2. "SSO" = gated behind nginx-sso (`generic` instance).
|
||||||
|
|
||||||
|
| Host | Upstream | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `nul.ie` (`_`, default) | static | landing page (`index.html`, CV PDF, SSH pubkey); serves Matrix `.well-known`s and redirects `webfinger`/`nodeinfo`/`host-meta` → `toot.nul.ie`, `atproto-did` → `pds.nul.ie`; `forceSSL` (plain HTTP redirects to HTTPS) |
|
||||||
|
| `localhost` | — | loopback-only VTS status page at `/status` (scraped by netdata); plain HTTP |
|
||||||
|
| `sso.nul.ie` | `localhost:8082` | nginx-sso endpoint |
|
||||||
|
| `netdata-colony.nul.ie` | `<host>.ams1.int.nul.ie:19999` | netdata fan-out over `vm`, `fw`, `ctr`, `oci`, `http`, `jackflix-ctr`, `chatterbox-ctr`, `colony-psql-ctr`; **SSO** |
|
||||||
|
| `pass.nul.ie` | `vaultwarden-ctr:8080` | [vaultwarden](vaultwarden.md); `/notifications/hub` proxied with websockets |
|
||||||
|
| `matrix.nul.ie` | `chatterbox-ctr:8008` | [chatterbox](chatterbox.md) Synapse client + federation; also listens on `:8448` as federation `default_server`; `= /` redirects to Element; serves Matrix `.well-known`s |
|
||||||
|
| `element.nul.ie` | static `element-web` | Element configured for the `nul.ie` homeserver |
|
||||||
|
| `torrents.nul.ie` | `jackflix-ctr:9091` | Transmission ([jackflix](jackflix.md)); **SSO** |
|
||||||
|
| `jackett.nul.ie` | `jackflix-ctr:9117` | **SSO** |
|
||||||
|
| `radarr.nul.ie` | `jackflix-ctr:7878` | **SSO**; websockets |
|
||||||
|
| `sonarr.nul.ie` | `jackflix-ctr:8989` | **SSO**; websockets |
|
||||||
|
| `gib.nul.ie` | `jackflix-ctr:5055` | Jellyseerr requests |
|
||||||
|
| `jackflix.nul.ie` | `jackflix-ctr:8096` | Jellyfin; `/socket` websockets; `/` redirects to `/web/` |
|
||||||
|
| `toot.nul.ie` | `toot-ctr:80` | Mastodon — **upstream currently disabled**, see [toot](toot.md) |
|
||||||
|
| `pds.nul.ie` | `toot-ctr:3000` | Bluesky PDS ([toot](toot.md)); websockets |
|
||||||
|
| `share.nul.ie` | `object-ctr:9090` | Sharry ([object](object.md)); websockets |
|
||||||
|
| `stuff.nul.ie` | `jackflix-ctr:3923` | copyparty |
|
||||||
|
| `public.nul.ie` (+ alias `p.nul.ie`) | static `/mnt/media/public` | fancyindex file listing; `addSSL` so plain HTTP also works |
|
||||||
|
| `mc-map.nul.ie` | `simpcraft-oci:8100` | Minecraft map (OCI container on [`whale2`](../../whale2.md#game-servers)) |
|
||||||
|
| `mc-rail.nul.ie` | `simpcraft-oci:3876` | Minecraft railway map ([`whale2`](../../whale2.md#game-servers)) |
|
||||||
|
| `mc-map-kink.nul.ie` | `kinkcraft-oci:8100` | Minecraft map ([`whale2`](../../whale2.md#game-servers)) |
|
||||||
|
| `speed.nul.ie` | `localhost:8989` | librespeed |
|
||||||
|
| `librespeed.ams1.int.nul.ie` | `localhost:8989` | librespeed on the internal domain (internal wildcard cert) |
|
||||||
|
| `md.nul.ie` | `object-ctr:3000` | HedgeDoc; websockets |
|
||||||
|
| `pb.nul.ie` | `object-ctr:8088` | wastebin |
|
||||||
|
| `photos.nul.ie` | `jackflix-ctr:2342` | PhotoPrism; websockets |
|
||||||
|
| `pront.nul.ie` | `stream-hi.h.nul.ie:5000` | OctoPrint on the home network ([`stream`](../../../home/stream.md)); `/webcam/` → `:5050`; **SSO** |
|
||||||
|
| `hass.nul.ie` | `hass-ctr.h.nul.ie:8123` | [Home Assistant](../../../home/sfh/containers/hass.md) (home network); websockets |
|
||||||
|
| `hass-john.nul.ie` | `john-valorant-tun.ams1.int.nul.ie:8123` | remote Home Assistant over the point-to-point tunnel; websockets |
|
||||||
|
| `minio.nul.ie` | `object-ctr:9001` | MinIO console; `/ws` websockets |
|
||||||
|
| `s3.nul.ie` (+ `*.s3.nul.ie`) | `object-ctr:9000` | MinIO S3 API (virtual-host style via the `*.s3` wildcard cert); `/gitea/packages/` has a hack forcing the correct `Content-Type` for Docker image manifests |
|
||||||
|
| `nix-cache.nul.ie` | `object-ctr:5000` | Harmonia Nix binary cache; `.narinfo`/`nar/`/`serve/` paths get immutable `Cache-Control`/`Expires` headers |
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/middleman/default.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/middleman/default.nix) — container definition: nginx, ACME, nginx-sso, librespeed, secrets
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/middleman/vhosts.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/middleman/vhosts.nix) — all virtual hosts, the SSO include helpers, and the `.well-known` tree
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/middleman/default.html`](../../../../../nixos/boxes/colony/vms/shill/containers/middleman/default.html) — default vhost landing page
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# object
|
||||||
|
|
||||||
|
Object storage and the Nix binary cache, plus a few small self-hosted web apps (Sharry,
|
||||||
|
HedgeDoc, wastebin).
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/object.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/object.nix)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md) (bind-mounts `/mnt/minio` and
|
||||||
|
`/mnt/nix-cache` read-write)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
| Service | Port | Purpose |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| MinIO | `9000` (S3) / `9001` (console) | S3-compatible object storage, `s3.nul.ie` + `*.s3.nul.ie` (virtual-host style via `MINIO_DOMAIN`), console at `minio.nul.ie`; region `eu-central-1`; data on the `/mnt/minio` XFS volume |
|
||||||
|
| Harmonia | `5000` | Nix binary cache at `nix-cache.nul.ie` — `harmonia-dev` cache serves `shill`'s `/nix/store` out of a dedicated store view rooted at `/var/lib/harmonia` (bind-mounted from `/mnt/nix-cache`), signed with the `nix-cache.key` secret; a `harmonia` user with authorized keys exists for cache pushes |
|
||||||
|
| Sharry | `9090` | file sharing at `share.nul.ie`; Postgres on [colony-psql](colony-psql.md), files stored in the `share` MinIO bucket; fixed `dev` account + invite signup; mail via `mail.nul.ie`; configured share-size limit |
|
||||||
|
| HedgeDoc | `3000` | collaborative markdown notes at `md.nul.ie`; Postgres on [colony-psql](colony-psql.md); anonymous edits but no anonymous notes, email login, no open email registration |
|
||||||
|
| wastebin | `8088` | pastebin at `pb.nul.ie` |
|
||||||
|
| atticd | `8069` | **currently disabled** (`services.atticd.enable = false`) — an alternative Nix cache that would store locally and sit behind `nix-cache.nul.ie`; config (including the `object/atticd.env` secret) is kept around |
|
||||||
|
|
||||||
|
Everything public is fronted by [middleman](middleman.md) (see its vhost table). The
|
||||||
|
`minio-client` is installed and the user's `~/.mc/config.json` points at an age-secret config.
|
||||||
|
`minio-2025-10-15T17-29-55Z` is allowlisted via `permittedInsecurePackages` (flagged as a TODO).
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `object`).
|
||||||
|
|
||||||
|
## Backing services
|
||||||
|
|
||||||
|
- [colony-psql](colony-psql.md) — Sharry and HedgeDoc databases (atticd too, when enabled).
|
||||||
|
- MinIO buckets back other boxes' services: Gitea LFS/packages (with the `middleman` MIME hack
|
||||||
|
for Docker manifests), Mastodon's `mastodon` bucket and the Bluesky PDS `pds` bucket on
|
||||||
|
[toot](toot.md), and Sharry's `share` bucket.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/object.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/object.nix) — container definition and all services
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# qclk
|
||||||
|
|
||||||
|
A WireGuard management appliance for the `qclk` network — it terminates the `management`
|
||||||
|
tunnel and routes/NATs the `qclk` prefix. No service daemon is currently defined in the config;
|
||||||
|
the container provides the network plumbing and opens the API port.
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/qclk/`](../../../../../nixos/boxes/colony/vms/shill/containers/qclk)
|
||||||
|
(`default.nix`)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- **WireGuard `management` interface** — listens on UDP `51821` (`lib.my.c.colony.qclk.wgPort`,
|
||||||
|
allowed through the firewall; `estuary` port-forwards it here) with the private key from the
|
||||||
|
`qclk/wg.key` age secret. Managed devices are static peers, each pinned to its own address in the
|
||||||
|
`qclk` prefix; the peer list currently has a single entry.
|
||||||
|
- **Routing/NAT** — `shill` routes the `qclk` prefix to this container, and outbound traffic from
|
||||||
|
`host0` into `management` is
|
||||||
|
SNATed to the container's `qclk` address. Forwarding into `management` is accepted from the
|
||||||
|
AS211024 trusted IPv4 ranges (`lib.my.c.as211024.trusted.v4`).
|
||||||
|
- **API port** — TCP `8080` is accepted on the `management` interface (`apiPort`), but note
|
||||||
|
`services = { }`: whatever serves the qclk API is not defined in this configuration today.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `qclk`).
|
||||||
|
|
||||||
|
Two assignments: `internal` on the `ctrs` network like the other containers, and `qclk` on the
|
||||||
|
`management` WireGuard interface (IPv4 only, no DNS name).
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/qclk/default.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/qclk/default.nix) — container definition: WireGuard netdev, peer list and firewall rules
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# toot
|
||||||
|
|
||||||
|
Federated-social container. Despite the name, the only service actually running is a **Bluesky
|
||||||
|
PDS** — the Mastodon instance ("toots") is **currently disabled**.
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/toot.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/toot.nix)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### Bluesky PDS
|
||||||
|
|
||||||
|
The active service listens on port 3000 as `pds.nul.ie`, fronted by [middleman](middleman.md),
|
||||||
|
which also redirects `/.well-known/atproto-did` here. It requires invites, applies an upload-size
|
||||||
|
limit, and stores blobs in [object](object.md)'s `pds` MinIO bucket. Federation uses the standard
|
||||||
|
Bluesky services and crawlers. `toot/pds.env` supplies secrets, including S3 credentials, and mail
|
||||||
|
comes from `pds@nul.ie`.
|
||||||
|
|
||||||
|
### Mastodon
|
||||||
|
|
||||||
|
Mastodon is disabled with `services.mastodon.enable = false`, but its configuration remains:
|
||||||
|
|
||||||
|
- `nul.ie` is the local domain and `toot.nul.ie` the web domain.
|
||||||
|
- PostgreSQL runs on [colony-psql](colony-psql.md), with local Redis and SMTP through `mail.nul.ie`.
|
||||||
|
- Media uses the `mastodon` MinIO bucket, a configured streaming-process pool, and periodic cleanup.
|
||||||
|
- [middleman](middleman.md) still proxies the dead vhost and `.well-known` endpoints.
|
||||||
|
|
||||||
|
The removed `otpSecretFile` option must be addressed before the service can return.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `toot`).
|
||||||
|
|
||||||
|
The firewall allows `http` (the Mastodon nginx vhost) and the PDS port `3000` besides netdata.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The local nginx still carries the Mastodon virtual host (`toot.nul.ie`) with proxy-header
|
||||||
|
overrides for being behind `middleman` — part of the preserved-but-disabled Mastodon setup.
|
||||||
|
- `mastodon-init-dirs` appends the S3 secret key to Mastodon's `.secrets_env` (the module has no
|
||||||
|
option for a secret-key file), and `mastodon-init-db` waits for `colony-psql` — moot while
|
||||||
|
Mastodon is disabled.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/toot.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/toot.nix) — container definition; active PDS config and the preserved (disabled) Mastodon config
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# vaultwarden
|
||||||
|
|
||||||
|
[Vaultwarden](https://github.com/dani-garcia/vaultwarden), a Bitwarden-compatible password
|
||||||
|
manager, published as `pass.nul.ie` through [middleman](middleman.md).
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/vaultwarden.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/vaultwarden.nix)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- **vaultwarden** — HTTP on `[::]:8080`, WebSocket notifications on `3012` (both allowed through
|
||||||
|
the firewall). Web vault enabled, signups disabled, Bitwarden push notifications enabled
|
||||||
|
(`PUSH_ENABLED`). `DOMAIN` is `https://pass.nul.ie`.
|
||||||
|
- **SMTP** via `mail.nul.ie:587` (STARTTLS) as `pass@nul.ie`; credentials and other sensitive
|
||||||
|
settings come from the `vaultwarden/config.env` age secret.
|
||||||
|
- **Backups** — a `borgbackup` job pushes `/var/lib/vaultwarden` to rsync.net
|
||||||
|
using a repokey-encrypted repository; its passphrase and SSH key come from secrets, and the job
|
||||||
|
keeps daily, weekly and monthly archives according to its configured retention policy.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `vaultwarden`).
|
||||||
|
|
||||||
|
## Persistence
|
||||||
|
|
||||||
|
`/var/lib/vaultwarden` is persisted through `my.tmproot.persistence` — like the other
|
||||||
|
`shill` containers the root is ephemeral and real state lives under `/persist` (bind-mounted
|
||||||
|
from the host's `/persist/containers/vaultwarden`).
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/vaultwarden.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/vaultwarden.nix) — container definition, service config and the borgbackup job
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# waffletail
|
||||||
|
|
||||||
|
The colony Tailscale node: a subnet router and exit node that advertises the colony prefixes
|
||||||
|
into the tailnet.
|
||||||
|
|
||||||
|
- **Source:** [`shill/containers/waffletail.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/waffletail.nix)
|
||||||
|
- **Host:** NixOS container on [`shill`](../README.md)
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### Tailscale
|
||||||
|
|
||||||
|
The node authenticates to `hs.nul.ie` with the secret `tailscale-auth.key`. It disables Tailscale's
|
||||||
|
netfilter management, advertises itself as an exit node and advertises the colony IPv4/IPv6 ranges,
|
||||||
|
but does not accept routes. UDP port 41641 is open and forwarded here by `estuary`.
|
||||||
|
|
||||||
|
### Routing and firewall
|
||||||
|
|
||||||
|
`shill` routes the Tailscale prefixes to this container.
|
||||||
|
The repository's nftables rules trust `tailscale0`, permit colony-sourced forwarding into the
|
||||||
|
tailnet, and SNAT tailnet traffic leaving through `host0` unless its destination is already within
|
||||||
|
colony.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `waffletail`).
|
||||||
|
|
||||||
|
Two assignments: `internal` on the `ctrs` network like the other containers, and `tailscale` for
|
||||||
|
its addresses on the tailnet itself (no DNS name).
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/shill/containers/waffletail.nix`](../../../../../nixos/boxes/colony/vms/shill/containers/waffletail.nix) — container definition, Tailscale setup and forward/NAT rules
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# whale2
|
||||||
|
|
||||||
|
The colony podman/OCI host, dedicated to game servers (kept off `shill` so
|
||||||
|
container churn and resource use stay isolated).
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/colony/vms/whale2/`](../../../nixos/boxes/colony/vms/whale2)
|
||||||
|
(`default.nix`, `valheim.nix`, `minecraft/`, `enshrouded.nix`)
|
||||||
|
- **Host:** VM on `colony`
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### Container runtime
|
||||||
|
|
||||||
|
OCI containers run under podman (`virtualisation.oci-containers`) with the netavark backend and
|
||||||
|
`firewall_driver = "none"`, leaving firewall management to `my.firewall`.
|
||||||
|
|
||||||
|
### Routable game servers
|
||||||
|
|
||||||
|
Each server gets an address from `extraAssignments` (`valheim-oci`, `simpcraft-oci`, …) on the
|
||||||
|
`colony` netavark network. That network is backed by the `oci` interface and `prefixes.oci` v4/v6
|
||||||
|
ranges; `lib.my.dockerNetAssignment` supplies the address through `--network=colony:ip=…`.
|
||||||
|
`estuary` forwards the public game ports, while IPv6 reaches the containers directly.
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
`/var/lib/containers` is a dedicated XFS disk with project quotas.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `whale2`).
|
||||||
|
|
||||||
|
## Game servers
|
||||||
|
|
||||||
|
The OCI containers are documented here (they have no pages of their own).
|
||||||
|
Their per-container `extraAssignments` on the `oci` network are listed in the generated
|
||||||
|
[network assignments](../../networking.md#box-assignments) table. Ports below are the public ones
|
||||||
|
forwarded by `estuary`.
|
||||||
|
|
||||||
|
| Container | Ports | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| `valheim` | `2456-2457`/udp | running |
|
||||||
|
| `simpcraft` | `25565` tcp+udp | running |
|
||||||
|
| `simpcraft-staging` | `25566` tcp | **disabled** (commented out) |
|
||||||
|
| `enshrouded` | `15636-15637`/udp | **disabled** (`enshrouded.nix` not imported) |
|
||||||
|
| `kevcraft` | `25567` tcp+udp | running |
|
||||||
|
| `kinkcraft` | `25568` tcp+udp | running |
|
||||||
|
| `graeme` | `25569` tcp+udp | running |
|
||||||
|
|
||||||
|
- **valheim** ([`valheim.nix`](../../../nixos/boxes/colony/vms/whale2/valheim.nix)) —
|
||||||
|
`lloesche/valheim-server`, public server "amogus sus", world `simpland2`,
|
||||||
|
allow-listed Steam IDs, password from agenix.
|
||||||
|
- **simpcraft** ([`minecraft/`](../../../nixos/boxes/colony/vms/whale2/minecraft)) —
|
||||||
|
`itzg/minecraft-server` (self-built `git.nul.ie/dev/craftblock` image),
|
||||||
|
Modrinth "Simpcraft" modpack, whitelist + ops.
|
||||||
|
- **simpcraft-staging** — the same setup pinned to an older pack version, currently commented out.
|
||||||
|
- **kevcraft** — vanilla Minecraft 1.20.1, extra op.
|
||||||
|
- **kinkcraft** — same Simpcraft modpack as `simpcraft`.
|
||||||
|
- **graeme** — vanilla Minecraft on hard difficulty with its own whitelist.
|
||||||
|
- **enshrouded** ([`enshrouded.nix`](../../../nixos/boxes/colony/vms/whale2/enshrouded.nix)) —
|
||||||
|
`sknnr/enshrouded-dedicated-server` ("UWUshrouded"); the file exists but is
|
||||||
|
commented out of `whale2`'s `imports`, so the server is down (its forwards
|
||||||
|
and DNS records remain).
|
||||||
|
|
||||||
|
The Minecraft containers share one whitelist/ops list and agenix env file
|
||||||
|
(`whale2/simpcraft.env`, which also carries the RCON password).
|
||||||
|
|
||||||
|
## Backups
|
||||||
|
|
||||||
|
A local borg job (`services.borgbackup.jobs.simpcraft`) archives the `simpcraft` world frequently,
|
||||||
|
offset from its autosave timer, into `/var/lib/containers/backup/simpcraft`. It uses `mcrcon` to
|
||||||
|
`save-off`/`save-on` around each run and keeps a short history for quick world rollback rather than
|
||||||
|
disaster recovery; the `oci` LV itself is covered by `colony`'s `borgthin`.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/vms/whale2/default.nix`](../../../nixos/boxes/colony/vms/whale2/default.nix) — VM config, podman/netavark setup, `extraAssignments`.
|
||||||
|
- [`nixos/boxes/colony/vms/whale2/valheim.nix`](../../../nixos/boxes/colony/vms/whale2/valheim.nix) — Valheim server.
|
||||||
|
- [`nixos/boxes/colony/vms/whale2/minecraft/default.nix`](../../../nixos/boxes/colony/vms/whale2/minecraft/default.nix) — the Minecraft servers + world backup job.
|
||||||
|
- [`nixos/boxes/colony/vms/whale2/enshrouded.nix`](../../../nixos/boxes/colony/vms/whale2/enshrouded.nix) — Enshrouded server (disabled, not imported).
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Home site
|
||||||
|
|
||||||
|
The home network (domain `h.nul.ie`): a redundant pair of routers in front of a VM host, an
|
||||||
|
NVMe-oF storage target, an IoT container host, and a workstation. The two routers — `river` (a VM)
|
||||||
|
and `stream` (a physical box) — are built from one shared
|
||||||
|
[`routing-common`](../../../nixos/boxes/home/routing-common) definition as an active/backup VRRP
|
||||||
|
pair, and everything clients touch (gateway, DNS) is a floating VIP that follows the master.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/home/`](../../../nixos/boxes/home)
|
||||||
|
|
||||||
|
## Boxes
|
||||||
|
|
||||||
|
| Box | Role | Host |
|
||||||
|
|---|---|---|
|
||||||
|
| [`palace`](palace.md) | VM host | physical |
|
||||||
|
| [`river`](river.md) | Primary router (VRRP pair with `stream`) | VM on `palace` |
|
||||||
|
| [`stream`](stream.md) | Secondary router (VRRP pair with `river`) | physical |
|
||||||
|
| [`cellar`](cellar.md) | NVMe-oF / SPDK storage target | VM on `palace` |
|
||||||
|
| [`sfh`](sfh/README.md) | NixOS container host (containers on its page) | VM on `palace` |
|
||||||
|
| [`castle`](castle.md) | Workstation / gaming desktop | physical |
|
||||||
|
|
||||||
|
## Router VIPs
|
||||||
|
|
||||||
|
Clients use per-VLAN floating VIPs as their gateway and DNS server; `keepalived` moves them between
|
||||||
|
`river` and `stream`. The addresses, DHCP/RA behavior and failover mechanics are documented once in
|
||||||
|
[Router VIPs](../../networking.md#router-vips) and [Router HA](../../networking.md#router-ha).
|
||||||
|
|
||||||
|
## Networks
|
||||||
|
|
||||||
|
The site separates core management, high-MTU trusted traffic, general trusted traffic, untrusted
|
||||||
|
clients and the two WAN paths. VLAN IDs, prefixes, MTUs and router addressing live in the canonical
|
||||||
|
[`home` section of networking.md](../../networking.md#home).
|
||||||
|
|
||||||
|
## Switch fabric
|
||||||
|
|
||||||
|
The boxes hang off three hand-configured switches — `jim` and `dave` (MikroTik, RouterOS) and
|
||||||
|
`brian` (Ubiquiti, UniFi) — which are **not** managed by this flake. The physical topology, VLAN
|
||||||
|
map, the Digiweb WAN path (trunked VLAN 10 + PVID 140 at the ONT edge), and the multi-ONT plan are
|
||||||
|
documented in [switches.md](switches.md).
|
||||||
|
|
||||||
|
## Wireless APs
|
||||||
|
|
||||||
|
The Wi-Fi APs — `vibe` (MikroTik cAP ax) and `wave` (Cudy AX3000 on OpenWrt) — are dumb APs, also
|
||||||
|
**not** managed by this flake. The shared VLAN-trunk design, SSIDs, per-AP management addressing,
|
||||||
|
and the OpenWrt flash/config for `wave` are in [aps.md](aps.md).
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
# Home wireless APs
|
# Home wireless APs
|
||||||
|
|
||||||
Reference for the home Wi-Fi access points. Like the switches (`home-switches.md`), these are **not**
|
Reference for the home Wi-Fi access points. Like the switches ([switches.md](switches.md)), these
|
||||||
managed by this flake — they are configured on-device (RouterOS on the MikroTik, OpenWrt/UCI on the
|
are **not** managed by this flake — they are configured on-device (RouterOS on the MikroTik,
|
||||||
Cudy). This file documents the shared VLAN/trunk design and each AP.
|
OpenWrt/UCI on the Cudy). This file documents the shared VLAN/trunk design and each AP.
|
||||||
|
|
||||||
Only the DNS records live in the flake (`nixos/boxes/home/routing-common/dns.nix`, `h.nul.ie` zone).
|
Only the DNS records live in the flake
|
||||||
Everything else here is applied by hand on the device.
|
([`nixos/boxes/home/routing-common/dns.nix`](../../../nixos/boxes/home/routing-common/dns.nix),
|
||||||
|
`h.nul.ie` zone). Everything else here is applied by hand on the device.
|
||||||
|
|
||||||
## The APs
|
## The APs
|
||||||
|
|
||||||
@@ -64,16 +65,18 @@ RouterOS, one hardware-offloaded bridge `main` with `vlan-filtering=yes`. Access
|
|||||||
(WPA2-PSK), untagged onto VLAN 120. `country=Ireland`.
|
(WPA2-PSK), untagged onto VLAN 120. `country=Ireland`.
|
||||||
- **Bridge VLANs** — 100 tagged `main,ether1`; 110 tagged `main,ether1` + untagged
|
- **Bridge VLANs** — 100 tagged `main,ether1`; 110 tagged `main,ether1` + untagged
|
||||||
`ether2,wifi1,wifi2`; 120 tagged `main,ether1` + untagged `wifi3`.
|
`ether2,wifi1,wifi2`; 120 tagged `main,ether1` + untagged `wifi3`.
|
||||||
- **Management** — `jim`/`dave`-style (core/hi/lo), on host `.15`: `192.168.64.15` on core
|
|
||||||
(native/untagged, backup), `192.168.68.15/22` + `2a0e:97c0:4d0:1::1:6` on the `hi` VLAN-100
|
|
||||||
interface (holds the default route, via the hi VIP `192.168.71.254`), and
|
|
||||||
`192.168.72.15/21` + `2a0e:97c0:4d0:2::1:6` on `lo` VLAN 110. No IP on `untrusted`.
|
|
||||||
`l2mtu 9214`, so `hi` carries jumbo (9000) here — `vibe` sits on `hi` because it *can* jumbo,
|
|
||||||
unlike `wave` (see its MTU note).
|
|
||||||
- **Roaming** — 802.11k/v via a `/interface wifi steering` profile (`rrm=yes wnm=yes`,
|
- **Roaming** — 802.11k/v via a `/interface wifi steering` profile (`rrm=yes wnm=yes`,
|
||||||
`neighbor-group=home-aps`) assigned to `wifi1`/`wifi2`/`wifi3`.
|
`neighbor-group=home-aps`) assigned to `wifi1`/`wifi2`/`wifi3`.
|
||||||
- **Resolver** — the hi VIP `192.168.71.254` / `2a0e:97c0:4d0:1::ffff`.
|
- **Resolver** — the hi VIP `192.168.71.254` / `2a0e:97c0:4d0:1::ffff`.
|
||||||
|
|
||||||
|
### Management
|
||||||
|
|
||||||
|
Management uses host `.15`: `192.168.64.15` on native/core as a backup,
|
||||||
|
`192.168.68.15/22` + `2a0e:97c0:4d0:1::1:6` on `hi` VLAN 100, and
|
||||||
|
`192.168.72.15/21` + `2a0e:97c0:4d0:2::1:6` on `lo` VLAN 110. The `hi` address holds the default
|
||||||
|
route through its VIP; `untrusted` has no address. With `l2mtu 9214`, `vibe` can use the jumbo
|
||||||
|
`hi` network unlike `wave`.
|
||||||
|
|
||||||
## wave (Cudy AX3000, OpenWrt)
|
## wave (Cudy AX3000, OpenWrt)
|
||||||
|
|
||||||
Single-port AP, so the port is a VLAN **trunk** carrying management + both SSIDs.
|
Single-port AP, so the port is a VLAN **trunk** carrying management + both SSIDs.
|
||||||
@@ -99,7 +102,7 @@ zone with `input REJECT` (and `wave` has no IP there) — **no management via th
|
|||||||
`wave` hangs off **brian** (UniFi). Its port is a **trunk**: tagged VLAN **110/120** (`lo` + guest),
|
`wave` hangs off **brian** (UniFi). Its port is a **trunk**: tagged VLAN **110/120** (`lo` + guest),
|
||||||
and **native/untagged = core** (the fabric's management VLAN, carrying `wave-core`). VLAN 100 (`hi`)
|
and **native/untagged = core** (the fabric's management VLAN, carrying `wave-core`). VLAN 100 (`hi`)
|
||||||
is **not** needed here — `wave` isn't on `hi` (see Management addressing). Configure via the UniFi
|
is **not** needed here — `wave` isn't on `hi` (see Management addressing). Configure via the UniFi
|
||||||
controller (brian has no CLI); see `home-switches.md`.
|
controller (brian has no CLI); see [switches.md](switches.md).
|
||||||
|
|
||||||
### Flashing OpenWrt (Cudy AX3000 / `cudy_ap3000-v1`)
|
### Flashing OpenWrt (Cudy AX3000 / `cudy_ap3000-v1`)
|
||||||
|
|
||||||
@@ -110,6 +113,7 @@ Hardware: MT7981B, 512 MB RAM, 256 MB SPI-NAND, 1× 2.5 GbE (RTL8221B), 2×2 WiF
|
|||||||
|
|
||||||
OpenWrt can't be flashed directly over stock. Two-stage, via a Cudy **transition** firmware (Cudy
|
OpenWrt can't be flashed directly over stock. Two-stage, via a Cudy **transition** firmware (Cudy
|
||||||
OpenWrt download page / `support@cudy.com`; `warnning.txt` in that bundle has the steps):
|
OpenWrt download page / `support@cudy.com`; `warnning.txt` in that bundle has the steps):
|
||||||
|
|
||||||
1. Stock Cudy UI: update to **≥ 2.4.7** (adds TFTP `recovery.bin` recovery), then flash the Cudy
|
1. Stock Cudy UI: update to **≥ 2.4.7** (adds TFTP `recovery.bin` recovery), then flash the Cudy
|
||||||
**intermediate** firmware (`cudy_ap3000-v1-sysupgrade_*.bin`), "keep settings" **unchecked**.
|
**intermediate** firmware (`cudy_ap3000-v1-sysupgrade_*.bin`), "keep settings" **unchecked**.
|
||||||
It reboots into an OpenWrt-based build at `192.168.1.1` (SSH `root`, empty password).
|
It reboots into an OpenWrt-based build at `192.168.1.1` (SSH `root`, empty password).
|
||||||
@@ -122,11 +126,13 @@ stock-side flashing is done from a browser, not headless.
|
|||||||
|
|
||||||
### On-device config notes
|
### On-device config notes
|
||||||
|
|
||||||
- Package manager is **`apk`** (not `opkg`). WiFi runs **`wpad-mbedtls`** (full — swapped from the
|
#### Wireless packages
|
||||||
default `wpad-basic-mbedtls`, which lacks 802.11v). **802.11k + 802.11v** (`ieee80211k` +
|
|
||||||
`bss_transition`) are enabled on all SSIDs. ⚠️ Swapping wpad **live** leaves the mac80211 vifs
|
The package manager is `apk`, not `opkg`. Wi-Fi uses the full `wpad-mbedtls` package so all SSIDs
|
||||||
stuck in a start→teardown loop (`nl80211 ... No such device`); a `wifi reload`/`network restart`
|
can enable 802.11k/v (`ieee80211k` + `bss_transition`). Replacing `wpad-basic-mbedtls` live leaves
|
||||||
won't recover it — **reboot** after `apk add wpad-mbedtls`.
|
the mac80211 interfaces in a start/teardown loop that reloads cannot recover; reboot after
|
||||||
|
`apk add wpad-mbedtls`.
|
||||||
|
|
||||||
- Radios: `radio0` = 2.4 GHz, `radio1` = 5 GHz (keyed by `band`, don't assume). 5 GHz is pinned to
|
- Radios: `radio0` = 2.4 GHz, `radio1` = 5 GHz (keyed by `band`, don't assume). 5 GHz is pinned to
|
||||||
**channel 36 / HE160** (any 160 MHz block in IE is DFS; ch36 has the shortest ~60 s CAC).
|
**channel 36 / HE160** (any 160 MHz block in IE is DFS; ch36 has the shortest ~60 s CAC).
|
||||||
- Bridge: `br-lan` with `vlan_filtering`, single port `eth0` — tagged `110/120`, untagged/PVID
|
- Bridge: `br-lan` with `vlan_filtering`, single port `eth0` — tagged `110/120`, untagged/PVID
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# castle
|
||||||
|
|
||||||
|
The home workstation / gaming desktop. Diskless-style: it netboots from `river` and keeps its
|
||||||
|
root storage on NVMe-oF volumes from `cellar`.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/home/castle/`](../../../nixos/boxes/home/castle) (`default.nix`)
|
||||||
|
- **Host:** physical
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### Desktop
|
||||||
|
|
||||||
|
The AMD desktop runs the GUI stack (`my.gui.enable`, Sway/Wayland via home-manager), low-latency
|
||||||
|
PipeWire, Bluetooth and Thunderbolt. Local `libvirtd`/`virt-manager` and the IOMMU are enabled, but
|
||||||
|
the box has no VFIO or GPU-passthrough configuration.
|
||||||
|
|
||||||
|
### Netboot
|
||||||
|
|
||||||
|
With `my.netboot.client.enable`, the firmware iPXE-boots from the 2.5G NIC. Kea matches
|
||||||
|
`et2.5g`'s MAC and directs it to `boot.h.nul.ie` on [`river`](river.md).
|
||||||
|
|
||||||
|
### NVMe-oF root
|
||||||
|
|
||||||
|
`/nix`, `/persist` and `/home` are `/dev/nvmeof/*` LVs in `cellar`'s
|
||||||
|
`nqn.2016-06.io.spdk:castle` namespace (`my.nvme.boot`, RDMA). The initrd brings
|
||||||
|
up `et100g`/`lan-hi` with `roceBootModules`; the running network keeps
|
||||||
|
`KeepConfiguration=static` so networkd does not drop the storage address. The root filesystem is a
|
||||||
|
size-limited tmpfs (`my.tmproot`).
|
||||||
|
|
||||||
|
### Other configuration
|
||||||
|
|
||||||
|
Both firewalls are disabled on this trusted `hi` desktop. Other settings include `binfmt`
|
||||||
|
emulation for `aarch64-linux`/`armv7l-linux`, `recursive-nix`, Wireshark and `rdma-core`/`qperf`;
|
||||||
|
a `drm-amd-display` flicker patch remains commented out.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `castle`).
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
| Component | Inventory |
|
||||||
|
|---|---|
|
||||||
|
| Platform | ASUS ProArt X670E-CREATOR WIFI |
|
||||||
|
| CPU | AMD Ryzen 9 7950X (16 cores / 32 threads) |
|
||||||
|
| Memory | 64 GiB |
|
||||||
|
| Graphics | Integrated AMD Radeon graphics |
|
||||||
|
| Network | Mellanox ConnectX-4 100G, Aquantia AQC113CS 10G, Intel I225-V 2.5G and MediaTek MT7922 Wi-Fi 6E controllers |
|
||||||
|
| System storage | No local root disk; the box netboots and uses the SPDK NVMe-oF namespace exported by `cellar` |
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
- `et100g` (100G, MTU 9000) carries `lan-hi` (the `hi` assignment, also pinned by a kea
|
||||||
|
reservation on its MAC) and `lan-lo`.
|
||||||
|
- `lan-lo` is a secondary network attachment: DHCPv4 with `UseGateway`/`UseDNS` off and RAs
|
||||||
|
accepted with gateway/DNS use off — present for reaching `lo` devices, never a default route.
|
||||||
|
- `et2.5g` (netboot) and `et10g` are renamed but carry no network config.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/home/castle/default.nix`](../../../nixos/boxes/home/castle/default.nix) — box
|
||||||
|
config: netboot/NVMe-oF boot, 100G networking, GUI/audio, virtualisation.
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# cellar
|
||||||
|
|
||||||
|
The home storage target. A VM on `palace` that drives three passed-through NVMe disks with SPDK
|
||||||
|
and exports them over NVMe-oF/RDMA — `river`, `sfh` and `castle` all run their root storage off
|
||||||
|
it.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/home/palace/vms/cellar/`](../../../nixos/boxes/home/palace/vms/cellar)
|
||||||
|
(`default.nix`, `spdk.nix`)
|
||||||
|
- **Host:** VM on `palace`
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- Runs an **SPDK userspace target** (`my.spdk`, [`spdk.nix`](../../../nixos/boxes/home/palace/vms/cellar/spdk.nix)):
|
||||||
|
the kernel `nvme` driver is blacklisted so SPDK can claim the three NVMe controllers directly
|
||||||
|
(host BDFs `41:00.0`–`43:00.0`, attached in the guest as `02:00.0`–`04:00.0`).
|
||||||
|
- Builds a **RAID-0** (`NVMeRaid`) across the three drives and exports one
|
||||||
|
partition per consumer as an **NVMe-oF subsystem over RDMA** (port 4420) on the `hi` network:
|
||||||
|
|
||||||
|
| Bdev | NQN | Consumer |
|
||||||
|
|---|---|---|
|
||||||
|
| `NVMeRaidp1` | `nqn.2016-06.io.spdk:river` | [`river`](river.md) |
|
||||||
|
| `NVMeRaidp2` | `nqn.2016-06.io.spdk:castle` | [`castle`](castle.md) |
|
||||||
|
| `NVMeRaidp3` | `nqn.2016-06.io.spdk:sfh` | [`sfh`](sfh/README.md) |
|
||||||
|
|
||||||
|
Each subsystem is pinned to its consumer's `hostnqn` (the `my.nvme.uuid` on the client side).
|
||||||
|
- `spdk-tgt` is ordered after `lan-hi` is online; the RDMA listener binds the `hi` assignment on
|
||||||
|
port 4420. The VM itself is pinned to NUMA node 1 on `palace` and gets SR-IOV VF 0.
|
||||||
|
- `netdata` (port 19999 allowed in the firewall) and `fstrim`.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `cellar`).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The `ublk_*` calls in `my.spdk.debugCommands` are only a debugging aid — they create a local
|
||||||
|
ublk device so the RAID can be mounted and inspected on `cellar` itself. Client exports are the
|
||||||
|
`nvmf` subsystems above.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/home/palace/vms/cellar/default.nix`](../../../nixos/boxes/home/palace/vms/cellar/default.nix) —
|
||||||
|
box config (assignment, networking, netdata).
|
||||||
|
- [`nixos/boxes/home/palace/vms/cellar/spdk.nix`](../../../nixos/boxes/home/palace/vms/cellar/spdk.nix) —
|
||||||
|
SPDK target: RAID-0, NVMe-oF/RDMA subsystems.
|
||||||
|
- [`nixos/boxes/home/palace/vms/default.nix`](../../../nixos/boxes/home/palace/vms/default.nix) —
|
||||||
|
the VM definition on `palace` (VF 0, NVMe passthrough, NUMA pinning).
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# palace
|
||||||
|
|
||||||
|
The physical VM host for the home site. Runs the `river`, `cellar` and `sfh` VMs and feeds them
|
||||||
|
SR-IOV VFs, PCI NVMe drives and LVM disks.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/home/palace/default.nix`](../../../nixos/boxes/home/palace/default.nix)
|
||||||
|
(VM definitions in [`palace/vms/default.nix`](../../../nixos/boxes/home/palace/vms/default.nix))
|
||||||
|
- **Host:** physical
|
||||||
|
- **nixpkgs:** `mine-stable`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- Home hypervisor: VMs are declared in `my.vms.instances`
|
||||||
|
([`palace/vms/default.nix`](../../../nixos/boxes/home/palace/vms/default.nix)); disks are LVs in
|
||||||
|
the `main` thin pool (`services.lvm.boot.thin.enable`).
|
||||||
|
- AMD box (`kvm-amd`, `amd_iommu=on`, microcode updates); the kernel is built with
|
||||||
|
`ACPI_APEI_PCIEAER`/`PCIEAER` for the PCIe passthrough work below.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `palace`).
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
| Component | Inventory |
|
||||||
|
|---|---|
|
||||||
|
| Platform | Gigabyte X399 DESIGNARE EX |
|
||||||
|
| CPU | AMD Ryzen Threadripper 1950X (16 cores / 32 threads) |
|
||||||
|
| Memory | 128 GiB |
|
||||||
|
| Host storage | 500 GB Samsung SSD 860 EVO containing the EFI partition and the `main` LVM thin pool |
|
||||||
|
| Bulk storage | Three 8 TB Seagate IronWolf disks in the `hdds` VG, providing the RAID-backed `hdd-storage` and `frigate` LVs |
|
||||||
|
| NVMe storage | Three 2 TB Samsung NVMe devices passed through to `cellar`; SPDK combines them as the `NVMeRaid` RAID 0 device |
|
||||||
|
| Network / graphics | Mellanox ConnectX-4 100G adapter with four SR-IOV VFs, two Intel I211 Gigabit Ethernet controllers, and an AMD Radeon RX 550/560-family GPU |
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
100G `et100g` (mlx5, MTU 9000) uplinks to the `dave` switch and carries `lan-hi` (VLAN 100, the
|
||||||
|
`hi` assignment). A udev rule creates four SR-IOV VFs on the PF:
|
||||||
|
|
||||||
|
| VF | Consumer | VLAN handling |
|
||||||
|
|---|---|---|
|
||||||
|
| 0 | `cellar` | `hi` |
|
||||||
|
| 1 | `river` | untagged VF; `river` tags its LAN and WAN VLANs |
|
||||||
|
| 2 | `sfh` | `hi` |
|
||||||
|
| 3 | `sfh` container MACVLAN parent | `hi` |
|
||||||
|
|
||||||
|
- `lan-core` is a bridge with the `core` assignment (no gateway); the 1G
|
||||||
|
`lan-core-phy` and the `lan-lo-phy` VLAN ride on it. `lan-lo` is a second, L3-less bridge used
|
||||||
|
for VM netboot and `lo` clients.
|
||||||
|
- The 1G `et1g0` (igb) is exported to `river` as a passthru-mode macvtap (`vm-et1g0`) — river sees
|
||||||
|
it as `wan-old`.
|
||||||
|
|
||||||
|
## VMs
|
||||||
|
|
||||||
|
| VM | vCPUs | RAM | Passthrough | Notes |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `cellar` | 8c × 2t | 16 GiB | VF 0 (`44:00.1`); NVMe `41:00.0`–`43:00.0` | pinned to NUMA node 1; split IRQ chip + vIOMMU |
|
||||||
|
| `river` | 3c × 2t | 4 GiB | VF 1 (`44:00.2`); macvtap `vm-et1g0` | only an ESP local disk (plus an installer ISO) — root is NVMe-oF from `cellar` |
|
||||||
|
| `sfh` | 8c × 2t | 32 GiB | VF 2 (`44:00.3`), VF 3 (`44:00.4`); two USB host ports | no boot disk — netboots; gets the `hdds/frigate` LV |
|
||||||
|
|
||||||
|
Boot ordering is enforced with systemd dependencies: `vm@river` waits for `cellar`'s SSH port (and
|
||||||
|
for the `vm-et1g0` device), and `vm@sfh` waits for `river` — storage first, then the router, then
|
||||||
|
everything that boots off both.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/home/palace/default.nix`](../../../nixos/boxes/home/palace/default.nix) — host
|
||||||
|
hardware, networkd (links/bridges/SR-IOV), LVM.
|
||||||
|
- [`nixos/boxes/home/palace/vms/default.nix`](../../../nixos/boxes/home/palace/vms/default.nix) —
|
||||||
|
VM instances and boot ordering.
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# river
|
||||||
|
|
||||||
|
A home router VM on `palace` with a Digiweb PPPoE WAN on the ISP's VLAN 10. It forms the redundant
|
||||||
|
router pair with [`stream`](stream.md).
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/home/palace/vms/river.nix`](../../../nixos/boxes/home/palace/vms/river.nix)
|
||||||
|
(shared router config: [`routing-common`](../../../nixos/boxes/home/routing-common), index 0)
|
||||||
|
- **Host:** VM on `palace`
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
At `routing-common` index 0, `river` normally holds the primary position in the router pair.
|
||||||
|
Pair-wide addressing, VIP, DHCP/DNS and failover behavior is documented in the
|
||||||
|
[`home` networking overview](../../networking.md#home) and [Router HA](../../networking.md#router-ha).
|
||||||
|
This page covers `river`'s Digiweb WAN, VM platform, storage and netboot duties.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `river`).
|
||||||
|
|
||||||
|
## WAN (Digiweb PPPoE)
|
||||||
|
|
||||||
|
### Link and addressing
|
||||||
|
|
||||||
|
`services.pppd` peer `digiweb` attaches directly to `wan-pon-isp`, the raw ISP VLAN 10
|
||||||
|
(`vlans.pon-isp`) trunked untranslated from the ONT through `brian` and `dave`; see
|
||||||
|
[switches.md](switches.md). The netdev has no L3 configuration and uses `MTUBytes=1508`, leaving a
|
||||||
|
clean `mtu`/`mru 1500` after PPPoE overhead.
|
||||||
|
|
||||||
|
The static `84.203.124.128` address is requested through IPCP. The provider-wide credentials are
|
||||||
|
deliberately not secret; the peer persists indefinitely with LCP echo monitoring and ignores
|
||||||
|
Digiweb DNS in favor of the local recursor.
|
||||||
|
|
||||||
|
### Management subnet
|
||||||
|
|
||||||
|
`wan-pon-ont` (VLAN 140, PVID'd at `brian`) holds `192.168.100.100/24`, reaching the ONT UI at
|
||||||
|
`192.168.100.1`. The `.100` address follows `stream`'s modem-management convention.
|
||||||
|
|
||||||
|
### WAN readiness
|
||||||
|
|
||||||
|
The `pppd` hooks drive this shared gate with `DefaultDependencies=false`: `ip-up` installs the
|
||||||
|
link-scoped default route and starts the target, while `ip-down` stops it and removes the route.
|
||||||
|
`ipsec` and `ipv6-clear-default-route` attach through `wantedBy` + `partOf`, so they reload after
|
||||||
|
every WAN flap.
|
||||||
|
|
||||||
|
### Traffic shaping
|
||||||
|
|
||||||
|
The shared `wan-ifb` ingress-shaping pieces are inert here. CAKE is specific to `stream`, and
|
||||||
|
`networkd-dispatcher` is `mkForce false` pending scheduling tests.
|
||||||
|
|
||||||
|
## Platform
|
||||||
|
|
||||||
|
### Virtual machine and network attachment
|
||||||
|
|
||||||
|
The 100G `lan` NIC is VF 1 of `palace`'s `et100g` (MTU 9000), with every router VLAN tagged on top
|
||||||
|
as `55-lan`. A macvtap of `palace`'s 1G `et1g0` remains as the old `wan-old` path without L3
|
||||||
|
configuration. Deployments use the `hi` assignment.
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
The VM's local disk holds only an ESP; `/nix` and `/persist` are LVs on `cellar`'s
|
||||||
|
`nqn.2016-06.io.spdk:river` namespace over RDMA. The initrd brings up `lan-hi`
|
||||||
|
with `roceBootModules`, and `KeepConfiguration=static` prevents networkd from dropping the address
|
||||||
|
during reconfiguration. An installer ISO remains attached.
|
||||||
|
|
||||||
|
## Netboot
|
||||||
|
|
||||||
|
`my.netboot.server` serves iPXE/TFTP for `sfh` and `castle` at `boot.h.nul.ie` from the `lo`
|
||||||
|
assignment, restricted to the `hi` and `lo` prefixes.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/home/palace/vms/river.nix`](../../../nixos/boxes/home/palace/vms/river.nix) — box
|
||||||
|
config: pppd, WAN VLANs, `wan-online.target` hooks, netboot server, NVMe-oF boot.
|
||||||
|
- [`nixos/boxes/home/routing-common/default.nix`](../../../nixos/boxes/home/routing-common/default.nix) —
|
||||||
|
shared router definition (assignments, firewall/NAT, `as211024`).
|
||||||
|
- [`nixos/boxes/home/palace/vms/default.nix`](../../../nixos/boxes/home/palace/vms/default.nix) —
|
||||||
|
the VM definition on `palace` (VF 1, macvtap, ESP disk).
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# sfh
|
||||||
|
|
||||||
|
"Services for home" — the NixOS container host for the home site. A VM on `palace` that netboots
|
||||||
|
from `river` and runs its root off NVMe-oF from `cellar`.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/home/palace/vms/sfh/`](../../../../nixos/boxes/home/palace/vms/sfh)
|
||||||
|
(`default.nix`, `containers/`)
|
||||||
|
- **Host:** VM on `palace`
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- Runs the home NixOS containers via `my.containers.instances` (systemd-nspawn); each container is
|
||||||
|
its own `nixos.systems.*` entry rendered through `my.asContainer`.
|
||||||
|
- **Netboot client** (`my.netboot.client.enable`): the VM has no boot disk — its `netboot` NIC
|
||||||
|
on palace's `lan-lo` bridge is matched by a kea client-class on [`river`](../river.md) and
|
||||||
|
iPXE-boots from `boot.h.nul.ie`.
|
||||||
|
- **Root on NVMe-oF**: `my.nvme.boot` connects to `nqn.2016-06.io.spdk:sfh`
|
||||||
|
([`cellar`](../cellar.md), RDMA) from the initrd (`lan-hi` up + `roceBootModules`); `/nix` and
|
||||||
|
`/persist` are LVs on that volume. `KeepConfiguration=static` on `lan-hi` protects the
|
||||||
|
NVMe-oF address from networkd reconfigures.
|
||||||
|
- **Frigate footage disk**: palace passes the `hdds/frigate` LVM LV through as a virtio disk;
|
||||||
|
sfh mounts it at `/mnt/frigate` (by label) and bind-mounts it into the `hass` container at
|
||||||
|
`/var/lib/frigate`.
|
||||||
|
- USB: two host ports are passed to the VM (qemu flags) for the Zigbee coordinator and webcam used
|
||||||
|
by `hass`; the nspawn unit gets `DeviceAllow` for `char-ttyUSB` and `char-video4linux`.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../networking.md#box-assignments) table (this box: `sfh`).
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
Four NICs, all MTU 9000 where jumbo-capable:
|
||||||
|
|
||||||
|
- `lan-hi` — SR-IOV VF 2, the box's own `hi` assignment.
|
||||||
|
- `lan-hi-ctrs` — SR-IOV VF 3, no L3: the MACVLAN parent for the containers' `hi` interfaces
|
||||||
|
(`host0` inside each container).
|
||||||
|
- `lan-core-ctrs` / `lan-lo-ctrs` — virtio NICs (bridged to palace's `lan-core` / `lan-lo`), no
|
||||||
|
L3: MACVLAN parents for containers that need a `core` or `lo` interface.
|
||||||
|
|
||||||
|
The per-container MACVLAN wiring lives in `systemd.nspawn.*.networkConfig` in
|
||||||
|
[`sfh/default.nix`](../../../../nixos/boxes/home/palace/vms/sfh/default.nix).
|
||||||
|
|
||||||
|
## Containers
|
||||||
|
|
||||||
|
| Container | Role |
|
||||||
|
|---|---|
|
||||||
|
| [`hass`](containers/hass.md) | Home Assistant + Frigate + MQTT |
|
||||||
|
| [`unifi`](containers/unifi.md) | UniFi controller |
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/home/palace/vms/sfh/default.nix`](../../../../nixos/boxes/home/palace/vms/sfh/default.nix) —
|
||||||
|
box config: netboot/NVMe-oF boot, container instances, MACVLAN plumbing, Frigate disk.
|
||||||
|
- [`nixos/boxes/home/palace/vms/sfh/containers/`](../../../../nixos/boxes/home/palace/vms/sfh/containers) —
|
||||||
|
the container system definitions.
|
||||||
|
- [`nixos/boxes/home/palace/vms/default.nix`](../../../../nixos/boxes/home/palace/vms/default.nix) —
|
||||||
|
the VM definition on `palace` (VFs, USB passthrough, netboot NIC, `hdds/frigate` disk).
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# hass
|
||||||
|
|
||||||
|
Home automation container: Home Assistant plus its supporting services (MQTT, camera restreaming,
|
||||||
|
Frigate NVR), running on [`sfh`](../README.md).
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/home/palace/vms/sfh/containers/hass.nix`](../../../../../nixos/boxes/home/palace/vms/sfh/containers/hass.nix)
|
||||||
|
- **Host:** NixOS container on `sfh`
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
### Home Assistant
|
||||||
|
|
||||||
|
`services.home-assistant` uses declarative configuration (`configWritable = false`). It enables
|
||||||
|
the `esphome`, `zha`, `denonavr`, `webostv`, `androidtv_remote`, `heos`, `mqtt`, `wled`, `met` and
|
||||||
|
`google_translate` components, plus custom `alarmo`, `frigate`, `west_wood_club` and Irish Rail
|
||||||
|
integrations. A `hass-cli` wrapper uses a token from `my.secrets` to reach the local server.
|
||||||
|
|
||||||
|
- **mosquitto** — MQTT broker (anonymous local listener; port 1883 allowed, alongside HTTP).
|
||||||
|
- **go2rtc** — restreams the Reolink living-room camera (RTSP from `reolink-living-room`, on the
|
||||||
|
`lo` network) and the office USB webcam (`/dev/video0` via ffmpeg).
|
||||||
|
- **Frigate** (`services.frigate`, `frigate.h.nul.ie` — the `frigate` alt name on the `hi`
|
||||||
|
assignment) — records both restreamed cameras with a short retention policy; detection is
|
||||||
|
disabled.
|
||||||
|
- External access is via `https://hass.nul.ie` through the `middleman` reverse proxy
|
||||||
|
(`trusted_proxies`); internally it's `hass-ctr.h.nul.ie`.
|
||||||
|
- Not a deploy-rs target (`my.deploy.enable = false`) — it's rendered via `my.asContainer` and
|
||||||
|
started by `sfh`'s `my.containers.instances`.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `hass`).
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
Frigate footage lives on a **separate HDD LV**: `palace` passes the `hdds/frigate` LVM LV to the
|
||||||
|
`sfh` VM, sfh mounts it at `/mnt/frigate`, and the container bind-mounts it at `/var/lib/frigate`
|
||||||
|
(read-write). This keeps recording churn off the NVMe-oF root.
|
||||||
|
|
||||||
|
## Devices
|
||||||
|
|
||||||
|
Passed through from `sfh` (USB host ports on `palace`):
|
||||||
|
|
||||||
|
- Nabu Casa Connect ZBT-1 Zigbee coordinator → `/dev/ttyUSB0` (used by `zha`).
|
||||||
|
- USB webcam → `/dev/video0` (go2rtc's `webcam_office` stream).
|
||||||
|
- The matching raw USB device node allowed through from `sfh`.
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
MACVLAN interfaces created from `sfh`'s container NICs: `host0` on `lan-hi-ctrs` (the `hi` assignment,
|
||||||
|
alt name `frigate`, default gateway via the VIP) and `lan-lo` on `lan-lo-ctrs` (the `lo`
|
||||||
|
assignment, no gateway) — the `lo` interface reaches the IoT devices (the Reolink camera lives there).
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/home/palace/vms/sfh/containers/hass.nix`](../../../../../nixos/boxes/home/palace/vms/sfh/containers/hass.nix) —
|
||||||
|
container system: Home Assistant, Frigate, mosquitto, go2rtc.
|
||||||
|
- [`nixos/boxes/home/palace/vms/sfh/default.nix`](../../../../../nixos/boxes/home/palace/vms/sfh/default.nix) —
|
||||||
|
the `sfh` side: bind mounts, MACVLAN wiring, `DeviceAllow`.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# unifi
|
||||||
|
|
||||||
|
The UniFi network controller, running as a container on [`sfh`](../README.md). It manages the
|
||||||
|
home UniFi switch `brian` (see [switches.md](../../switches.md)).
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/home/palace/vms/sfh/containers/unifi.nix`](../../../../../nixos/boxes/home/palace/vms/sfh/containers/unifi.nix)
|
||||||
|
- **Host:** NixOS container on `sfh`
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
- **UniFi controller** (`services.unifi`, `pkgs.unifi` on `mongodb-7_0`, firewall open; TCP 8443
|
||||||
|
allowed).
|
||||||
|
- Not a deploy-rs target (`my.deploy.enable = false`) — it's rendered via `my.asContainer` and
|
||||||
|
started by `sfh`'s `my.containers.instances`.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../../../networking.md#box-assignments) table (this box: `unifi`).
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
**Currently enabled.** The container spent a while disabled — its import was commented out of
|
||||||
|
[`containers/default.nix`](../../../../../nixos/boxes/home/palace/vms/sfh/containers/default.nix)
|
||||||
|
while there was no UniFi gear to manage — and was re-enabled when the UniFi switch `brian` was
|
||||||
|
added, gaining a `core` interface (`unifi-ctr-core`) at the same time so it can reach the switch on its
|
||||||
|
management network. It is imported, listed in `sfh`'s `my.containers.instances`, and
|
||||||
|
`services.unifi.enable = true`.
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
Two MACVLAN interfaces come from `sfh`'s container NICs: `host0` on `lan-hi-ctrs` carries the `hi`
|
||||||
|
assignment (`unifi-ctr`, default gateway via the VIP), while `lan-core` on `lan-core-ctrs` carries
|
||||||
|
the gatewayless `core` assignment (`unifi-ctr-core`). The `core` interface is how the controller
|
||||||
|
talks to `brian` and the other switches on their management network.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/home/palace/vms/sfh/containers/unifi.nix`](../../../../../nixos/boxes/home/palace/vms/sfh/containers/unifi.nix) —
|
||||||
|
container system: UniFi service, assignments.
|
||||||
|
- [`nixos/boxes/home/palace/vms/sfh/default.nix`](../../../../../nixos/boxes/home/palace/vms/sfh/default.nix) —
|
||||||
|
the `sfh` side: container instance, MACVLAN wiring.
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# stream
|
||||||
|
|
||||||
|
A physical Intel home router with a DHCP WAN from the Virgin Media cable modem. It forms the
|
||||||
|
redundant router pair with [`river`](river.md) and is dual-homed to both switches.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/home/stream.nix`](../../../nixos/boxes/home/stream.nix) (shared router
|
||||||
|
config: [`routing-common`](../../../nixos/boxes/home/routing-common), index 1)
|
||||||
|
- **Host:** physical
|
||||||
|
- **nixpkgs:** `mine`
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
At `routing-common` index 1, `stream` normally holds the secondary position in the router pair.
|
||||||
|
Pair-wide addressing, VIP, DHCP/DNS and failover behavior is documented in the
|
||||||
|
[`home` networking overview](../../networking.md#home) and [Router HA](../../networking.md#router-ha).
|
||||||
|
This page covers `stream`'s Virgin Media WAN, physical platform and redundant switch attachment.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
See the consolidated [network assignments](../../networking.md#box-assignments) table (this box: `stream`).
|
||||||
|
|
||||||
|
## WAN (Virgin Media DHCP)
|
||||||
|
|
||||||
|
### Link and addressing
|
||||||
|
|
||||||
|
`wan` is a renamed igc NIC (`00:f0:cb:ee:ca:dd`) towards the cable modem. The modem segment is
|
||||||
|
switch VLAN 130; `jim` handles the tag, so the box interface is untagged. See
|
||||||
|
[switches.md](switches.md) for the fabric side.
|
||||||
|
|
||||||
|
`DHCP=ipv4` pulls the public lease. `dhcpV4Config.UseDNS=false` points resolution at the local
|
||||||
|
recursor, and `IPv6AcceptRA=false` because public IPv6 arrives over the tunnel rather than this WAN.
|
||||||
|
|
||||||
|
### Management subnet
|
||||||
|
|
||||||
|
The static `192.168.0.100/24` address (host `.100` of `prefixes.modem.v4`) sits on `wan` without a
|
||||||
|
gateway, keeping the modem UI reachable alongside the DHCP lease.
|
||||||
|
|
||||||
|
### WAN readiness
|
||||||
|
|
||||||
|
`wan-wait-online.service` polls until the DHCP default route exists, then satisfies
|
||||||
|
`wan-online.target`. The route is the gate because the permanent modem address would make
|
||||||
|
networkd's wait-online report success before the public lease arrives, allowing `ipsec` to start
|
||||||
|
without its public `left=` address.
|
||||||
|
|
||||||
|
### Traffic shaping
|
||||||
|
|
||||||
|
Egress is shaped at the `wan` root qdisc. A `routing-common` `networkd-dispatcher` rule redirects
|
||||||
|
ingress through `tc`/`mirred` into `wan-ifb`; each direction has its own configured bandwidth and
|
||||||
|
uses the DOCSIS overhead preset.
|
||||||
|
|
||||||
|
### Per-box `routing-common` options
|
||||||
|
|
||||||
|
The modem's management subnet shares the `wan` interface, which `routing-common` itself knows
|
||||||
|
nothing about — it declares two per-box options
|
||||||
|
([`routing-common/default.nix`](../../../nixos/boxes/home/routing-common/default.nix)) that this
|
||||||
|
box sets:
|
||||||
|
|
||||||
|
- `my.homeRouter.dns.wanSkipBroadcasts` — skip the modem subnet's broadcast address when
|
||||||
|
auto-selecting the router's own `wan` A record for the zone's LUA record.
|
||||||
|
- `my.homeRouter.firewall.untrustedRejectV4` — reject untrusted clients from
|
||||||
|
reaching the modem subnet (needed only because it shares `wan`; WAN egress is otherwise
|
||||||
|
accepted).
|
||||||
|
|
||||||
|
## Platform
|
||||||
|
|
||||||
|
### Hardware
|
||||||
|
|
||||||
|
| Component | Inventory |
|
||||||
|
|---|---|
|
||||||
|
| Platform | BROUNION R86S |
|
||||||
|
| CPU | Intel Celeron N5105 (4 cores / 4 threads) |
|
||||||
|
| Memory | 16 GiB |
|
||||||
|
| Storage | 512 GB Samsung SSD 970 PRO NVMe containing `/boot`, `/nix` and `/persist`; integrated 128 GB eMMC is present but is not used by the declared filesystems |
|
||||||
|
| Network | Three Intel `igc` interfaces and a dual-port Mellanox `mlx4_en` adapter; `wan`, `lan-jim` and `lan-dave` use three of these ports |
|
||||||
|
|
||||||
|
The platform configuration enables `kvm-intel`, `intel_iommu=on` and Intel microcode updates.
|
||||||
|
|
||||||
|
### Switching (RSTP)
|
||||||
|
|
||||||
|
`stream` is dual-homed to both switches: `lan-jim` (igc) and `lan-dave` (mlx4_en), both MTU 9000,
|
||||||
|
are enslaved to the `lan` bridge with `STP=true`. [`routing-common/mstpd.nix`](../../../nixos/boxes/home/routing-common/mstpd.nix)
|
||||||
|
runs a patched `mstpd` and forces RSTP on `lan` once it's routable, so exactly one uplink carries
|
||||||
|
traffic at a time. (The remaining NICs are renamed `et2`/`et5` and left unconfigured.)
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
`my.deploy.node.hostname` is currently commented out.
|
||||||
|
|
||||||
|
## Disabled printer services
|
||||||
|
|
||||||
|
`octoprint` and `mjpg-streamer` are defined but disabled (`enable = false`).
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/home/stream.nix`](../../../nixos/boxes/home/stream.nix) — box config: DHCP WAN,
|
||||||
|
modem management, CAKE, `wan-online.target` gate, STP bridge.
|
||||||
|
- [`nixos/boxes/home/routing-common/default.nix`](../../../nixos/boxes/home/routing-common/default.nix) —
|
||||||
|
shared router definition (index 1).
|
||||||
|
- [`nixos/boxes/home/routing-common/mstpd.nix`](../../../nixos/boxes/home/routing-common/mstpd.nix) —
|
||||||
|
RSTP on the `lan` bridge.
|
||||||
@@ -9,7 +9,9 @@ In short: the Digiweb ISP VLAN (10) is trunked straight through to river (which
|
|||||||
and the ONT's untagged management is PVID'd onto VLAN 140 at brian, its edge switch. VLAN 10 is
|
and the ONT's untagged management is PVID'd onto VLAN 140 at brian, its edge switch. VLAN 10 is
|
||||||
carried untranslated because a single ONT makes it unique on the fabric — see
|
carried untranslated because a single ONT makes it unique on the fabric — see
|
||||||
[the WAN path](#the-digiweb-wan-path-trunked-vlan-10--pvid-140) and
|
[the WAN path](#the-digiweb-wan-path-trunked-vlan-10--pvid-140) and
|
||||||
[why not translation](#why-not-translation-for-one-ont).
|
[why not translation](#why-not-translation-for-one-ont). The router side lives in
|
||||||
|
[river.md](river.md); the logical network map in [networking.md](../../networking.md). The Wi-Fi
|
||||||
|
APs that hang off these switches are in [aps.md](aps.md).
|
||||||
|
|
||||||
## The switches
|
## The switches
|
||||||
|
|
||||||
@@ -18,8 +20,8 @@ carried untranslated because a single ONT makes it unique on the fabric — see
|
|||||||
| Identity | `jim-sw` | `dave-sw` | (UniFi) |
|
| Identity | `jim-sw` | `dave-sw` | (UniFi) |
|
||||||
| Model | CRS326-24G-2S+ | CRS504-4XQ | Ubiquiti Switch Pro XG 8 PoE |
|
| Model | CRS326-24G-2S+ | CRS504-4XQ | Ubiquiti Switch Pro XG 8 PoE |
|
||||||
| Switch chip | Marvell 98DX3236 | Marvell 98DX4310 (+ Atheros 8227 for the 1G mgmt port) | — |
|
| Switch chip | Marvell 98DX3236 | Marvell 98DX4310 (+ Atheros 8227 for the 1G mgmt port) | — |
|
||||||
| OS | RouterOS 7.18 | RouterOS 7.18 | UniFi |
|
| OS | RouterOS | RouterOS | UniFi |
|
||||||
| Ports | 24×1G + 2×SFP+ | 4×QSFP28 (100G, breakout-capable) + 1G mgmt | 8×10GBASE-T PoE + 2×25G SFP28 |
|
| Ports | 24×1G + 2×SFP+ | 4×QSFP28 (100G, breakout-capable) + 1G mgmt | 8×10GBASE-T PoE + 2×10G SFP+ |
|
||||||
| Bridge | `main`, `vlan-filtering=yes` | `main`, `vlan-filtering=yes` | UniFi VLAN profiles |
|
| Bridge | `main`, `vlan-filtering=yes` | `main`, `vlan-filtering=yes` | UniFi VLAN profiles |
|
||||||
|
|
||||||
jim and dave run a single hardware-offloaded bridge (`main`) with VLAN filtering. Access to the
|
jim and dave run a single hardware-offloaded bridge (`main`) with VLAN filtering. Access to the
|
||||||
@@ -29,52 +31,45 @@ chips); brian cannot rewrite tags, only trunk/PVID them.
|
|||||||
|
|
||||||
## Physical topology
|
## Physical topology
|
||||||
|
|
||||||
The ONT terminates on brian; jim's `wan-pon-in` (`sfp-sfpplus2`) is a spare SFP+ port.
|
The two WAN sources enter at the top: the Virgin Media modem lands on **jim** (VLAN 130), and the
|
||||||
|
Digiweb **ONT** lands on **brian**. Both `jim` and `brian` are edge switches that uplink down into
|
||||||
|
the **dave** core; the home boxes hang off dave's 100G ports, with backup links up to jim. jim's
|
||||||
|
`wan-pon-in` (`sfp-sfpplus2`) is a spare SFP+ port, unused today.
|
||||||
|
|
||||||
```
|
```
|
||||||
Virgin Media cable modem
|
Virgin Media cable modem Digiweb ONT
|
||||||
│ VLAN 130 (wan1 / wan2 / wan-in)
|
stream WAN, VLAN 130 river WAN, management + VLAN 10
|
||||||
┌───────────────┴──────────────────────────────────────┐
|
| |
|
||||||
│ jim CRS326-24G-2S+ (Marvell 98DX3236) │
|
jim brian
|
||||||
│ 1G edge: fort, pronter, laptop-dock, palace-kvm, │
|
| 10G trunk 802.3ad LAG |
|
||||||
│ ups, ether15-20, wan1/wan2/wan-in │
|
+--------------------+ +---------------+
|
||||||
│ wan-pon-in = sfp-sfpplus2 (spare SFP+) │
|
| |
|
||||||
└───────┬──────────────────────────────────────────────┘
|
+---+----------+---+
|
||||||
│ dave-uplink = sfp-sfpplus1 (10G trunk)
|
| dave |
|
||||||
│ also: palace, stream (1G secondaries),
|
+--------+---------+
|
||||||
│ castle (2.5G, normally down)
|
|
|
||||||
┌───────┴──────────────────────────────────────────────┐
|
+---------------------+---------------------+
|
||||||
│ dave CRS504-4XQ (Marvell 98DX4310) │
|
| | |
|
||||||
│ jim-downlink = qsfp28-3-1 │
|
palace (100G) castle (100G) stream
|
||||||
└──┬──────────────────┬───────────────────┬────────────┘
|
river VM NVMe-oF root second router
|
||||||
│ palace │ castle │ brian-downlink
|
|
||||||
│ = qsfp28-1-1 │ (100G) │ 802.3ad LAG
|
Backup links to jim (normally idle):
|
||||||
│ (100G) │ │ (brian1 + brian2)
|
* palace: 1G
|
||||||
│ … │
|
* stream: 1G; STP selects the active link
|
||||||
┌───────┴────────┐ ┌───────┴─────────────┐
|
* castle: 2.5G, normally down; no live failover
|
||||||
│ palace host │ │ brian │
|
|
||||||
│ └ river (VM) │ │ Switch Pro XG 8 PoE │
|
|
||||||
└────────────────┘ └───────┬─────────────┘
|
|
||||||
river WAN + LAN ride the 100G link │ hosts the ONT
|
|
||||||
│
|
|
||||||
┌───────┴─────────────┐
|
|
||||||
│ ONT (Digiweb) │ untagged mgmt
|
|
||||||
│ PPPoE via ONT │ 192.168.100.1
|
|
||||||
└─────────────────────┘ + VLAN 10; PVID 140
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- **river** runs as a VM on the **palace** host; its uplink is dave's 100G `palace` port. jim also
|
- **river** runs as a VM on the **palace** host; its uplink is dave's 100G `palace` port. jim also
|
||||||
has 1G `palace`/`stream` ports, but those are secondary links and do **not** carry the WAN.
|
has 1G `palace`/`stream` ports, but those are secondary links and do **not** carry the WAN.
|
||||||
- **stream** (the second router box) is dual-homed to both jim and dave (STP picks the active path).
|
- **stream** (the second router box) is dual-homed to both jim and dave (STP picks the active path).
|
||||||
- **castle** is dual-homed but **not** via STP: its primary uplink is dave's **100G** `castle` port
|
|
||||||
(`et100g`, active), and it has a secondary **2.5G** link to jim's `castle` edge port (`et2.5g`,
|
### Castle storage dependency
|
||||||
**normally down** — no live failover). ⚠️ **castle's root disk is NVMe-oF over the fabric** (via
|
|
||||||
`et100g`→dave), so rebooting **dave** — or downing castle's `et100g` — freezes castle mid-I/O.
|
`castle` is dual-homed without STP: its primary link is dave's 100G `castle` port (`et100g`), while
|
||||||
Do dave maintenance (upgrades/reboots) from a host that doesn't depend on dave for storage or
|
the 2.5G link to jim (`et2.5g`) is normally down and provides no live failover. Its root disk is
|
||||||
network, or with castle cleanly powered off; don't drive it from castle.
|
NVMe-oF over `et100g` and dave, so interrupting either freezes `castle` mid-I/O. Do dave maintenance
|
||||||
- **brian** is a Ubiquiti **Switch Pro XG 8 PoE** (8×10GBASE-T), downlinked from dave over an
|
from a box that does not depend on it, or power `castle` off cleanly first.
|
||||||
**802.3ad LAG** (`brian-downlink` = `brian1` + `brian2`, layer-2 hash). It hosts the ONT.
|
|
||||||
|
|
||||||
## VLANs
|
## VLANs
|
||||||
|
|
||||||
@@ -115,12 +110,13 @@ we just carry it end to end and let river run PPPoE directly on it:
|
|||||||
1500).
|
1500).
|
||||||
|
|
||||||
Net result: **river runs PPPoE single-tagged on VLAN 10 and holds a VLAN 140 address to reach the
|
Net result: **river runs PPPoE single-tagged on VLAN 10 and holds a VLAN 140 address to reach the
|
||||||
ONT.** See `nixos/boxes/home/palace/vms/river.nix` for the river side.
|
ONT.** See [`nixos/boxes/home/palace/vms/river.nix`](../../../nixos/boxes/home/palace/vms/river.nix)
|
||||||
|
for the river side.
|
||||||
|
|
||||||
```
|
```
|
||||||
ONT ──(untagged + VLAN10)── brian ──(VLAN140 + VLAN10)── dave ──(VLAN140 + VLAN10)── river
|
ONT -- untagged + VLAN 10 -- brian -- VLAN 140 + VLAN 10 -- dave -- palace -- river
|
||||||
ONT port │ PVID140 + tagged 10 │ plain bridging
|
|
|
||||||
└─ brian-downlink LAG ── dave ┘
|
+-- ONT port PVID 140; VLAN 10 remains tagged
|
||||||
```
|
```
|
||||||
|
|
||||||
### Why not translation (for one ONT)?
|
### Why not translation (for one ONT)?
|
||||||
@@ -183,8 +179,10 @@ translation there, feeding distinct fabric VLANs up to dave.
|
|||||||
## Accessing the switches
|
## Accessing the switches
|
||||||
|
|
||||||
The switches resolve by **short hostname** on the home network — the home routers serve their
|
The switches resolve by **short hostname** on the home network — the home routers serve their
|
||||||
records in the home zone (`nixos/boxes/home/routing-common/dns.nix`: `jim` → hi `.10`, `dave` → hi
|
records in the home zone
|
||||||
`.11`, `brian` → core `.13`). From a box on the home network just `ssh admin@jim` / `admin@dave`.
|
([`nixos/boxes/home/routing-common/dns.nix`](../../../nixos/boxes/home/routing-common/dns.nix):
|
||||||
|
`jim` → hi `.10`, `dave` → hi `.11`, `brian` → core `.13`). From a box on the home network just
|
||||||
|
`ssh admin@jim` / `admin@dave`.
|
||||||
|
|
||||||
**Key auth** for `admin` is installed on jim/dave (and the `vibe` AP) — `ssh -i ~/.ssh/id_rsa
|
**Key auth** for `admin` is installed on jim/dave (and the `vibe` AP) — `ssh -i ~/.ssh/id_rsa
|
||||||
admin@jim` works keyless (imported via `/user ssh-keys import`). Password `admin`/`admin` remains as
|
admin@jim` works keyless (imported via `/user ssh-keys import`). Password `admin`/`admin` remains as
|
||||||
+1
-1
@@ -318,7 +318,7 @@ rec {
|
|||||||
# the switches to river (PPPoE runs directly on it), and the switch at the ONT edge PVIDs the
|
# the switches to river (PPPoE runs directly on it), and the switch at the ONT edge PVIDs the
|
||||||
# untagged management port onto wan-pon-ont (140). wan-pon-isp (141) is reserved for the
|
# untagged management port onto wan-pon-ont (140). wan-pon-isp (141) is reserved for the
|
||||||
# future multi-ONT case, where per-port VLAN translation on a dedicated switch swaps each
|
# future multi-ONT case, where per-port VLAN translation on a dedicated switch swaps each
|
||||||
# ONT's VLAN 10 to a distinct fabric VLAN (see home-switches.md).
|
# ONT's VLAN 10 to a distinct fabric VLAN (see docs/sites/home/switches.md).
|
||||||
pon-isp = 10;
|
pon-isp = 10;
|
||||||
wan-pon-ont = 140;
|
wan-pon-ont = 140;
|
||||||
wan-pon-isp = 141;
|
wan-pon-isp = 141;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
# collision, so the switches simply trunk the ISP's VLAN 10 straight through to river (PPPoE
|
# collision, so the switches simply trunk the ISP's VLAN 10 straight through to river (PPPoE
|
||||||
# runs directly on it) and PVID the ONT's untagged management port onto wan-pon-ont (140).
|
# runs directly on it) and PVID the ONT's untagged management port onto wan-pon-ont (140).
|
||||||
# river takes .100 in the ONT's /24 (matching stream's modem-mgmt .100) to reach its web
|
# river takes .100 in the ONT's /24 (matching stream's modem-mgmt .100) to reach its web
|
||||||
# UI at 192.168.100.1. (See home-switches.md for the switch side and the multi-ONT plan.)
|
# UI at 192.168.100.1. (See docs/sites/home/switches.md for the switch side and the multi-ONT plan.)
|
||||||
ontV4 = net.cidr.host 100 prefixes.ont.v4;
|
ontV4 = net.cidr.host 100 prefixes.ont.v4;
|
||||||
|
|
||||||
# river is routing-common index 0; the Digiweb static IP we request via IPCP
|
# river is routing-common index 0; the Digiweb static IP we request via IPCP
|
||||||
|
|||||||
Reference in New Issue
Block a user