// devlocal · part 1 of 2

devlocal, Part 1: The Boundary

Trust zones, one way in, encryption on every hop, and rule sets that each deny on their own. The devlocal network, built to contain north/south and east/west threats alike.

OPNsense802.1QWireGuardmTLSNTSDNS-over-TLSMACsecSecurity Onion

The Problem

A perimeter is one control. Behind it, most networks are flat: every host can reach every other host, the root of trust shares a kernel with the applications, and the security sensor sees only what crosses a cable. Defend north/south and ignore east/west, and the first compromised service becomes a staging point for everything else.

The devlocal network is built so that no single compromise, at the edge, in a service, in CI or in an agent, can reach the root of trust or move sideways without crossing a filter that logs it.

Threat Model

Every control on this page exists because of at least one of these adversaries.

Adversary What they want What stops them
Internet attacker A way in Zero inbound. The only path in is a tailnet the lab dials out to, followed by SSO.
Compromised edge host A route to backends or secrets The edge holds no Vault credential, only a short-lived certificate, and reaches exactly one listener, over mTLS, through one firewall rule.
Compromised tailnet coordinator A rogue node, or impersonation of the edge SSO doesn't depend on the coordinator, and clients verify the edge's certificate. A rogue node gets a login page, not a service.
Malicious CI job Lateral movement, exfiltration, persistence Three independent filters, hosts that only jail, and HTTPS-only egress with private ranges denied first.
Compromised agent Credentials, other agents, the root of trust Part 2. Nothing to steal, nowhere to go, and evidence of every attempt.
Compromised service An east/west pivot toward Vault One VM and VLAN per service group, every hop through the firewall, certificates that name only their holder.

Two risks are accepted deliberately. Root on a single host is game over for that host, and the design's job is to keep it from spreading, not to pretend it can't happen. Copies a host produces for the sensor are trusted only as far as that host, which is why switch mirrors remain the independent record of traffic between hosts.

Five Enforcement Layers

Each layer answers a different question. A flow passes every layer it touches.

Layer Question Enforced by
Zone Which host may reach which host, and how The lab firewall (OPNsense): default deny, every denial logged. A second, independent firewall wraps CI.
VLAN Which layer-2 segment a port belongs to 802.1Q on switches that never route. Tagged-only trunks, an unused native VLAN, ingress filtering.
Host What a host accepts and forwards nftables and host firewalls. Forwarding between interfaces stays off; hypervisors pin every VM's MAC and IP.
Container Which workloads inside a host can talk Directional Docker networks: per-service ingress networks, internal-only core networks, narrow metrics networks.
Identity Who is calling, and what they may do Vault PKI for mTLS on every persistent hop, AppRole for secrets, Authentik for SSO and authorization.

Physical Shape and Trust Zones

