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.
This commit is contained in:
prosolis
2026-07-26 23:21:43 -07:00
parent 2363ef2d37
commit 5b221cc7a3
2 changed files with 24 additions and 1 deletions
+8
View File
@@ -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
+16 -1
View File
@@ -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