fix(restore): isolate authelia runtime config mount

This commit is contained in:
2026-06-03 07:57:57 +02:00
parent 541c7be853
commit cd4dd178ed
4 changed files with 33 additions and 26 deletions
+2 -2
View File
@@ -30,7 +30,7 @@ services:
condition: service_healthy condition: service_healthy
command: command:
- authelia - authelia
- --config=/config/configuration.restoretest.yml - --config=/config/configuration.yml
environment: environment:
TZ: Europe/Berlin TZ: Europe/Berlin
# Wegwerf-Secrets nur fuer den isolierten Smoke. Niemals produktive # Wegwerf-Secrets nur fuer den isolierten Smoke. Niemals produktive
@@ -47,7 +47,7 @@ services:
# SMTP-Versand ausgeloest wird. # SMTP-Versand ausgeloest wird.
AUTHELIA__SERVER__ADDRESS: tcp://0.0.0.0:9091 AUTHELIA__SERVER__ADDRESS: tcp://0.0.0.0:9091
volumes: volumes:
- /mnt/user/backups/restore-lab/authelia/config:/config - /mnt/user/backups/restore-lab/authelia/test-config:/config
ports: ports:
# nur 127.0.0.1, keine Public-Route, keine Traefik-Labels # nur 127.0.0.1, keine Public-Route, keine Traefik-Labels
- "127.0.0.1:19091:9091" - "127.0.0.1:19091:9091"
+7 -6
View File
@@ -23,10 +23,11 @@ Bewusst **nicht** Teil dieses Tests:
- Restore-Lab: `/mnt/user/backups/restore-lab/authelia` - Restore-Lab: `/mnt/user/backups/restore-lab/authelia`
- Testdatenpfade: - Testdatenpfade:
- `/mnt/user/backups/restore-lab/authelia/config` (restaurierte `configuration.yml.original` + erzeugte `configuration.restoretest.yml`) - `/mnt/user/backups/restore-lab/authelia/config` (restaurierte Originalkonfiguration + `configuration.yml.original`)
- `/mnt/user/backups/restore-lab/authelia/test-config` (Runtime-Mount mit sanitizter `configuration.yml`)
- `/mnt/user/backups/restore-lab/authelia/postgres` (Test-Postgres-Datadir) - `/mnt/user/backups/restore-lab/authelia/postgres` (Test-Postgres-Datadir)
- `/mnt/user/backups/restore-lab/authelia/dumps/latest/postgresql17-authelia.dump` (falls extrahiert) - `/mnt/user/backups/restore-lab/authelia/dumps/latest/postgresql17-authelia.dump` (falls extrahiert)
- `/mnt/user/backups/restore-lab/authelia/config/notifier/notifications.txt` (Filesystem-Notifier-Ausgabe) - `/mnt/user/backups/restore-lab/authelia/test-config/notifier/notifications.txt` (Filesystem-Notifier-Ausgabe)
- Testcontainer: - Testcontainer:
- `restoretest-authelia` (Image-Pin wie Produktion) - `restoretest-authelia` (Image-Pin wie Produktion)
- `restoretest-authelia-postgres` (postgres:18.4, gleiche Major wie shared Postgres) - `restoretest-authelia-postgres` (postgres:18.4, gleiche Major wie shared Postgres)
@@ -37,8 +38,8 @@ Bewusst **nicht** Teil dieses Tests:
- produktive Pfade `/mnt/user/appdata/authelia/*` werden **nicht** beschrieben - produktive Pfade `/mnt/user/appdata/authelia/*` werden **nicht** beschrieben
- produktive Secret-Dateien `/mnt/user/appdata/secrets/authelia_*.txt` werden **nicht** gemountet - produktive Secret-Dateien `/mnt/user/appdata/secrets/authelia_*.txt` werden **nicht** gemountet
- produktive shared PostgreSQL 18 wird **nicht** angesprochen (`configuration.restoretest.yml` zwingt `storage` auf Test-Postgres) - produktive shared PostgreSQL 18 wird **nicht** angesprochen (`test-config/configuration.yml` zwingt `storage` auf Test-Postgres)
- echter SMTP-Versand wird **nicht** ausgeloest (`configuration.restoretest.yml` zwingt `notifier` auf Filesystem) - echter SMTP-Versand wird **nicht** ausgeloest (`test-config/configuration.yml` zwingt `notifier` auf Filesystem)
- produktive Domain `auth.kaleschke.info` wird **nicht** uebernommen - produktive Domain `auth.kaleschke.info` wird **nicht** uebernommen
- Testcontainer publishen nur auf `127.0.0.1`, keine LAN-/Tailscale-Bindung - Testcontainer publishen nur auf `127.0.0.1`, keine LAN-/Tailscale-Bindung
- Borg-Passphrase wird aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` gelesen und nirgendwo geloggt - Borg-Passphrase wird aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` gelesen und nirgendwo geloggt
@@ -48,10 +49,10 @@ Bewusst **nicht** Teil dieses Tests:
1. Restore-Lab-Pfade leer anlegen 1. Restore-Lab-Pfade leer anlegen
2. `local/appdata/authelia/config` aus dem aktuellsten Borg-Archiv extrahieren 2. `local/appdata/authelia/config` aus dem aktuellsten Borg-Archiv extrahieren
3. optional `local/borg-dumps/latest/postgresql17-authelia.dump` extrahieren; wenn nicht im Archiv vorhanden, weiter ohne DB-Restore 3. optional `local/borg-dumps/latest/postgresql17-authelia.dump` extrahieren; wenn nicht im Archiv vorhanden, weiter ohne DB-Restore
4. `configuration.restoretest.yml` aus der restaurierten `configuration.yml` erzeugen: produktive `storage`/`notifier`/`session`/JWT-Bloecke entfernen und Test-Werte anhaengen 4. `test-config/configuration.yml` aus der restaurierten `configuration.yml` erzeugen: produktive `storage`/`notifier`/`session`/JWT-Bloecke entfernen und Test-Werte anhaengen
5. Test-Postgres mit `ops/restore-tests/authelia-compose.test.yml` hochfahren 5. Test-Postgres mit `ops/restore-tests/authelia-compose.test.yml` hochfahren
6. optional Dump per `pg_restore -Fc --clean --if-exists --no-owner --no-privileges` einspielen (mit transientem Retry wie im Immich-/Paperless-Test) 6. optional Dump per `pg_restore -Fc --clean --if-exists --no-owner --no-privileges` einspielen (mit transientem Retry wie im Immich-/Paperless-Test)
7. `authelia config validate` gegen `configuration.restoretest.yml` laufen lassen 7. `authelia config validate` gegen `test-config/configuration.yml` laufen lassen
8. `restoretest-authelia` starten und HTTP-Health `http://127.0.0.1:19091/api/health` pollen 8. `restoretest-authelia` starten und HTTP-Health `http://127.0.0.1:19091/api/health` pollen
9. Report unter `/mnt/user/backups/restore-reports/authelia-YYYY-MM-DD.md` schreiben 9. Report unter `/mnt/user/backups/restore-reports/authelia-YYYY-MM-DD.md` schreiben
10. Testcontainer stoppen und Restore-Lab bereinigen (`--keep-data` ueberschreibt) 10. Testcontainer stoppen und Restore-Lab bereinigen (`--keep-data` ueberschreibt)
+23 -17
View File
@@ -31,6 +31,8 @@ for arg in "$@"; do
done done
RESTORE_ROOT="/mnt/user/backups/restore-lab/authelia" RESTORE_ROOT="/mnt/user/backups/restore-lab/authelia"
RESTORED_CONFIG_DIR="$RESTORE_ROOT/config"
TEST_CONFIG_DIR="$RESTORE_ROOT/test-config"
REPORT_ROOT="/mnt/user/backups/restore-reports" REPORT_ROOT="/mnt/user/backups/restore-reports"
EXTRACT_DIR="$BORG_RESTORE_HOST_ROOT/authelia-extract" EXTRACT_DIR="$BORG_RESTORE_HOST_ROOT/authelia-extract"
COMPOSE_FILE="$SCRIPT_DIR/authelia-compose.test.yml" COMPOSE_FILE="$SCRIPT_DIR/authelia-compose.test.yml"
@@ -49,13 +51,13 @@ Planned isolation:
- Test-Postgres: postgres:18.4 mit Wegwerf-Credentials - Test-Postgres: postgres:18.4 mit Wegwerf-Credentials
- Test-Authelia: authelia/authelia:4.39.20 (Image-Pin wie Produktion) - Test-Authelia: authelia/authelia:4.39.20 (Image-Pin wie Produktion)
- Wegwerf-Secrets ausschliesslich im Test-Compose - Wegwerf-Secrets ausschliesslich im Test-Compose
- configuration.restoretest.yml wird im Restore-Lab erzeugt: - test-config/configuration.yml wird im Restore-Lab erzeugt:
* storage -> Test-Postgres (kein produktives Postgres erreicht) * storage -> Test-Postgres (kein produktives Postgres erreicht)
* notifier -> Filesystem (KEIN SMTP-Versand) * notifier -> Filesystem (KEIN SMTP-Versand)
* session -> lokaler Smoke ohne produktive Session-Secrets * session -> lokaler Smoke ohne produktive Session-Secrets
- Test endpoint: 127.0.0.1:19091/api/health (no Traefik, no public domain) - Test endpoint: 127.0.0.1:19091/api/health (no Traefik, no public domain)
Smoke-Test: Smoke-Test:
- authelia config validate gegen configuration.restoretest.yml - authelia config validate gegen test-config/configuration.yml
- HTTP 200 von /api/health - HTTP 200 von /api/health
EOF EOF
exit 0 exit 0
@@ -82,7 +84,7 @@ cleanup() {
trap cleanup EXIT trap cleanup EXIT
rm -rf "$EXTRACT_DIR" "$RESTORE_ROOT" rm -rf "$EXTRACT_DIR" "$RESTORE_ROOT"
mkdir -p "$RESTORE_ROOT/config" "$RESTORE_ROOT/postgres" "$RESTORE_ROOT/dumps/latest" "$RESTORE_ROOT/notifier" mkdir -p "$RESTORED_CONFIG_DIR" "$TEST_CONFIG_DIR" "$RESTORE_ROOT/postgres" "$RESTORE_ROOT/dumps/latest"
archive="$(latest_archive_name)" archive="$(latest_archive_name)"
repo="$(borg_repo_url)" repo="$(borg_repo_url)"
@@ -98,7 +100,7 @@ if [ ! -d "$EXTRACT_DIR/local/appdata/authelia/config" ]; then
echo "Authelia config path missing in Borg archive" >&2 echo "Authelia config path missing in Borg archive" >&2
exit 1 exit 1
fi fi
cp -a "$EXTRACT_DIR/local/appdata/authelia/config/." "$RESTORE_ROOT/config/" cp -a "$EXTRACT_DIR/local/appdata/authelia/config/." "$RESTORED_CONFIG_DIR/"
# Stufe 2: optionalen Postgres-Dump extrahieren und ggf. einspielen # Stufe 2: optionalen Postgres-Dump extrahieren und ggf. einspielen
dump_available=0 dump_available=0
@@ -114,15 +116,19 @@ fi
# Wir entfernen produktive Top-Level-Bloecke, die im Test andere Backends # Wir entfernen produktive Top-Level-Bloecke, die im Test andere Backends
# brauchen, und haengen danach Test-Definitionen an. So verlassen wir uns # brauchen, und haengen danach Test-Definitionen an. So verlassen wir uns
# nicht darauf, dass ein Overlay alte Map-Keys wie notifier.smtp loescht. # nicht darauf, dass ein Overlay alte Map-Keys wie notifier.smtp loescht.
CONFIG_FILE="$RESTORE_ROOT/config/configuration.yml" ORIGINAL_CONFIG_FILE="$RESTORED_CONFIG_DIR/configuration.yml"
TEST_CONFIG_FILE="$RESTORE_ROOT/config/configuration.restoretest.yml" TEST_CONFIG_FILE="$TEST_CONFIG_DIR/configuration.yml"
if [ ! -f "$CONFIG_FILE" ]; then if [ ! -f "$ORIGINAL_CONFIG_FILE" ]; then
echo "configuration.yml missing in restored config dir" >&2 echo "configuration.yml missing in restored config dir" >&2
exit 1 exit 1
fi fi
# Sichere Originalkopie fuer Diff/Diagnose # Kopiere alle Begleitdateien (z. B. users_database.yml) in einen separaten
cp "$CONFIG_FILE" "$CONFIG_FILE.original" # Runtime-Mount. Die produktive configuration.yml selbst wird dort durch die
# sanitizte Testkonfiguration ersetzt, damit Authelia keine Default-Config mit
# produktivem notifier.smtp nachladen kann.
cp -a "$RESTORED_CONFIG_DIR/." "$TEST_CONFIG_DIR/"
cp "$ORIGINAL_CONFIG_FILE" "$RESTORED_CONFIG_DIR/configuration.yml.original"
# Entferne produktive Blocks, die der Restore-Smoke bewusst ersetzt. # Entferne produktive Blocks, die der Restore-Smoke bewusst ersetzt.
awk ' awk '
@@ -132,7 +138,7 @@ awk '
skip = (key == "storage" || key == "notifier" || key == "session" || key == "identity_validation" || key == "jwt_secret") skip = (key == "storage" || key == "notifier" || key == "session" || key == "identity_validation" || key == "jwt_secret")
} }
!skip { print } !skip { print }
' "$CONFIG_FILE" > "$TEST_CONFIG_FILE" ' "$ORIGINAL_CONFIG_FILE" > "$TEST_CONFIG_FILE"
cat >> "$TEST_CONFIG_FILE" <<'YAML' cat >> "$TEST_CONFIG_FILE" <<'YAML'
@@ -167,8 +173,8 @@ identity_validation:
jwt_algorithm: HS256 jwt_algorithm: HS256
YAML YAML
mkdir -p "$RESTORE_ROOT/config/notifier" mkdir -p "$TEST_CONFIG_DIR/notifier"
chmod -R a+rwX "$RESTORE_ROOT/config/notifier" chmod -R a+rwX "$TEST_CONFIG_DIR/notifier"
# Stufe 4: Test-Postgres hochfahren # Stufe 4: Test-Postgres hochfahren
docker compose -f "$COMPOSE_FILE" up -d restoretest-authelia-postgres >/dev/null docker compose -f "$COMPOSE_FILE" up -d restoretest-authelia-postgres >/dev/null
@@ -209,17 +215,17 @@ if ! docker run --rm \
-e AUTHELIA_STORAGE_ENCRYPTION_KEY=restoretest-authelia-storage-enc-key-placeholder-32 \ -e AUTHELIA_STORAGE_ENCRYPTION_KEY=restoretest-authelia-storage-enc-key-placeholder-32 \
-e AUTHELIA_STORAGE_POSTGRES_PASSWORD=restoretest-authelia-db \ -e AUTHELIA_STORAGE_POSTGRES_PASSWORD=restoretest-authelia-db \
-e AUTHELIA_NOTIFIER_SMTP_PASSWORD=restoretest-authelia-smtp-placeholder \ -e AUTHELIA_NOTIFIER_SMTP_PASSWORD=restoretest-authelia-smtp-placeholder \
-v "$RESTORE_ROOT/config:/config" \ -v "$TEST_CONFIG_DIR:/config" \
authelia/authelia:4.39.20@sha256:1b363e9279e742397966333f364e0876ae02bf5c876de73e83af6d48c57ff51b \ authelia/authelia:4.39.20@sha256:1b363e9279e742397966333f364e0876ae02bf5c876de73e83af6d48c57ff51b \
authelia config validate --config /config/configuration.restoretest.yml \ authelia config validate --config /config/configuration.yml \
>/tmp/authelia-validate.log 2>&1; then >/tmp/authelia-validate.log 2>&1; then
validate_status="failed" validate_status="failed"
cat /tmp/authelia-validate.log >&2 cat /tmp/authelia-validate.log >&2
exit 1 exit 1
fi fi
# Stufe 7: Authelia-Container starten. Das Compose nutzt die sanitizte # Stufe 7: Authelia-Container starten. Das Compose nutzt test-config als
# configuration.restoretest.yml mit isolierten Test-Backends. # /config-Mount mit isolierten Test-Backends.
docker compose -f "$COMPOSE_FILE" up -d restoretest-authelia >/dev/null docker compose -f "$COMPOSE_FILE" up -d restoretest-authelia >/dev/null
http_status="" http_status=""
@@ -256,7 +262,7 @@ write_report "$REPORT_FILE" <<EOF
- Borg extract of config: \`ok\` - Borg extract of config: \`ok\`
- Borg extract of dump: \`$dump_status\` - Borg extract of dump: \`$dump_status\`
- configuration.yml present: \`ok\` - configuration.yml present: \`ok\`
- configuration.restoretest.yml written: \`ok\` - test runtime configuration.yml written: \`ok\`
- \`authelia config validate\`: \`$validate_status\` - \`authelia config validate\`: \`$validate_status\`
- HTTP /api/health status: \`$http_status\` - HTTP /api/health status: \`$http_status\`
+1 -1
View File
@@ -59,7 +59,7 @@ Minimal erwartet im Report:
| `config validate` failt mit `notifier` Block | Produktive Notifier-Map wurde nicht sauber aus der Testkonfig entfernt | Originalkonfig pruefen unter `restore-lab/authelia/config/configuration.yml.original`; Strip-Liste oder Test-Block im Skript anpassen | | `config validate` failt mit `notifier` Block | Produktive Notifier-Map wurde nicht sauber aus der Testkonfig entfernt | Originalkonfig pruefen unter `restore-lab/authelia/config/configuration.yml.original`; Strip-Liste oder Test-Block im Skript anpassen |
| `config validate` failt mit `session.domain` | aelteres/neueres Schema | Test-`session:`-Block an reales Authelia-Schema anpassen | | `config validate` failt mit `session.domain` | aelteres/neueres Schema | Test-`session:`-Block an reales Authelia-Schema anpassen |
| HTTP-Timeout 120 s | Authelia haengt in Postgres-Schema-Migration | `docker logs --tail 200 restoretest-authelia` lesen, ggf. Wartezeit erhoehen | | HTTP-Timeout 120 s | Authelia haengt in Postgres-Schema-Migration | `docker logs --tail 200 restoretest-authelia` lesen, ggf. Wartezeit erhoehen |
| SMTP-Connect im Log | Produktiver Notifier-Block wurde nicht sauber entfernt | `configuration.restoretest.yml` auf `smtp:` pruefen und Strip-Liste haerten | | SMTP-Connect im Log | Produktiver Notifier-Block wurde nicht sauber entfernt | `test-config/configuration.yml` auf `smtp:` pruefen und Strip-Liste haerten |
| `pg_restore` failt mit Schema-Drift | Dump aus 17er-Cluster, 18er Image braucht andere Initialisierung | Schritt als optional dokumentiert; Smoke ohne Dump akzeptieren und Issue nachverfolgen | | `pg_restore` failt mit Schema-Drift | Dump aus 17er-Cluster, 18er Image braucht andere Initialisierung | Schritt als optional dokumentiert; Smoke ohne Dump akzeptieren und Issue nachverfolgen |
## Cleanup ## Cleanup