flowchart TB
    UP["Uplink · one 6 GHz wireless bridge
treated as untrusted WAN"] --> FW["Lab firewall · OPNsense
routes and filters every zone"] FW --> DMZSW["DMZ switch
physically separate"] DMZSW --> EDGE["Edge box
bonded 2.5G pair · edge proxy"] FW --> PSW["Private switch · 2.5G
tagged trunks, per-port isolation"] PSW --> AUTH["Authority host
Vault · PKI · SSO · DNS front"] PSW --> WL["Workload host · KVM
one VM per service group"] PSW --> CIFW["CI firewall · OPNsense
second, independent filter"] CIFW --> CIX["x86 CI host
Linux + Windows runner VMs"] CIFW --> CIM["Apple Silicon CI host
ephemeral macOS VMs"] PSW --> MGMT["Management plane
BMCs · switch UIs · KVM console"] PSW -->|"10G uplink"| CSW["Cluster switch · 10G"] CSW --> GPU["3 GPU nodes
inference only"] GPU ---|"200G ring · RDMA over MACsec"| GPU CSW --> PRI["Primary storage
encrypted pool"] CSW --> BAK["Backup storage
pulls ciphertext only"] DMZSW -.->|"mirror"| SO["Sensor · Security Onion
receive-only capture links"] PSW -.->|"mirror"| SO CSW -.->|"mirror"| SO style UP fill:#1a2332,stroke:#475569,color:#e2e8f0 style FW fill:#1e3a5f,stroke:#22d3ee,color:#e2e8f0 style CIFW fill:#1e3a5f,stroke:#22d3ee,color:#e2e8f0 style DMZSW fill:#1e3a5f,stroke:#f59e0b,color:#e2e8f0 style EDGE fill:#1e3a5f,stroke:#f59e0b,color:#e2e8f0 style PSW fill:#1a2332,stroke:#475569,color:#e2e8f0 style CSW fill:#1a2332,stroke:#475569,color:#e2e8f0 style AUTH fill:#1e3a5f,stroke:#10b981,color:#e2e8f0 style WL fill:#1e3a5f,stroke:#22d3ee,color:#e2e8f0 style CIX fill:#1e3a5f,stroke:#f59e0b,color:#e2e8f0 style CIM fill:#1e3a5f,stroke:#f59e0b,color:#e2e8f0 style MGMT fill:#1a2332,stroke:#475569,color:#e2e8f0 style GPU fill:#1a2332,stroke:#475569,color:#e2e8f0 style PRI fill:#1a2332,stroke:#475569,color:#e2e8f0 style BAK fill:#1a2332,stroke:#475569,color:#e2e8f0 style SO fill:#1a2332,stroke:#475569,color:#e2e8f0

The DMZ and the private side never share a switch; they meet only inside the firewall. The cluster tier hangs off the private switch on a single 10G uplink, so bulk traffic between GPU nodes and storage never touches the firewall. Every lab host is wired-only. A GPU node or server left on house Wi-Fi would bridge the house into its zone around the firewall, so built-in radios are off and stay off across reboots.

Zone Who lives there What may come in
DMZ The edge box and its switch's management Nothing. The edge only dials out.
Authority Vault, PKI and certificate push, Authentik, the DNS-over-TLS front SSO checks from ingress, secret fetches from named VMs, DNS from the firewall, break-glass SSH. Nothing else.
Ingress The private reverse proxy, the agent listener and the agent egress broker The edge proxy and agent pods; the agents node reaches the broker only, for distro mirrors, and never the agent listener.
Service groups Forge (git, gists, package registry), memory, mail, ITSM, observability, one VLAN each Ingress only, plus named scrapes, log pushes, and CI runners to the forge.
Model gateway The model gateway and its database Ingress, the memory service and the dispatcher.
Agent control The agent dispatcher Nothing. It only starts connections.
Agents The agents VM node and a routed pod network behind it The cluster API from the dispatcher alone, the UI through ingress. Pods accept no inbound connections.
Workload management The hypervisor itself Break-glass SSH, certificate push, metrics scrapes.
Cluster GPU nodes and storage Nothing from the DMZ. Bulk traffic stays on layer 2.
Management BMCs, switch UIs, the KVM console, the sensor's UI The break-glass path only. No internet in either direction.
CI link and enclave The CI firewall and the CI hosts behind it Nothing. Runners only dial out.
GPU ring Point-to-point GPU interconnect Not routed at all. RDMA and collectives only, over MACsec.

IPv6 is disabled across the lab zones. The firewall sends no router advertisements there, hosts disable it, and container runtimes run without it. The tailnet's own IPv6 lives inside WireGuard and is unaffected.

The Only Way In

sequenceDiagram
    participant D as My device
    participant C as Tailnet coordinator (off-site)
    participant E as Edge proxy (DMZ)
    participant F as Lab firewall
    participant I as Private proxy (ingress VM)
    participant A as Authentik
    participant S as Service gateway
    E->>C: Dials out for keys (TLS)
    D->>C: Gets keys and ACLs (TLS)
    D->>E: WireGuard, direct or via relay
    E->>F: mTLS with edge client cert
    F->>I: One permitted rule (X03)
    I->>A: forward_auth over mTLS
    A-->>I: Allowed user identity
    I->>S: mTLS on that group's route

Nothing enters the house unsolicited. The edge box dials out to an off-site coordinator (Headscale, with its own relay) for keys and NAT-traverses for WireGuard, falling back to the relay, which only ever carries ciphertext. House devices take the same path. There is no LAN shortcut.

The edge holds no Vault credential and never opens a connection to Vault. Its short-lived certificates are issued on the private side and pushed to it over SSH with a forced command. Its single upstream is the private proxy, which requires the edge's client certificate, strips any identity headers a client supplies, and trusts only what SSO answers.

A compromised coordinator isn't enough. A rogue coordinator can add a node to the tailnet, and the edge will forward that node's requests, because the edge's certificate proves a request came through the edge, not which device sent it. Coordinator ACLs don't count against this threat; a compromised coordinator writes them. SSO does, because it doesn't depend on the coordinator. And when a rogue coordinator tries to stand in for the edge, the device's TLS check of the edge's certificate keeps it from ever becoming a readable interception.

Trust Tiers Inside the Private Zone

The private side is not one flat network. It is split by trust tier across two identical hosts, so a compromised application never shares a kernel with the root of trust.

The authority host runs Vault, Authentik and its database, the PKI with its CSR collector and certificate push, the DNS-over-TLS front, and its own encrypted backups. Nothing else. One interface, one access port. These mini PCs have no ECC memory, so a unit holds the root of trust only after it passes a fitness gate: repeated memory tests and a multi-day soak with zero kernel memory faults. Its database runs with data checksums on, and every retained Vault snapshot generation is restore-tested, not just the newest.

The workload host is a KVM hypervisor that only jails. Each service group gets its own VM, VLAN and bridge. No bridge has an address, forwarding is off, and the host's only address lives on its own management VLAN. Two VMs on the same box meet only through the firewall, which logs the hop, and the switch mirror copies it to the sensor.

Rule What the hypervisor enforces, below anything a guest can change
W01 Each VM tap's source MAC and IP are pinned. Anything else is dropped and logged.
W02 Every tap is an isolated bridge port, and tap-to-tap frames are dropped as well. No VM reaches another at layer 2.
W03 On the cluster bridge, only the model gateway's egress network may send, and only to the GPU nodes' mTLS inference gateways.
W04 On the copy bridge, each VM may only send encapsulated span copies from its pinned span MAC. Nothing is ever delivered to a VM there.
W05 VMs cannot reach the host. No bridge has an address, and IPv6 is off, so there is no link-local address for a guest to find.
W06 Host SSH answers only the break-glass path and the certificate push.
W07 The host's own traffic leaves only toward the firewall: DNS over TLS, NTS, logs, backups and package updates.

VMs are cloned from a template, so first boot regenerates SSH host keys and the machine ID. No two VMs share a key.

The Rule Sets

Rules come in families, and each family is enforced by a different device. A flow that several families deny stays blocked when any single device turns permissive. Everything not listed is denied, and every denial is logged.

Family Enforced by Covers
X The lab firewall Forwarded traffic between zones
B The lab firewall Traffic to and from the firewall itself
C The CI firewall Everything entering or leaving the CI enclave
H The x86 CI host What its runner VMs may reach. The macOS CI host enforces the same with its own packet filter.
W The workload hypervisor Layer-2 isolation between VMs, above
K The agents VM node What agent pods may reach, in Part 2
I Layer 2 inside one zone Flows that never touch the firewall, each tested to stay on layer 2

Between zones (X)

ID Flow Protocol and identity Verdict
X01 Edge → tailnet coordinator TLS; node key permit, outbound only
X02 Edge ↔ tailnet peers and relay WireGuard, matched on the client's source port, since peers' NATs choose the destination permit, outbound only
X03 Edge proxy → private proxy on the ingress VM mTLS, edge client certificate permit
X04 Private side → edge, certificate push SSH, pinned key, forced command permit
X05, X06 Edge logs out; edge metrics scraped from the private side mTLS permit
X08 Any DNS that bypasses the firewall — deny, logged. A host that tries is misconfigured or compromised, and the log says which.
X09 CI runners → internet HTTPS only, after private, link-local, CGNAT, multicast and external DNS are denied permit
X10 Any zone → SMTP on the internet — deny. No external relay.
X11 Break-glass → management interfaces SSH keys, HTTPS-only UIs, IPMI restricted to its strongest cipher suite permit, break-glass only
X12 Management plane → internet — deny
X13 Service tiers → internet for updates, image pulls and model providers HTTPS only, from HTTPS mirrors permit, logged. Never from agents, the agents node or the dispatcher.
X14 DMZ → anything not above — deny, logged
X15 House LAN → lab services — deny, logged. House devices use the tailnet like everything else.
X16 CI runners → the forge's CI listener mTLS, one certificate per runner permit
X17 Break-glass → authority host SSH key permit, break-glass only. Recovery while the tailnet is down.
X18 CI firewall's logs → syslog receiver Syslog over TLS, client certificate permit
X19 Ingress → Authentik, for forward_auth and the SSO UI mTLS, proxy client certificate permit
X20 Ingress → each group's gateway, including the model gateway and the agent UI mTLS, proxy client certificate permit
X21 Named VMs → Vault for secrets mTLS plus AppRole, secret IDs bound to that VM's address permit
X22 Named senders → log and trace ingest mTLS, pinned sender certificates permit
X23 Metrics → named exporters on every tier mTLS, the scraper's certificate permit
X24 Memory service → model gateway mTLS, egress certificate permit
X25 Agent pods → the ingress agent listener mTLS with the run's certificate, which must match the SSO user permit
X26 SSO and alerting → mail submission Implicit-TLS SMTP, per-service account permit
X27 Authority host → every host, certificate push SSH, pinned push key, forced command permit
X28 Authority and workload hosts → backup target on primary storage SFTP, per-source key, chroot; archives encrypted before they leave permit
X29 Dispatcher → the agents cluster's API mTLS, namespaced role permit
X30 Dispatcher → model gateway, to mint and revoke run keys mTLS, admin routes only permit
X31 Dispatcher → Vault, to sign run certificates mTLS plus AppRole bound to the dispatcher permit
X32 Agent pods → the egress broker; the agents node → the egress broker for distro mirrors mTLS: the run's certificate for pods (that agent's allowlist), the node certificate for the node (mirror-only policy) permit
X33 Egress broker → internet, per agent allowlist HTTPS permit
X34 Mail → Authentik, to check users mTLS permit
X35 Agents node → the lab registry mTLS, node certificate permit

