4.2 KiB
Authentik deployment
This repository owns the production Authentik Compose deployment and its
Komodo Stack declaration. The central komodo-bootstrap repository owns the
child Resource Sync and the ordered deployment Procedure.
Layout
.
├── docker-compose.yml
├── docker-compose.override.yml
├── komodo.toml
├── env/
│ └── common.env
├── secrets/
│ └── prod.env.age
├── scripts/
│ ├── compose.sh
│ └── init.sh
└── tests/
└── compose-secrets.sh
env/common.env contains tracked, non-secret configuration and explicit image
versions. secrets/prod.env.age contains only secrets and is encrypted to the
sbx0docker01 SSH host key and the personal agenix-phg SSH key.
The authentik-prod Stack follows protected main. Its direct webhook is
disabled. A Forgejo webhook calls the central authentik-deploy Procedure,
which runs the child Sync before DeployStackIfChanged.
Komodo passes docker-compose.yml first and docker-compose.override.yml
second. The base and deployment-specific override remain separate by design.
Secrets
The encrypted production bundle contains:
PG_PASS
AUTHENTIK_SECRET_KEY
AUTHENTIK_EMAIL__PASSWORD
GEOIPUPDATE_LICENSE_KEY
To edit it locally, decrypt to a mode-0600 temporary file, edit it, then
encrypt it to both recipients:
tmp=$(mktemp)
chmod 0600 "$tmp"
trap 'rm -f -- "$tmp"' EXIT HUP INT TERM
age --decrypt \
--identity ~/.ssh/identities/agenix-phg \
--output "$tmp" \
secrets/prod.env.age
${EDITOR:-vi} "$tmp"
age --encrypt \
--armor \
--recipient 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4umkUY5CG5aNJBhUjVcU8TWbh453N0pGHZhYDTOGQa' \
--recipient 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIr4Dji0wWzwSXyqbxdGa8LdWkhP+0T7kDKrLbyCoyos' \
--output secrets/prod.env.age.new \
"$tmp"
mv secrets/prod.env.age.new secrets/prod.env.age
Do not add a plaintext secret file to Git. The Stack config tracks the
encrypted bundle so a rotation counts as a deployment change, but does not
track the temporary .komodo/prod.env file.
The decrypted values are Compose secret sources rather than service
environment values. Containers receive only their declared files under
/run/secrets; Authentik reads them through file:// configuration values,
while PostgreSQL and GeoIP Update use their _FILE variables. Consequently,
docker compose config renders secret source names and file paths without
persisting the values in Komodo's merged configuration.
Compose wrapper
Komodo wraps config, pull, up, and run with
the NixOS-provided with-age-env command. The wrapper:
- obtains an exclusive per-bundle lock;
- removes stale plaintext and temporary files;
- decrypts through the NixOS-provided systemd
age-identitycredential; - creates plaintext with mode
0600; and - removes plaintext on success, failure, or a handled signal.
The plaintext target and matching .lock and .tmp.* paths are exclusively
owned by the wrapper. A SIGKILL or power loss can leave plaintext behind;
the next invocation removes it before decrypting again.
NixOS owns the wrapper, its runtime dependencies, the host-key systemd
credential, Periphery's Podman permissions, and the external
dokploy-network. Workload containers do not receive the age identity, but
commands launched by Periphery can access it. Revisit that trust boundary if
deployment authority is ever granted to another operator.
Run bash tests/compose-secrets.sh to verify that sentinel secret values do
not appear in the rendered Compose model and that every service receives only
its intended secret files.
Optional test environment
Do not create a test Stack or dummy bundle until there is a real long-lived
test branch. When it is needed:
- add
secrets/test.env.age, encrypted to the test host and personal keys; - keep both Stack declarations in
komodo.tomlonmain; - point
authentik-testat branchtestand its own encrypted/decrypted paths; and - add
authentik-testto the Procedure's deployment stage in the central bootstrap repository.
The repository webhook should use the Procedure's __ANY__ endpoint so pushes
to either protected environment branch enter the same ordered flow.