net-kingdom/sso-mfa/k8s/authelia
tegwick 2f1e6c3369
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 11s
Fix plus-address sign-in and finish NK-WP-0041
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e31f-2bcc-7051-a050-70d8cb2dfa49
2026-09-27 16:05:03 +02:00
..
tests Fix plus-address sign-in and finish NK-WP-0041 2026-09-27 16:05:03 +02:00
configmap.yaml Fix plus-address sign-in and finish NK-WP-0041 2026-09-27 16:05:03 +02:00
create-secrets.sh feat(sso-mfa): T05 SSO stack pivot — Keycloak → Authelia + LLDAP + KeyCape (NK-WP-0001-T05) 2026-03-19 08:31:51 +00:00
deployment.yaml Fix plus-address sign-in and finish NK-WP-0041 2026-09-27 16:05:03 +02:00
ingress.yaml Make Authelia certificate lifecycle explicit 2026-07-28 02:16:07 +02:00
pvc.yaml feat(sso-mfa): T05 SSO stack pivot — Keycloak → Authelia + LLDAP + KeyCape (NK-WP-0001-T05) 2026-03-19 08:31:51 +00:00
README.md Fix plus-address sign-in and finish NK-WP-0041 2026-09-27 16:05:03 +02:00

T05b — Authelia (Authentication Frontend)

Exercise status: unknown for the procedures in this runbook; no per-procedure successful-run receipt with operator attribution was established in the 2026-09-05 review. See procedure inventory.

Authelia is the password-authentication frontend for the net-kingdom SSO stack. It acts as an upstream OIDC provider for KeyCape: users are redirected here to enter their password; Authelia validates credentials against LLDAP and returns an authorization code to KeyCape, which then performs the MFA step via privacyIDEA.

Important: Authelia's access control policy is set to one_factor (password only). MFA is handled exclusively by KeyCape + privacyIDEA. Do not change this to two_factor.

Prerequisites

  • T05a complete (LLDAP is Running and healthy, application groups created)
  • bootstrap/gen-secrets.sh run and secrets/authelia/secrets.env populated in KeePassXC
  • kubectl configured with cluster access

Apply order

# 1. Create K8s Secret
cd sso-mfa/k8s/authelia
chmod +x create-secrets.sh
./create-secrets.sh

# 2. Apply manifests (order matters)
kubectl apply -f pvc.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f ingress.yaml

# 3. Wait for pod to be ready
#    The startup probe allows 90 s for the initial LLDAP connection.
kubectl rollout status deployment/authelia -n sso --timeout=120s

Configuration

All non-sensitive configuration is in configmap.yaml (mounted as configuration.yml). Sensitive values are injected via *_FILE environment variables pointing to Secret-mounted files (see deployment.yaml env section).

Key config points:

  • authentication_backend.ldap.url — points to LLDAP cluster-internal service
  • identity_providers.oidc.clients[0].redirect_uris — must match CP-NK-004 (kc.coulomb.social)
  • session.domain — set to parent domain coulomb.social so cookies are valid across both auth.coulomb.social and kc.coulomb.social
  • default_redirection_url — https://users.coulomb.social when the visitor opened the portal with no follow-up. A provided rd still wins
  • access_control.default_policy: one_factor — fleet default; MFA is not Authelia’s job
  • access_control.rules — domain allowlists by LLDAP group (see below)

Domain rules (app operator groups)

Authelia evaluates rules top-down; first match wins. A rule that matches domain but not subject is skipped, so non-members would fall through to default_policy and stay allowed unless a later domain-only deny rule exists.

Pattern used for activity-core public hosts (NK-WP-0021):

access_control:
  default_policy: one_factor
  rules:
    - domain: app.example.com
      subject: "group:app-operators"
      policy: one_factor
    - domain: app.example.com
      policy: deny
Host Required LLDAP group
activity.coulomb.social activity-core-operators
temporal.coulomb.social activity-core-operators

Membership: sso-mfa/k8s/lldap/OPERATOR-GROUPS.md. Do not set two_factor on these rules. Do not invent KeyCape OIDC clients for Traefik ForwardAuth apps — the edge already calls Authelia /api/verify.

Secrets managed

Secret name Keys Purpose
authelia-secrets jwt_secret Session JWT signing
session_secret Session cookie encryption
storage_encryption_key SQLite database encryption
ldap_password LDAP bind password (= LLDAP_LDAP_USER_PASS)
oidc_hmac_secret OIDC HMAC signing
oidc_issuer_private_key RSA-2048 private key for OIDC token signing
keycape_client_secret_hash Bcrypt hash of AUTHELIA_KEYCAPE_CLIENT_SECRET

create-secrets.sh reads plaintext values from secrets/authelia/secrets.env and secrets/lldap/secrets.env. It generates the bcrypt hash on the fly (requires python3+bcrypt or apache2-utils). The RSA OIDC private key is generated automatically if AUTHELIA_OIDC_PRIVATE_KEY_FILE is not set.

Storage

authelia-data PVC (1 Gi, ReadWriteOnce) holds:

  • db.sqlite3 — SQLite database (user sessions, regulation data)
  • notification.txt — notification log (filesystem notifier)

Back this PVC up alongside the LLDAP PVC.

Verify

# Pod status
kubectl get pod -n sso -l app.kubernetes.io/name=authelia

# Health check
kubectl run -n sso --rm -it auth-test --image=busybox --restart=Never \
  -- wget -qO- http://authelia.sso.svc.cluster.local:9091/api/health

# OIDC discovery (should return issuer + endpoints)
curl -s https://auth.coulomb.social/.well-known/openid-configuration | jq .

4.39 compatibility and regression check

KeyCape needs preferred_username in the signed upstream ID token for its directory and MFA lookup. Authelia 4.39 requires the explicit keycape claims policy in configmap.yaml; deploy it together with the pinned image. KeyCape's in-cluster token requests must also carry the public HTTPS forwarded scheme and host (KeyCape 3b0446e or later). Neither a health check nor an authorization redirect exercises these requirements.

Run the isolated token regression with an Authelia 4.39.28 binary and Python requests, PyYAML, and cryptography installed:

python3 sso-mfa/k8s/authelia/tests/probe_claims.py --authelia-bin /path/to/authelia

It uses disposable local users, keys and SQLite databases, checks real signed authorization-code tokens with and without the policy, and terminates the scratch processes. It uses a file backend, so it does not prove LDAP email lookup. Live acceptance also requires a fresh plus-addressed email sign-in through KeyCape to both Vergabe and the account portal, with successful callback and token issuance evidence.

Before a 4.38 → 4.39 rollout, stop Authelia and preserve the SQLite database and any sidecar files. Keep the previous ConfigMap and image reference too. Rollback requires stopping 4.39 and restoring all three: database, ConfigMap, and image. The migrated database and 4.39 claims policy cannot be used by 4.38. See NK-WP-0041 for the exercised rollout and backup receipt.