authentik: boot-persistent quadlet conversion with deploy handler

The compose-era authentik trio died at every boot (rootless podman does not
auto-start bare containers). Convert the trio to Quadlet units installed by
deploy-remote.sh into ~/.config/containers/systemd/, enabled + WantedBy=
default.target:

- 3 unit files under dankclaw/authentik/quadlet/ (postgres -> server -> worker
  ordering via Wants=/After=; secrets stay in the git-ignored host .env,
  expanded by systemd at start time exactly like the gitea units)
- deploy_authentik() handler: install units, daemon-reload, retire stale
  compose-era containers only while units are inactive, enable once, then
  start in dependency order with podman-health gates between stages
- compose.yaml: authentik service definitions retired to a comment pointing at
  the quadlet dir (same pattern as searxng/open-webui/gitea/caddy)
- tests/authentik.bats rewritten from compose assertions to unit-file
  contract assertions (images, Exec roles, ordering, loopback-only publish,
  pg_isready probe, secret hygiene, dispatch routing)
This commit is contained in:
Frank committed 2026-08-28 03:09:19 -07:00
1 parent 5829a0bfe8
commit 914e251ebc
6 files changed
+211 -120

No files matched your search

+39 -57
View File
@@ -19,86 +19,68 @@ COMPOSE="$REPO_ROOT/dankclaw/compose.yaml"
CADDYFILE="$REPO_ROOT/dankclaw/Caddyfile"
ENV_EXAMPLE="$REPO_ROOT/dankclaw/.env.example"
# --- Compose: the 3-service stack ---
# --- Quadlet units: the 3-service stack ------------------------------------
# The trio left compose: they are boot-persistent Quadlet units installed to
# ~/.config/containers/systemd/ by deploy-remote.sh (deploy_authentik).
@test "compose defines authentik-postgres" {
grep -qE '^ authentik-postgres:' "$COMPOSE"
}
UNIT_DIR="$REPO_ROOT/dankclaw/authentik/quadlet"
@test "compose defines authentik-server" {
grep -qE '^ authentik-server:' "$COMPOSE"
}
@test "compose defines authentik-worker" {
grep -qE '^ authentik-worker:' "$COMPOSE"
@test "the three authentik quadlet units exist" {
for unit in authentik-postgres authentik-server authentik-worker; do
[ -f "$UNIT_DIR/$unit.container" ]
done
}
@test "authentik-postgres uses the official postgres image" {
grep -q 'image: docker.io/library/postgres:' "$COMPOSE"
grep -q 'Image=docker.io/library/postgres:16-alpine' "$UNIT_DIR/authentik-postgres.container"
}
@test "authentik-server uses the official goauthentik image" {
grep -q 'image: ghcr.io/goauthentik/server:' "$COMPOSE"
@test "server and worker use the official goauthentik image" {
grep -q 'Image=ghcr.io/goauthentik/server:' "$UNIT_DIR/authentik-server.container"
grep -q 'Image=ghcr.io/goauthentik/server:' "$UNIT_DIR/authentik-worker.container"
}
@test "authentik-server runs the 'server' command" {
# The same image runs both server and worker; the command selects the role.
grep -qE '^\s+command:\s*server' "$COMPOSE"
@test "the same image runs both roles; the Exec command selects the role" {
grep -qE '^Exec=server$' "$UNIT_DIR/authentik-server.container"
grep -qE '^Exec=worker$' "$UNIT_DIR/authentik-worker.container"
}
@test "authentik-worker runs the 'worker' command" {
grep -qE '^\s+command:\s*worker' "$COMPOSE"
@test "startup order is postgres -> server -> worker via unit ordering" {
grep -q 'After=network.target authentik-postgres.service' "$UNIT_DIR/authentik-server.container"
grep -q 'Wants=authentik-postgres.service' "$UNIT_DIR/authentik-server.container"
grep -q 'Wants=authentik-server.service' "$UNIT_DIR/authentik-worker.container"
}
@test "authentik-server depends on a healthy authentik-postgres" {
local block
block=$(awk -v s="^ authentik-server:\$" '
$0 ~ s { inblk = 1; next }
inblk && /^ [a-z]/ { inblk = 0 }
inblk' "$COMPOSE")
echo "$block" | grep -q 'condition: service_healthy'
}
@test "authentik-worker depends on a healthy authentik-postgres" {
local block
block=$(awk -v s="^ authentik-worker:\$" '
$0 ~ s { inblk = 1; next }
inblk && /^ [a-z]/ { inblk = 0 }
inblk' "$COMPOSE")
echo "$block" | grep -q 'condition: service_healthy'
}
@test "authentik-server exposes port 9000" {
local block
block=$(awk -v s="^ authentik-server:\$" '
$0 ~ s { inblk = 1; next }
inblk && /^ [a-z]/ { inblk = 0 }
inblk' "$COMPOSE")
echo "$block" | grep -qE '"9000:9000"'
@test "the server publishes 9000 on loopback only; LAN reaches it through Caddy" {
grep -q 'PublishPort=127.0.0.1:9000:9000' "$UNIT_DIR/authentik-server.container"
}
@test "authentik-postgres healthcheck uses pg_isready" {
local block
block=$(awk -v s="^ authentik-postgres:\$" '
$0 ~ s { inblk = 1; next }
inblk && /^ [a-z]/ { inblk = 0 }
inblk' "$COMPOSE")
echo "$block" | grep -q 'pg_isready'
grep -q 'HealthCmd=.*pg_isready' "$UNIT_DIR/authentik-postgres.container"
}
# --- Compose: environment (secrets from .env) ---
# --- Units: environment (secrets stay in the host .env, expanded at start) --
@test "authentik services consume AUTHENTIK_PG_PASS from .env" {
grep -q 'AUTHENTIK_PG_PASS' "$COMPOSE"
@test "the units consume AUTHENTIK_PG_PASS via systemd expansion, not literals" {
grep -q 'POSTGRES_PASSWORD=${AUTHENTIK_PG_PASS}' "$UNIT_DIR/authentik-postgres.container"
grep -q 'PASSWORD=${AUTHENTIK_PG_PASS}' "$UNIT_DIR/authentik-server.container"
grep -q 'PASSWORD=${AUTHENTIK_PG_PASS}' "$UNIT_DIR/authentik-worker.container"
}
@test "authentik services consume AUTHENTIK_SECRET_KEY from .env" {
grep -q 'AUTHENTIK_SECRET_KEY' "$COMPOSE"
@test "the units consume AUTHENTIK_SECRET_KEY via systemd expansion, not literals" {
for unit in authentik-server authentik-worker; do
grep -q 'AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}' "$UNIT_DIR/$unit.container"
done
}
@test "no real authentik secret is committed in compose" {
# The compose must reference the secret via ${VAR}, never a literal.
! grep -qE 'AUTHENTIK_SECRET_KEY:\s*["\x27]?\w{16,}' "$COMPOSE"
@test "no secret material is committed in the quadlet units" {
! grep -rqE 'SECRET_KEY=[^$]' "$UNIT_DIR"
}
@test "deploy-remote.sh routes the authentik trio to the quadlet handler, not compose up" {
grep -q 'authentik-postgres|authentik-server|authentik-worker)' "$REPO_ROOT/scripts/deploy-remote.sh"
grep -q 'deploy_authentik()' "$REPO_ROOT/scripts/deploy-remote.sh"
! grep -qE '^[[:space:]]+comfyui\|.*authentik' "$REPO_ROOT/scripts/deploy-remote.sh"
}
# --- Caddyfile: the auth vhost ---