The firewall itself (B)

ID Flow Protocol and identity Verdict
B01 Every zone → firewall, DNS DNS over TLS with a Vault-issued certificate. Plain DNS to the firewall is dropped and logged, so a client that falls back to plaintext fails loudly instead of quietly working. permit
B02 Every zone → firewall, time NTS: a TLS 1.3 key exchange, then authenticated NTP permit
B03, B04 Firewall → upstream resolvers and time servers DNS over TLS and NTS to named servers, pinned by address and name so reaching them needs no DNS named hosts only
B05 Firewall → the lab's own DNS front DNS over TLS, Vault-issued certificate permit
B06 Firewall's logs → syslog receiver Syslog over TLS, client certificate permit
B07 Break-glass → firewall UI and SSH SSH keys, HTTPS break-glass only. They answer on no other address.
B08 Firewall → its update mirrors HTTPS; packages still signature-checked permit, logged
B09 Firewall's WAN lease DHCP (a named exception) permit
B10 Anything else to or from the firewall — deny, logged
B11 Metrics → the firewall's exporter mTLS permit

Inside a zone, never through the firewall (I)

ID Flow Transport
I01 Model gateway's egress sidecar → GPU inference gateways mTLS
I02 GPU nodes ↔ primary storage NFS over RPC-with-TLS, mutual TLS required. Plaintext mounts are refused.
I03 Backup storage pulls from primary Replication over mTLS, raw encrypted sends
I05 GPU ↔ GPU collectives RDMA over MACsec

