# Two-Host Routing Simulation Proves, with real Docker network isolation (not mocks), that the Gateway-to-Lab data path described in `docs/PHASE4_1_PREPRODUCTION_HARDENING.md` §1 actually works before the real Hetzner/Proxmox hardware exists. See that document for the full design; this is the test harness. ## Run it ```bash cd infra/scripts/simulate-two-host PROVISIONER_SHARED_SECRET=... ./run-simulation.sh # builds, runs, tears down PROVISIONER_SHARED_SECRET=... ./run-simulation.sh --keep-up # leaves the stack up for debugging ``` Confirmed passing, 13/13, as of this phase's implementation — see the Phase 4.1 report for the full run transcript. ## What this genuinely proves (TESTED LOCALLY, with real Docker networking) - The `gateway` container, attached ONLY to an isolated bridge network with no route to any Lab's per-environment bridge network, **cannot** open a TCP connection to a Lab container's Docker-internal IP — verified via a raw connect attempt run from inside the actual gateway container's own network namespace, not asserted from the test driver's (irrelevant) vantage point. - The Gateway **does** reach the Lab correctly through the Lab Host Router, over real mTLS, for both a plain HTTP request and a full WebSocket terminal session — including a genuine `kubectl get nodes` executed inside the Lab and its real output read back. - Alice/Bob isolation (own lab ALLOW, other's lab DENY, including over WebSocket), garbage tokens, anonymous requests, and destroyed-environment tokens are all rejected through the real code path — the same `checkOwnership`/`resolveTarget` logic production will run. - The Lab Host Router refuses a connection with no client certificate at all — mTLS is the actual authentication mechanism, not a documented intention. ## What this does NOT prove (SIMULATED PRODUCTION BOUNDARY, not identical to real hardware) - Real cross-VM latency, MTU, or anything about two genuinely separate kernels — this is one Docker daemon with two isolated networks, a faithful stand-in for network *reachability* rules but not a physical network. - The actual Hetzner/Proxmox firewall enforcement (`infra/firewall/*.rules`) — this harness doesn't touch iptables at all; Docker's own network isolation is what's under test here. - Provisioner/API/Postgres reachability across the VM boundary — deliberately kept host-networked and unchanged from `docker-compose.dev.yml` in this harness, because that control-plane path was already proven separately (Phase 4's real mTLS handshake test against the Docker Engine TLS port) and isn't what changed this phase. Only the Gateway's isolation is new here. ## REQUIRES REAL HARDWARE (tracked in docs/PRODUCTION_VALIDATION_CHECKLIST.md, not here) Running this exact flow against the real `rn-platform-01`/`rn-lab-host-01` VMs, through the real firewall, with real network latency between two physical/virtual hosts.