# Production compose for rn-lab-host-01. Deployed by infra/ansible/roles/lab-host/ to # /opt/rootnode/lab-host/docker-compose.yml, run as a systemd unit # (rootnode-lab-host.service.j2). Runs the shared egress proxy every student Lab's outbound # traffic passes through (platform/egress-proxy/, unchanged since Phase 1) and, as of Phase # 4.1, the Lab Host Router (platform/lab-router/) that gives the Gateway on rn-platform-01 a # way to reach a running Lab without ever touching a Docker-internal address — see # docs/PHASE4_1_PREPRODUCTION_HARDENING.md §1-3. Student Lab containers themselves are NOT # part of this compose file — they're created/destroyed individually by the Provisioner over # the remote Docker TLS endpoint (dockerd itself, not compose), same mechanism as dev, just # remote; the Router finds them by label, purely from local Docker state (see its own README). networks: rn-egress: driver: bridge services: egress-proxy: image: "{{ registry }}/rootnode/egress-proxy:{{ image_tag }}" restart: unless-stopped ports: - "3128:3128" # reachable from every per-Lab Docker network on this host — restricted # to those networks by the Provisioner's per-environment DOCKER-USER # chains, not by a published-port firewall rule (there is no sensible # single source IP to restrict this to; the egress proxy's OWN allowlist # is the actual control, see platform/egress-proxy/allowlist.txt) networks: [rn-egress] lab-router: image: "{{ registry }}/rootnode/lab-router:{{ image_tag }}" restart: unless-stopped # Needs the host's own routing table to reach whichever per-Lab bridge network # (rootnode-lab-net-) the Provisioner created for a given container — those networks # aren't known ahead of time and aren't attachable one-by-one, the same reason egress-proxy # would need this too if it initiated connections outward instead of just accepting them. # Restricted to platform_ip on port {{ lab_router_port | default('8443') }} at the host # firewall (infra/firewall/rn-lab-host-01.rules) — this is the ONLY thing that makes host # networking here safe, same defense-in-depth pattern as the Docker TLS port. network_mode: host volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - /etc/rootnode/lab-router-tls:/tls:ro environment: LAB_ROUTER_PORT: "{{ lab_router_port | default('8443') }}" LAB_ROUTER_TLS_CA: /tls/ca.pem LAB_ROUTER_TLS_CERT: /tls/router-server-cert.pem LAB_ROUTER_TLS_KEY: /tls/router-server-key.pem node-exporter: image: prom/node-exporter:latest restart: unless-stopped network_mode: host pid: host volumes: - /proc:/host/proc:ro - /sys:/host/sys:ro - /:/rootfs:ro command: - "--path.procfs=/host/proc" - "--path.sysfs=/host/sys" - "--path.rootfs=/rootfs" - "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)" cadvisor: image: gcr.io/cadvisor/cadvisor:latest restart: unless-stopped privileged: true devices: - /dev/kmsg volumes: - /:/rootfs:ro - /var/run:/var/run:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro ports: - "8080:8080" # scraped remotely by Prometheus on rn-platform-01 — restricted to that # one source IP by the host firewall (rn-lab-host-01.rules), not by # binding to localhost, since Prometheus isn't local on this host. promtail: image: grafana/promtail:latest restart: unless-stopped volumes: - /opt/rootnode/lab-host/promtail-config.yml:/etc/promtail/config.yml:ro - /var/run/docker.sock:/var/run/docker.sock:ro - /var/log/journal:/var/log/journal:ro - /etc/machine-id:/etc/machine-id:ro command: ["-config.file=/etc/promtail/config.yml"] # host networking so promtail can reach Loki on rn-platform-01 over the network — this # traffic is OUTBOUND from rn-lab-host-01 (log push, client-initiated), which the host # firewall's default-ACCEPT OUTPUT chain already allows; nothing new needed there. network_mode: host