Encryption on Every Hop

Where an encrypted form exists, it is used: mTLS between services, HTTPS for all internet egress, DNS over TLS, NTS for time, NFS over RPC-with-TLS, syslog over TLS, HTTPS-only management UIs, IPMI restricted to its strongest cipher suite, LUKS2 under ZFS on every pool disk, and MACsec on the GPU ring. NTS authenticates time rather than hiding it. Time isn't secret. Forged time is the threat, because every certificate check downstream depends on it.

Anything that can't meet the rule gets an E-number, a reason and its containment. There is no silent fallback anywhere.

ID Traffic Why it can't be encrypted Containment
E01 ARP, LLDP, STP, LACP, and the firewall's WAN DHCP lease No encrypted form exists One layer-2 segment each, carrying no secrets. Lab hosts use static addresses, so lab zones run no DHCP.
E02 Plain NTP to management appliances with no NTS client The appliance can't do better Served only from the firewall's management address. Port isolation keeps everything else from reaching them.
E03 Host-local DNS: containers and pods → the host's TLS stub The container runtime's resolver and the cluster DNS speak only plain DNS Never leaves the host or VM. The stub listens only on loopback and the bridge gateway, and the span never copies it.
E04 NTS bootstrap on a box with no battery-backed clock With no trustworthy date at cold start, a certificate check fails on a stale clock Only certificate dates are skipped, and only until the first clock update. Chain and name checks stay on, and trust is pinned to the intended servers' CAs.
E05 Windows runner VM → the vendor's update CDN The vendor delivers update files over plain HTTP by design; discovery stays on pinned HTTPS and every file is signature- and hash-checked Allowed from that one VM, to the vendor's update hosts only, on three separate filters.
E06 Windows runner VM time Its time service sends plain NTP outside a domain The VM syncs from its host over a virtual network that never reaches a cable. The host itself uses NTS.
E07 Gateway → app over loopback inside one network namespace The app speaks plain HTTP Loopback never reaches a veth, so it never leaves the namespace and is never copied.
E09 Vault and SSO bootstrap on one-member host bridges The bootstrap runs before TLS material exists On the authority host, which runs no span.
E11 Agent harness → Citadel inside one container Harness runtimes call a plain base URL; handing them a client key would break the secret boundary Process-local inside the container. Citadel attaches the run's identity before anything leaves it. See Part 2.
E12 Kubelet health probes to pods on the same node Probes are plain HTTP from the node's namespace Never leaves the agents VM, hits only health paths, never copied.

