Files
rootnode-academy/infra/docker/IMAGE_STRATEGY.md
T
ced4568 d2280d0f0d feat(security): production Compose topology, firewall rulesets, Docker mTLS
Named-bridge-network production Compose (not host networking, unlike dev)
for rn-platform-01; egress-proxy + agents for rn-lab-host-01. Caddy reverse
proxy, single public origin, Grafana restricted to management_cidr.
iptables-restore firewall rulesets for all three hosts (Proxmox host,
rn-platform-01, rn-lab-host-01) codifying the Platform/Lab-Host boundary:
no student-to-student traffic, no direct Proxmox exposure, no direct
Postgres/Provisioner exposure from the Lab Host.

Mutual TLS for the Provisioner<->Lab Host Docker connection: certificate
generation/verification scripts, dockerd TLS config, firewall-restricted
port 2376. Application support already existed since Phase 1; this is
purely the operational half.

Validated: docker compose config, caddy validate, firewall structural lint,
and a real openssl s_server/s_client mTLS handshake proving the cert chain
and mutual-auth rejection (legitimate client verified, rogue client
correctly rejected with verify code 19).
2026-08-18 17:13:09 -05:00

2.4 KiB
Raw Blame History

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 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).