8095ab8b5d
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>
122 lines
3.5 KiB
Bash
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
|