Files
serverAppConfigs/dankclaw/anubis/quadlet/anubis.container
T
Frank 799441a8be anubis: health probe in JSON exec form for the distroless image
The image has no shell (ko-build distroless; entrypoint /ko-app/anubis),
and this host's quadlet generator joins a bare HealthCmd into one
\x20-escaped argv element that podman then runs shell-form — exec of a
missing shell exits 1 and the container sits unhealthy. A JSON array in
HealthCmd passes through the generator verbatim and executes the binary
directly.
2026-08-28 10:39:09 -07:00

62 lines
2.8 KiB
INI

# Quadlet unit: Anubis bot-challenge proxy (rootless, boot-persistent).
#
# Install path on dankclaw: ~/.config/containers/systemd/anubis.container
# Unit name: anubis.service (container name: anubis)
#
# Two-tier chain: the public Caddy listeners for the guarded vhosts proxy
# everything to this container (port 8923, internal only — no published port,
# so metrics on :9090 stay reachable only inside dankclaw_default); Anubis
# verifies the client (challenge/DENY per policy) and forwards accepted
# traffic back to Caddy's internal listener (TARGET below), where the normal
# per-vhost routing — including Authentik forward_auth — resumes. Host headers
# survive both hops, so vhost matching on the internal listener still works.
#
# Image pin: the image is pulled from ghcr.io/techarohq/anubis (source of
# truth), but the unit runs it under a localhost/-aliased tag: on this host's
# podman 5.7 quadlet path, registry-prefixed refs fail to resolve from local
# storage at container create time ("image not known") even when present; the
# localhost alias resolves (verified live 2026-08-28). v1.13.0 was
# source-verified before pinning (policy parser is JSON-only at this tag,
# flags are the config surface via flagenv: TARGET sets --target,
# POLICY_FNAME sets --policy-fname). Upgrading = pull new tag, re-tag the
# localhost alias, restart the unit; re-verify the policy schema first.
# (the active Image= key lives in the [Container] section below)
#
# No secrets here: Anubis holds no credentials (memory store; challenge
# cookies are client-side JWTs). A restart invalidates issued challenges —
# users just re-challenge once. See serverAppConfigs-docs/Infrastructure/Anubis.md.
[Unit]
Description=Anubis bot-challenge proxy container
After=network-online.target
Wants=network-online.target
[Container]
ContainerName=anubis
Image=localhost/techarohq/anubis:v1.13.0
Pull=never
Network=dankclaw_default
# flagenv maps env names to flag names (TARGET -> --target, POLICY_FNAME ->
# --policy-fname). TARGET points back at Caddy's internal listener; the
# container DNS name resolves on dankclaw_default.
Environment=TARGET=http://caddy:8016
Environment=POLICY_FNAME=/policy/policy.json
Volume=/home/dank/serverAppConfigs/dankclaw/anubis/policy.json:/policy/policy.json:ro
# The image ships no HEALTHCHECK (distroless ko-build: entrypoint is
# /ko-app/anubis, no shell in the image), so the probe is declared here.
# JSON exec form on purpose: this quadlet generator joins HealthCmd into a
# single \x20-escaped argv element and podman then runs it as shell-form —
# which needs a shell the distroless image does not have. A JSON array is
# passed through verbatim and executes /ko-app/anubis directly.
HealthCmd=["/ko-app/anubis","--healthcheck"]
HealthInterval=15s
HealthTimeout=5s
HealthRetries=3
[Service]
Restart=always
RestartSec=5
[Install]
WantedBy=default.target