Mirror links carry copies of traffic, not a hop of their own. Every plaintext exception is filtered out before anything is copied, so the capture fabric carries only ciphertext and metadata.

The CI Enclave

CI runs code nobody here wrote, so it lives in its own enclave behind a second, independent OPNsense firewall with its own rule set, credentials and update schedule.

  • Three filters, each denying alone. A job's traffic passes its host's own filter, then the CI firewall's C rules, then the lab firewall's X rules. The C rules restate the CI policy instead of trusting the lab firewall's.
  • Hosts that only jail. The x86 CI host runs a hypervisor and nothing else: a Linux runner VM and a Windows runner VM on a routed virtual network with no NAT. The macOS CI host runs ephemeral macOS VMs cloned per job from golden images and deleted afterward. Neither host ever runs a job itself, and guests get no shared folders, clipboard or host sockets.
  • Lanes can't reach each other. Each CI host has its own firewall port and subnet, so the two meet only through the CI firewall, which denies and logs anything between them. A job that escapes onto one host can't pivot to the other lane.
  • Egress is HTTPS only, with private ranges, link-local, CGNAT, multicast and external DNS denied first, on all three filters.
  • CI can't administer its own jailer. The CI firewall's admin port forwards nothing and stays reachable with the lab firewall powered off. The runner side answers no UI and no SSH.

Independent denial is tested, not assumed. Each filter is tested in isolation. One forbidden CI flow, a runner VM reaching the cluster zone for example, is temporarily permitted on the other two filters, and the filter under test has to deny and log it on its own. The rules are restored, and the test repeats for each of the three filters. Opening only one filter would leave two possible blockers and prove nothing about either. Two firewalls running the same software are only independent if their configs, credentials and updates are, and this test is the proof.

