Files
homelab-infra/services/authelia-diff.sh
T
Micha 8095ab8b5d F-10: automated Authelia repo<->host drift check
New services/authelia-diff.sh compares the access_control: section of the
repo baseline against the live host configuration.yml. OIDC clients,
identity providers, and secret values stay out of scope by design.
Exit codes: 0 ok, 1 drift, 2 file missing, 3 section missing, 4 tool missing.

posture-check.sh gains check_authelia_config_drift, which calls the diff
script and reports drift as warning (not critical). SKIP_AUTHELIA_DRIFT=1
opts out; AUTHELIA_DIFF_SCRIPT overrides the path.

WORKFLOW.md gets a dedicated "Ausnahme: Authelia configuration.yml" section
analogous to the Traefik dynamic-config exception, with the mandatory
repo->host merge workflow and the env-variable contract.

Smoke-tested locally: identical files rc=0, ACL change rc=1 with proper
unified diff, non-ACL change (session.default_redirection_url) correctly
ignored.

Operator follow-up: set up a read-only repo mirror at
/mnt/user/services/homelab-infra/ so the check finds a current baseline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 09:52:16 +02:00

122 lines
3.5 KiB
Bash

#!/usr/bin/env bash
# Vergleicht die Repo-Baseline der Authelia-Konfiguration gegen die produktive
# Host-Datei. Bewusst nur fuer Sektionen, die laut Repo-Konvention auf Host
# und Repo identisch sein muessen (Default: access_control). OIDC-Clients,
# identity_providers und Secret-Werte bleiben hostseitig und werden nicht
# verglichen.
#
# Aufruf-Defaults siehe Variablen unten. Aufruf typischerweise:
# bash services/authelia-diff.sh
#
# Exit-Codes:
# 0 alle verglichenen Sektionen identisch
# 1 Drift festgestellt (Diff wird auf stdout ausgegeben)
# 2 Repo-Baseline oder Host-Datei fehlt
# 3 Sektion in mindestens einer Datei nicht gefunden
# 4 internes Werkzeug fehlt (awk/diff)
set -uo pipefail
AUTHELIA_REPO_BASELINE="${AUTHELIA_REPO_BASELINE:-/mnt/user/services/homelab-infra/security/authelia/configuration.yml}"
AUTHELIA_HOST_CONFIG="${AUTHELIA_HOST_CONFIG:-/mnt/user/appdata/authelia/config/configuration.yml}"
AUTHELIA_DIFF_SECTIONS="${AUTHELIA_DIFF_SECTIONS:-access_control}"
for cmd in awk diff; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "authelia-diff: missing required command '$cmd'" >&2
exit 4
fi
done
if [ ! -f "$AUTHELIA_REPO_BASELINE" ]; then
echo "authelia-diff: repo baseline not found: $AUTHELIA_REPO_BASELINE" >&2
exit 2
fi
if [ ! -f "$AUTHELIA_HOST_CONFIG" ]; then
echo "authelia-diff: host config not found: $AUTHELIA_HOST_CONFIG" >&2
exit 2
fi
# Extrahiert einen Top-Level-Block aus einer YAML-Datei.
# Block-Anfang: Zeile, die mit "<section>:" beginnt (kein Whitespace davor).
# Block-Ende: naechste Top-Level-Key-Zeile (`^[A-Za-z_][A-Za-z0-9_]*:`).
# Eingaberauschen wird entfernt: reine Kommentarzeilen, trailing whitespace,
# Leerzeilen.
extract_section() {
local file="$1"
local section="$2"
awk -v section="$section" '
BEGIN { inside = 0; found = 0 }
{
line = $0
sub(/[[:space:]]+$/, "", line)
}
# Top-Level-Key entdeckt
/^[A-Za-z_][A-Za-z0-9_]*:/ {
key = line
sub(/:.*$/, "", key)
if (key == section) {
inside = 1
found = 1
print line
next
} else if (inside == 1) {
inside = 0
}
}
inside == 1 {
# Kommentar- und Leerzeilen ignorieren
if (line ~ /^[[:space:]]*#/) next
if (line ~ /^[[:space:]]*$/) next
print line
}
END {
if (!found) exit 10
}
' "$file"
}
tmpdir="$(mktemp -d -t authelia-diff.XXXXXX)"
trap 'rm -rf "$tmpdir"' EXIT
overall_status=0
diff_output=""
missing_sections=""
IFS=',' read -r -a sections <<< "$AUTHELIA_DIFF_SECTIONS"
for section in "${sections[@]}"; do
section="${section// /}"
[ -z "$section" ] && continue
repo_file="$tmpdir/repo.$section"
host_file="$tmpdir/host.$section"
if ! extract_section "$AUTHELIA_REPO_BASELINE" "$section" > "$repo_file" 2>/dev/null; then
missing_sections="${missing_sections}${missing_sections:+, }$section (repo)"
continue
fi
if ! extract_section "$AUTHELIA_HOST_CONFIG" "$section" > "$host_file" 2>/dev/null; then
missing_sections="${missing_sections}${missing_sections:+, }$section (host)"
continue
fi
if ! diff_chunk="$(diff -u \
--label "repo:$section" "$repo_file" \
--label "host:$section" "$host_file")"; then
overall_status=1
diff_output="${diff_output}${diff_chunk}"$'\n'
fi
done
if [ -n "$missing_sections" ] && [ "$overall_status" -eq 0 ]; then
echo "authelia-diff: sections missing: $missing_sections" >&2
exit 3
fi
if [ "$overall_status" -ne 0 ]; then
printf '%s' "$diff_output"
exit 1
fi
exit 0