Skip to content

refactor infra for Traefik + Cloudflare Tunnel deploy#2

Open
whereisanzi wants to merge 1 commit into
mainfrom
infra/traefik-and-container-hardening
Open

refactor infra for Traefik + Cloudflare Tunnel deploy#2
whereisanzi wants to merge 1 commit into
mainfrom
infra/traefik-and-container-hardening

Conversation

@whereisanzi
Copy link
Copy Markdown
Contributor

Summary

Replaces the in-cluster Caddy with Traefik labels so the production stack can sit behind a single global reverse-proxy fronted by Cloudflare Tunnel. No more ports exposed to the LAN; container privileges dropped.

Depends on (but does not strictly require) #1.env.example documents env vars introduced in that PR.

docker-compose.yml

  • Remove the caddy service and its volumes
  • Add Traefik labels on web and api:
    • api matches Host(CALUNGA_HOST) && (PathPrefix(/v1) || Path(/health))
    • web matches Host(CALUNGA_HOST) (catch-all, lower priority)
    • Also matches www.CALUNGA_HOST
  • Two networks: calunga_internal (bridge, project-local) and edge_proxy (external, created by the /srv/edge stack)
  • web and api join both networks; db, redis, dagster-* stay on calunga_internal
  • All published ports bind 127.0.0.1 — nothing leaks to the LAN, Traefik reaches services via the container network
  • Hardening: cap_drop: [ALL] and security_opt: [no-new-privileges:true] on every service; deploy.resources.limits.memory per service; ./scripts mounted read-only

Caddyfile

Deleted. Traefik replaces it; routing now lives in Compose labels.

.env.example

Refreshed and documents every env consumed by the stack, including hardening knobs added in #1 (APP_ENV, magic-link limits, token quotas, message-size caps, Sentry DSNs) plus CALUNGA_HOST for Traefik routing.

Host prerequisite

The external edge_proxy network must exist before docker compose up:

docker network create edge_proxy

In production this is created by the /srv/edge stack (next PR). In development it is just an empty bridge that Traefik never attaches to (Traefik isn't running locally).

Test plan

  • python -c "import yaml; yaml.safe_load(open('docker-compose.yml'))" — syntax OK
  • docker network create edge_proxy && docker compose up -d — stack starts cleanly
  • curl http://localhost:8000/health — 200 (via host port bind)
  • docker compose exec api curl -fsS http://localhost:8000/health — 200 (internal)
  • nmap -p 5432,6379 192.168.x.x from another LAN host — no banner (db/redis bound to loopback)

Replaces the in-cluster Caddy with Traefik labels so the production stack can
sit behind a single global reverse-proxy fronted by Cloudflare Tunnel.

docker-compose.yml:
- Remove the caddy service and its volumes
- Add Traefik labels on web and api (Host(CALUNGA_HOST), api on /v1 and /health)
- Declare two networks: calunga_internal (project-local) and edge_proxy (external)
- Connect web and api to both networks; db/redis/dagster stay internal only
- Bind all published ports to 127.0.0.1 so nothing leaks to the LAN
- Drop ALL capabilities and apply no-new-privileges on every service
- Apply memory limits via deploy.resources
- Mount ./scripts read-only

Caddyfile: deleted (Traefik replaces it).

.env.example: refreshed and documents every env consumed by the stack,
including the hardening knobs introduced in the previous PR
(APP_ENV, JWT_SECRET strength gate, magic-link limits, token quotas,
message-size caps, Sentry DSNs) plus CALUNGA_HOST for Traefik routing.

The edge_proxy network must be created once on the host
(`docker network create edge_proxy`) before starting the stack; in production
the /srv/edge stack provisions it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant