From d9f151a1c3e6e1b881c07f2232858c2c14107c60 Mon Sep 17 00:00:00 2001 From: Bot Date: Fri, 11 Sep 2026 22:40:36 -0700 Subject: [PATCH] feat(traefik): migrate keepalived password to manifest-driven Pattern C provisioning Root cause of the 2026-09-12 VIP instability incident: traefik/traefik.env was committed to git with a literal "***REDACTED***" placeholder as KEEPALIVED_PASSWORD. Every git-guard resync/checkout restored that broken value onto disk, diverging from keepalived-backup's stale-but-correct in-memory value and causing a continuous VRRP auth failure + VIP flapping. - Adds TRAEFIK_KEEPALIVED_PASSWORD (from_secret: traefik_keepalived_password) to provision-secrets environment block - Replaces the traefik legacy case-entry (previously grouped with maintenance|media|unifi|... as "no Docker secrets, host .env only") with a call to deploy/provision-stack.py traefik, matching the ai) stack's manifest-driven pattern - traefik/traefik.env is now rendered fresh from traefik/traefik.env.template on every provisioning run and never committed to git again Diffed $${ occurrence count against previous version before committing, per this file's own 2026-08-26 incident note. --- .woodpecker/deploy.yml | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/.woodpecker/deploy.yml b/.woodpecker/deploy.yml index b4bb40f..b9f679d 100644 --- a/.woodpecker/deploy.yml +++ b/.woodpecker/deploy.yml @@ -70,6 +70,16 @@ when: # tolerating a failing stack-ps in verify (|| true) so a genuinely missing # stack produces the designed WARNING instead of killing the step. This # hazard was first flagged in July (PR #3, closed unmerged). +# +# 2026-09-12 MIGRATION: traefik moved from Pattern B (secrets hand-typed +# into the host-only traefik.env, no CI involvement at all) to the same +# manifest-driven provisioning as ai) below. Root cause of the migration: +# traefik.env had been accidentally committed to git with a literal +# "***REDACTED***" placeholder as KEEPALIVED_PASSWORD; every git-guard +# resync/checkout silently restored that broken value, which diverged from +# keepalived-backup's stale-but-correct in-memory value and produced a +# continuous VRRP auth failure ("received an invalid passwd!") and VIP +# instability. The manifest-driven path never commits the rendered file. # ───────────────────────────────────────────────────────────────────────────── steps: @@ -216,6 +226,11 @@ steps: from_secret: flowagent_azure_tenant_id FLOWAGENT_AZURE_CLIENT_SECRET: from_secret: flowagent_azure_client_secret + # ── traefik stack (manifest-driven — secrets/secrets-map.yaml + + # traefik/traefik.env.template + deploy/provision-stack.py). + # Migrated 2026-09-12; see header note above for root cause. ── + TRAEFIK_KEEPALIVED_PASSWORD: + from_secret: traefik_keepalived_password commands: - apk add --no-cache openssh-client python3 py3-yaml - mkdir -p ~/.ssh @@ -243,8 +258,19 @@ steps: for STACK in $ALL_STACKS; do echo "Provisioning: $STACK" case "$STACK" in - maintenance|media|unifi|guacamole|security|auth|traefik|meshcentral|ddm) + maintenance|media|unifi|guacamole|security|auth|meshcentral|ddm) echo " No Docker secrets for $STACK — secrets in host .env";; + traefik) + # MIGRATED (2026-09-12) to manifest-driven provisioning after + # discovering traefik.env was committed to git with a literal + # "***REDACTED***" placeholder as KEEPALIVED_PASSWORD — every + # git-guard resync/checkout restored the broken value, causing + # VRRP auth mismatch between keepalived-master/-backup and VIP + # instability. All logic lives in deploy/provision-stack.py; + # the authoritative key list lives in + # traefik/traefik.env.template; the mapping lives in + # secrets/secrets-map.yaml. This case is intentionally one line. + python3 deploy/provision-stack.py traefik;; immich) ssh -o StrictHostKeyChecking=no root@$${SWARM_MANAGER_IP} "source /tmp/cs.sh create_or_update_secret 'immich_db_password' '$${IMMICH_DB_PASSWORD}'