Storage That Treats Its Own Backup Host as Hostile

  • Two hosts, two shelves, two power paths. The primary server holds the main RAIDZ2 pool and serves it to the GPU nodes. A separate backup server holds the backup pool on its own disk shelf, behind its own HBA, with its own credentials.
  • Ciphertext below the filesystem. Every pool disk is LUKS2, so the SAS paths carry only ciphertext. ZFS native encryption sits on top for per-dataset keys and raw sends.
  • Pull, never push. The backup host pulls raw encrypted sends over mTLS with its own certificate and never loads the primary's ZFS keys. A compromised backup host can't read the data, and the primary holds no credentials that reach the backup.
  • Retention the primary can't touch. The backup host keeps its own snapshot retention, so a wiped or encrypted primary can't age out the older copies. The backup shelf powers up for scheduled runs, then the pool is exported and the shelf powers down.
  • Escrow. Vault unseal shares, LUKS keys, LUKS header backups, the disk-serial → LUKS UUID → pool map and the ZFS keys also live offline, because Vault's own backups sit on the storage those keys unlock. A key alone can't open a disk with a damaged header. Stale header backups are destroyed after every keyslot change, since an old header still opens with an old key.
  • Acceptance is a restore. The storage tier's gate is a full restore from the backup pool to the primary, with the primary pool unavailable and every key and header taken from escrow, starting with opening the LUKS devices. A successful send proves nothing. Header restore is proven separately on a disposable disk image, never on real pool disks.

Reads pass through AES three times (LUKS, ZFS, TLS), so CPU headroom is part of the restore gate.

DNS and Time That Fail Loudly

The firewall serves DNS over TLS and NTS to every zone, and resolves and keeps time upstream the same way. Serving it is half the job. Every client has to use it, and fail rather than fall back.

  • Every Linux host runs chrony with NTS on every source and no plain sources, and resolves in strict DNS-over-TLS mode with the server's certificate name checked.
  • The container runtime forwards container DNS straight to the host's upstream servers in plain DNS, skipping the host's TLS stub. Every container host therefore runs a TLS stub bound only to loopback and the bridge gateway, and the runtime points at it (E03).
  • Appliances get no DNS server at all. They're configured by address, so they send no DNS.
  • Fallback is tested from fresh state. With the DNS-over-TLS path blocked, an uncached random name fails to resolve. With chrony's saved NTS state cleared and its key exchange blocked, the source never becomes selectable.

Seeing East/West Traffic

What the sensor sees, and what it can't

The sensor runs Security Onion (Zeek, Suricata, full packet capture and the hunting UI) on three receive-only capture links: the private trunk, the cluster switch and the DMZ. Its capture interfaces have no address, and each mirror destination port forwards nothing.

  • It sees everything entering or leaving the DMZ, everything the firewall routes for the private side, and east/west traffic on the cluster tier, with storage and replication filtered out at capture.
  • It doesn't see payloads. Nearly every hop is mTLS, so the sensor works from SNI, JA4, timing and volume. Caller identity comes from the proxy's and Vault's audit logs.
  • Mirrors are best effort. An oversubscribed mirror drops frames silently, so capture loss and kernel drops are alert signals in their own right.

Copies from inside a host

Traffic between VMs crosses the trunk, so the switch mirror already has it. Traffic between containers in one VM, or between pods on the agents node, never touches a wire. Citadel's span component copies exactly that, and nothing else.

The obvious approach is wrong. Copies sent out the NIC with their original MAC addresses teach the switch that both endpoints live on the host's port, and from then on it filters later frames of the flow before the mirror ever sees them. Worse, a copy of a frame from the firewall can relearn the firewall's MAC on the wrong port and misroute real traffic.

