Files
ced4568 eae8e48d43 feat(routing): Lab Host Router closes the Gateway-to-Lab cross-VM gap
Adds platform/lab-router/ — a narrow service that runs on the Lab Host,
authenticates the Gateway via mTLS, and resolves an opaque routing token to
a local container purely from Docker label state (no Postgres access, no
callback to the Provisioner). Replaces the Gateway's env.internal_ip
assumption (only valid when Gateway and Lab share a Docker daemon) with a
data-driven direct/router decision per lab_host
(lab_hosts.routing_mode/router_endpoint, migration 004), resolved in one
place (platform/gateway/src/lab-endpoint.js's labTarget()) that every
proxy/fetch call site in server.js now goes through.

Extends the existing Docker TLS CA (infra/scripts/docker-tls/generate-docker-tls.sh)
with a second mTLS leaf pair (router-server/gateway-client) for this new
data-plane connection, deployed by the docker-tls Ansible role into a
separate directory from the control-plane pair. Firewall
(rn-lab-host-01.rules) replaces the old speculative wide port-range
allowance with one narrow rule: platform_ip -> the Router's port only.

dev-local stays routing_mode: direct — zero behavior change for local dev,
confirmed by the full existing regression suite passing unmodified.
2026-08-24 20:14:39 -05:00

51 lines
2.5 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Container image strategy
## Images
| Image | Built from | Registry path |
|---|---|---|
| `rootnode/platform-api` | `platform/api/Dockerfile` | `{{ registry }}/rootnode/platform-api` |
| `rootnode/gateway` | `platform/gateway/Dockerfile` | `{{ registry }}/rootnode/gateway` |
| `rootnode/provisioner` | `platform/provisioner/Dockerfile` | `{{ registry }}/rootnode/provisioner` |
| `rootnode/egress-proxy` | `platform/egress-proxy/Dockerfile` | `{{ registry }}/rootnode/egress-proxy` |
| `rootnode/lab-router` | `platform/lab-router/Dockerfile` (Phase 4.1) | `{{ registry }}/rootnode/lab-router` |
| `rootnode/lab` | root `Dockerfile` (the student Lab image) | `{{ registry }}/rootnode/lab` |
## Tagging
**Never `:latest` in staging or production.** Every build is tagged with:
- the full git SHA (`sha-<40 hex>`) — always, every build, the ground truth
- `staging-<short-sha>` on every push to `main` that passes CI (moving pointer, staging only)
- a semantic tag (`vX.Y.Z`) only on an explicit, human-triggered production release
`infra/scripts/ci/tag-and-push.sh` implements this — see §18 in
`docs/PHASE4_PRODUCTION_INFRASTRUCTURE.md`.
Deployment configs (`infra/environments/*/​*.env.template`, Ansible `group_vars`) reference the
tag as a variable (`image_tag`), never a hardcoded value baked into a compose file — rolling
back is changing one variable and re-running the `platform`/`lab-host` Ansible role, not editing
a compose file by hand (see `docs/RUNBOOK_ROLLBACK.md`).
## Registry
No paid registry required. Two supported options, chosen at deploy time via `registry` in
`infra/ansible/group_vars/production.yml`:
1. **Gitea's own built-in container registry** (`git.synthossystems.com`) — zero new
infrastructure, already authenticated the same way the repo itself is. Preferred default.
2. **A self-hosted registry** (e.g. a `registry:2` container on rn-platform-01 itself, or
elsewhere in the homelab) — a documented fallback if the Gitea registry isn't enabled on
this instance.
Either way: private registry, credentials pulled the same way any other production secret is
(`docs/PRODUCTION_DEPLOYMENT.md` §10), never a public Docker Hub push of anything beyond what's
already public (this project doesn't publish anything to Docker Hub in Phase 4).
## Rollback
Every image is content-addressable by SHA tag, so "roll back" is always available as long as
the previous tag hasn't been garbage-collected from the registry — see
`docs/RUNBOOK_ROLLBACK.md` for the exact procedure (re-point `image_tag`, re-run the Ansible
role, verify).