- 31 new managed skills (anubis/arr/authentik/hermes/freetoken/podman/llm-swap runbooks etc.) now tracked. - Removed superseded skills: omp_harness_config, peon_ping_integration (replaced by user-authored peon-ping-* managed skills), rocmfp4-strix-benchmark-orchestration. - New memory-root skills: niri_compositor_ops, omp_git_workflow, omp_model_config, quadlet-media-migration, homelab-service-triage, hermes-dankclaw-ops, frank-default-large-ops, caddy-auth-dns-playbook.
2.9 KiB
2.9 KiB
name, description
| name | description |
|---|---|
| anubis-guard-chain-flip | Runbook for moving a Caddy-fronted service behind the Anubis + Authentik embedded-outpost guard chain on Dankclaw (provider-first ordering, validate gate, verification probes). |
Anubis + Authentik guard-chain flip (Dankclaw)
Move a public vhost from reverse_proxy <app>:<port> onto: client → TLS edge → Anubis challenge (:8923) → Authentik embedded outpost decision → return to internal http://:8017 listener → Host-matched handler → real upstream.
Order (never reorder)
- Provider first. Create the outpost ProxyProvider before touching routes, or authed traffic hits the outpost error page instead of the app:
- Write a Python snippet to a local /tmp file that imports
authentik.providers.proxy.models.ProxyProvider, constructs it withname,external_host="https://<vhost>/"(exact, trailing slash),internal_host="http://caddy:8017", setsp.pk = None, thenp.save(). - Pipe raw:
ssh -o BatchMode=yes dankclaw 'podman exec -i authentik-server ak shell' < /tmp/x.py. Heredoc quoting through ssh SyntaxErrors; the raw-stdin pattern is the proven one (ak shell doesexec(sys.stdin.read())).
- Write a Python snippet to a local /tmp file that imports
- Route flip in ONE commit/deploy: vhost becomes
reverse_proxy anubis:8923 { header_up X-Real-Ip {remote_host} }; inside thehttp://:8017listener add@<svc> host <vhost>+handle @<svc> { reverse_proxy <upstream> }. - Deploy via the repo deploy flow (commit first; deploy pushes main itself).
Verification (all three)
- Unauthenticated probe from client:
curl -s https://<vhost>/→ HTTP 200 challenge page, grep body for challenge markers (challenge|not a bot|anubis). A 502 here instead of the challenge = chain mis-wired. - Post-auth routing proof: grep the LIVE config inside the container —
podman exec caddy sh -c "grep -n -A3 <handler> /etc/caddy/Caddyfile"— after the deploy prints its reload line; confirm the reload actually ran, don't trust exit 0. - Outpost reachability:
podman exec authentik-server getent hosts caddyresolves the compose DNS alias (10.89.1.x) that internal_host dials.
Pitfalls
- Probing
http://localhost:8017/from the host returns HTTP 000 (connection refused): the post-auth listener lives in the caddy container's netns and is not bound on host loopback. Probe inside the netns or grep the config instead. - Non-browser clients cannot solve the JS challenge. Before flipping, audit service-to-service traffic: ingestion/push paths (e.g. Alloy →
loki:3100) must target internal addresses; any backend datasource URL pointing at a public hostname of a guarded service breaks the moment you flip. - Exempt-by-construction categories that must stay unguarded: the IdP itself (
auth.— its login flow can't sit behind a challenge), git smart-HTTP endpoints (Gitea git clients), services with their own complete auth. Document the exemption in the vhost comment.