From 5b221cc7a3473e1945064dc5cd7cabc0550597c0 Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Sun, 26 Jul 2026 23:21:43 -0700 Subject: [PATCH] Gate the VPS instance behind Traefik basic auth until Phase 16 Petal authenticates nobody yet -- StaticResolver hands every request the same local user -- so on a public host the whole API is open: anyone who finds the hostname can read and write documents and fill the disk with image uploads. Traefik holds the door until the OIDC flow exists. /api/health keeps its own higher-priority router with no middleware, so the acceptance criterion (public health endpoint, reachable by the monitoring on this box) still holds. Both the middleware and that router are deleted when Phase 16 lands. --- deploy/petal.env.example | 8 ++++++++ docker-compose.yml | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/deploy/petal.env.example b/deploy/petal.env.example index 1f2f06e..578cd9e 100644 --- a/deploy/petal.env.example +++ b/deploy/petal.env.example @@ -20,6 +20,14 @@ TZ=Europe/Lisbon PETAL_UID=1001 PETAL_GID=1001 +# --- Interim edge gate (delete when Phase 16 auth lands) --------------------- +# Petal has no authentication of its own yet — StaticResolver hands every +# request the same local user — so Traefik holds the door with basic auth until +# the OIDC flow exists. user:bcrypt-hash, as produced by: +# htpasswd -nbB petal 'your-password' +# /api/health is deliberately exempt (its own router) so monitoring still works. +PETAL_BASIC_AUTH= + # --- LLM (millenia, over headscale) ------------------------------------------ # The only cross-VPN dependency. Petal degrades warmly when it's unreachable: # spell check, gloss, garden, search, export and read-aloud all keep working and diff --git a/docker-compose.yml b/docker-compose.yml index 5ac8743..dbd7df3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -65,8 +65,23 @@ services: traefik.http.routers.petal.tls: "true" traefik.http.routers.petal.tls.certResolver: default traefik.http.routers.petal.service: petal - traefik.http.routers.petal.middlewares: compression@file,petal-headers + traefik.http.routers.petal.middlewares: compression@file,petal-headers,petal-auth traefik.http.services.petal.loadbalancer.server.port: "8080" + # INTERIM — delete this middleware and the petal-health router when Phase + # 16's OIDC login lands. Petal has no authentication of its own yet + # (StaticResolver hands every request the same local user), so without a + # gate at the edge anyone who finds the hostname can read and write + # documents and upload images. PETAL_BASIC_AUTH is a user:bcrypt-hash + # pair; see deploy/README.md for generating it. + traefik.http.middlewares.petal-auth.basicauth.users: ${PETAL_BASIC_AUTH:?set PETAL_BASIC_AUTH in .env} + # /api/health stays open on its own higher-priority router: a monitoring + # probe must not need a credential, and the endpoint carries no user data. + traefik.http.routers.petal-health.rule: Host(`${PETAL_HOST:-petal.parodia.dev}`) && Path(`/api/health`) + traefik.http.routers.petal-health.priority: "100" + traefik.http.routers.petal-health.entrypoints: web-secure + traefik.http.routers.petal-health.tls: "true" + traefik.http.routers.petal-health.tls.certResolver: default + traefik.http.routers.petal-health.service: petal # Petal is a private writing space: no framing, no sniffing, HSTS on. traefik.http.middlewares.petal-headers.headers.customresponseheaders.Content-Security-Policy: frame-ancestors 'self' traefik.http.middlewares.petal-headers.headers.customresponseheaders.Strict-Transport-Security: max-age=31536000; includeSubDomains