diff --git a/traefik/traefik.env.template b/traefik/traefik.env.template new file mode 100644 index 0000000..b95d019 --- /dev/null +++ b/traefik/traefik.env.template @@ -0,0 +1,39 @@ +# ───────────────────────────────────────────────────────────────────────── +# traefik.env.template — AUTHORITATIVE template for traefik/traefik.env +# (rendered by deploy/provision-stack.py per secrets/secrets-map.yaml) +# +# - This file IS the complete key list for traefik.env. The whole file is +# rendered on every provisioning run — no line surgery, so a key can +# never silently go missing. +# - Non-secret config lives here as LITERAL values (visible, reviewable). +# - Secret values are dollar-brace placeholders resolved from the CI env +# (Woodpecker from_secret vars) at provisioning time. provision-stack.py +# FAILS HARD if any placeholder is missing/empty. +# - The rendered traefik/traefik.env exists only on the host (gitignored). +# - Rendered by provision-stack.py, NOT Woodpecker's yaml preprocessor — +# single-dollar placeholders are safe here (deploy.yml's double-dollar +# rule does NOT apply to this file). +# +# Consumed by traefik/traefik.yaml (KEEPALIVED_PASSWORD, KEEPALIVED_VIRTUAL_IPS). +# DOMAIN comes from deploy/global.env, not here. +# +# ⚠️ HIGH RISK: Traefik is the entry point for all homelab services, and the +# Keepalived VIP (192.168.4.30) depends on this stack. KEEPALIVED_PASSWORD +# MUST be identical across keepalived-master and keepalived-backup — both +# consume this same rendered value — or VRRP auth fails and the VIP +# becomes unstable (this is exactly what the 2026-09-12 incident was: +# a committed literal "***REDACTED***" placeholder in git, restored every +# time the file was deleted/re-synced, diverging from -backup's stale but +# correct in-memory value). +# +# Classic VRRP simple-auth is silently truncated to 8 characters by +# keepalived itself. Set TRAEFIK_KEEPALIVED_PASSWORD in Woodpecker to a +# value 8 characters or fewer (or accept that only the first 8 chars are +# actually significant) so the effective negotiated value is unambiguous. +# ───────────────────────────────────────────────────────────────────────── + +# ── Keepalived (non-secret config) ──────────────────────────────────────── +KEEPALIVED_VIRTUAL_IPS="#PYTHON2BASH:['192.168.4.30']" + +# ── Keepalived (secret) ──────────────────────────────────────────────────── +KEEPALIVED_PASSWORD=${TRAEFIK_KEEPALIVED_PASSWORD}