flowchart LR
    V["Container or pod veth"] -->|"tc mirror · policed"| X["VXLAN device
VNI = source group"] X --> CB["Copy bridge
send-only, pinned MAC"] CB -->|"copy-only VLAN"| SW["Private switch
floods: sink MAC never learned"] SW --> P1["Firewall trunk port
only other member of the VLAN"] P1 -.->|"port mirror"| SO["Sensor
decodes VXLAN"] P1 --> FW["Firewall
no interface on that VLAN · drops it"] style V fill:#1e3a5f,stroke:#22d3ee,color:#e2e8f0 style X fill:#1e3a5f,stroke:#22d3ee,color:#e2e8f0 style CB fill:#1e3a5f,stroke:#f59e0b,color:#e2e8f0 style SW fill:#1a2332,stroke:#475569,color:#e2e8f0 style P1 fill:#1a2332,stroke:#475569,color:#e2e8f0 style SO fill:#1e3a5f,stroke:#10b981,color:#e2e8f0 style FW fill:#1a2332,stroke:#475569,color:#e2e8f0

So copies are wrapped in VXLAN and sent from a fixed, locally administered span MAC per VM to a sink MAC that nothing ever sends from. The switch never learns the sink, so it floods. The copy-only VLAN has exactly two member ports, the workload host and the firewall trunk, so "flood" means one port. The existing trunk mirror delivers the copy to the sensor, and the firewall, which has no interface on that VLAN, drops it. No new switch, no new ports.

Never copied:

  • Plaintext. DNS and kubelet probes are excluded, and the span filter is generated from the exception table, so any future host-local plaintext exception is excluded by default. DNS visibility comes from the stubs' query logs instead.
  • Duplicates. Traffic through VM taps is already on the switch mirror. Copying it twice would skew Zeek's counts and double the load.
  • Bulk. Backups and image pulls.
  • The authority host. It runs no span, so its port never carries a tagged frame.

Copies are policed per VM and per host and marked background priority, so a flood lands on the policer, not on production traffic. A canary flow in each VM alerts when its feed goes quiet. A rooted VM can forge or drop its own copies, which is exactly why the switch mirror remains the independent record of traffic between groups.

Management and Recovery

The management plane holds the softest targets in the rack: BMC firmware and switch web UIs. It is reachable only from a labelled break-glass port and has no route to the internet in either direction. IPMI answers only that path, because its RAKP handshake hands a password hash to anyone who can reach it. Management devices share layer 2, so the firewall never sees traffic between them. The switch's per-port forwarding lists do that job: each manager forwards only to the uplink and the break-glass port, and each device's own allowlist admits only the firewall and the break-glass address.

Recovery never depends on what it recovers. A laptop on the break-glass port reaches every BMC, the KVM console and the sensor's UI at layer 2, and both firewalls through the uplink, with no tailnet, DNS, SSO or Vault involved. The one recovery-critical host without a BMC has an IP-KVM cabled to it. The CI firewall's local admin port works with the lab firewall powered off.

Cold start runs in dependency order: firewall → switches → Vault (unsealed with offline shares) → DNS, proxy and SSO → edge and tailnet → primary storage (LUKS opened, pool imported, keys loaded) → backup storage (imported without its keys) → GPU nodes → CI firewall → CI hosts → runner VMs → sensor. The firewall needs a sane clock before any of its own TLS works, so its first time sync is built to work cold.

Acceptance Gates

A chosen policy is not a demonstrated capability. Every guarantee that depends on hardware or client behavior is held to a gate on the live system, each with paired permit and deny tests.

Gate 1 · MACsec on the GPU ring. Software MACsec can't carry RDMA at all, because RDMA bypasses the kernel. The ring requires hardware offload, and the gate requires that:

  • NIC firmware and kernel meet the vendor's floor, with offload on;
  • the cipher uses extended packet numbering;
  • RDMA benchmarks and collective all-reduces move the security-association counters on both ends;
  • a mismatched SA stops RDMA on every GID, not just the MACsec one;
  • collectives are pinned so a broken RDMA path fails the job instead of falling back to plaintext TCP;
  • keys come from MKA and hold through rotation under load.

Extended packet numbering isn't optional. At 200 Gb/s with 4 KB frames the link moves about 6.1 million frames a second, so a 32-bit packet number (about 4.3 billion) is exhausted in roughly 12 minutes, and in about 4 at a 1500-byte MTU. A ring that can't meet the gate carries nothing.

Gate 2 · Capture isolation. A compromised sensor must not be able to inject a frame into any production VLAN. A laptop standing in for the sensor on each capture port sends untagged, tagged and double-tagged frames plus ARP and HTTPS probes. Nothing appears on any VLAN, the switch never learns the laptop's MAC, and no switch UI answers. A switch that can't hold this loses that capture link to a network TAP.

Gate 3 · DNS and time clients. Every client uses DNS over TLS and NTS and fails rather than falls back. Physical links carry no plain DNS at all; host-local hops are checked on each host, where no mirror can see them.

Gate 4 · East/west visibility. A 60-second flow between two containers in one VM arrives at the sensor whole, with zero missed bytes. No plaintext reaches the sensor through the copies, a flood is absorbed by the policer, and every VM's canary is live.

Gate 5 · Agent containment. See Part 2.

Gate 6 · Authority host fitness. Memory tests and a multi-day soak with zero logged memory faults before a unit holds Vault, PKI and SSO. The deny half runs the same log query over a window where faults are known to exist, proving the check can fail.

How the Tests Are Written

Every permit has a matching deny. Every test is an exact command run from a named host. No command prints a secret.

# Permit (ingress VM): reach the forge through the firewall with the proxy's cert
curl -sS -o /dev/null -w '%{http_code}\n' --cacert <lab-ca> \
  --cert <proxy.crt> --key <proxy.key> https://<forge-gateway>/api/healthz
# expect 200, and a firewall log entry under X20

# Deny (forge VM): reach mail directly
nc -vz -w3 <mail-vm> <smtp-submission>
# expect failure, and a logged deny
# Deny (workload host, as root): hop VLANs with a double-tagged frame
python3 -I -c "from scapy.all import *; sendp(Ether(dst='<fw-mac>')/Dot1Q(vlan=<native>)/Dot1Q(vlan=<mgmt>)/IP(src='<marker-src>',dst='<fw-mgmt>')/ICMP(), iface='<nic>', count=5)"
# on the firewall's management interface, tcpdump for <marker-src> captures nothing

Key Design Decisions

An x86 firewall, not a travel router. The lab boundary is a dedicated OPNsense box: a real stateful firewall, TLS log shipping built in, and headroom for every zone and rule above.

A second firewall for CI, running its own policy. The CI enclave sits behind its own OPNsense box with separate config, credentials and updates. Each CI host gets its own port and subnet, so the lanes meet only through a filter that logs it.

Visibility without new hardware. In-host copies ride VXLAN to a never-learned MAC over the existing trunk mirror. No capture switch, no extra ports, no purchases.

GPU nodes serve models and nothing else. Agents run in their own VM on the workload host. Model memory belongs to models, and a sandbox escape lands nowhere near the hardware that serves every model.

Two storage hosts with pull replication. A backup host that pulls ciphertext and never holds the primary's keys leaves no shared key or delete path between the copies.

A small fanless box at the edge. The edge does almost nothing by design, so it runs on hardware sized for that. The capacity it would have wasted runs the workload hypervisor instead.

Hard-Won Details

The details that separate a design that looks right from one that holds:

  • FORWARD DROP proves nothing on a container host. Docker inserts its own accept rules, so a dual-homed container host filters in DOCKER-USER, then gets tested for routing between interfaces, published ports and wildcard-bound listeners, again after a daemon restart and again after a reboot.
  • Container DNS skips the encrypted stub. The runtime forwards container lookups to the host's upstream servers in plain DNS. A local TLS stub the runtime points at closes that hole.
  • Every Linux bridge grows an IPv6 link-local address. Any host service listening on the IPv6 wildcard answers every guest on that bridge. Hypervisors that don't need IPv6 don't run it.
  • Pods hide behind their node by default. Common CNIs SNAT pod traffic to the node's address, so the firewall can't tell an agent from the kubelet. A routed, unmasqueraded pod network fixes that.
  • Broad PKI roles are a lateral-movement tool. A role that signs any subdomain of the internal zone lets a compromised VM obtain a certificate for Vault's name. Every host gets a role for its exact names.
  • 6 GHz drops WPA2 and unencrypted open networks. The band allows only WPA3-Personal (SAE), WPA3-Enterprise or Enhanced Open, each with protected management frames, and Enhanced Open encrypts without authenticating anyone. The bridge is configured for SAE only, on 6 GHz only, so the lab's one wireless hop gets authenticated WPA3 without forcing WPA3-only on everything else. Saving no fallback bands means a lost link fails closed instead of downgrading.
  • NTS has a chicken-and-egg problem on boxes without a battery-backed clock: certificate validity can't be checked before the clock is set. Skip only the dates, only until the first sync, with trust pinned to the intended servers.
  • Some vendors ship updates over plain HTTP on purpose. Signed, hashed files over a CDN are a chain-of-custody decision, not an oversight. It gets written down as an exception and contained, not papered over.
  • Postgres data checksums are not on by default in every release. Verify, enable them with the cluster stopped, and prove they catch a flipped byte.