authentik/README.md

223 lines
9.2 KiB
Markdown

# Authentik deployment
This repository owns the production deployment of
[Authentik](https://goauthentik.io/) for `sso.s1q.dev`, including its Compose
configuration, encrypted secrets, and Komodo Stack declaration.
## Deployment architecture
The `authentik-prod` Stack follows the protected `main` branch. Its direct
webhook is disabled. A Forgejo webhook calls the central `authentik-deploy`
Procedure, which first runs the child Resource Sync and then runs
`DeployStackIfChanged`. The central `docker-compose/komodo-bootstrap`
repository owns that child Sync and Procedure.
Komodo passes `docker-compose.yml` first and `docker-compose.override.yml`
second. The upstream-derived base and deployment-specific override remain
separate by design. A regular deployment therefore follows this path:
1. merge a reviewed change into `main`;
2. Forgejo calls the `authentik-deploy` Procedure webhook;
3. the Procedure syncs `komodo.toml`; and
4. Komodo deploys `authentik-prod` if a tracked file changed.
For a first deployment or a manual retry, run `authentik-deploy` in Komodo
instead of deploying the Stack directly. This keeps resource synchronization
and deployment in the intended order.
## Prerequisites
Before the first deployment, ensure that:
- NixOS has provisioned the `sbx0docker01` Komodo Server, Periphery's container
permissions, the `with-age-env` wrapper, and its `age-identity` credential;
- the external `dokploy-network` exists on the deployment host;
- the root `bootstrap` Resource Sync has registered the `authentik` child Sync
and the `authentik-deploy` Procedure; and
- the Forgejo repository webhook uses the Procedure's `__ANY__` webhook URL.
Host provisioning, Tailscale, CrowdSec, and registry setup belong to NixOS or
their respective infrastructure repositories and are intentionally not
duplicated here.
## Layout and ownership
```text
.
├── .age-docker.toml # Secret recipients and policy
├── docker-compose.yml # Upstream-derived Compose base
├── docker-compose.override.yml # Production networks, routing, and images
├── env/
│ └── common.env # Tracked non-secret configuration and versions
├── komodo.toml # authentik-prod Stack declaration
├── secrets/
│ └── prod.env.age # Encrypted production secrets
└── data/authentik/custom-templates/
```
NixOS owns Komodo, Periphery, host credentials, Podman permissions, and the
external `dokploy-network`. This repository owns the application definition.
Persistent database, Redis, media, certificate, and GeoIP data live in named
container volumes on the deployment host and are not stored in Git.
`env/common.env` is the authoritative source for tracked runtime settings and
explicit image versions. The old local `.env`/template and init-script
workflow is no longer part of deployment.
## Secrets
The encrypted production bundle contains:
```text
PG_PASS
AUTHENTIK_SECRET_KEY
AUTHENTIK_EMAIL__PASSWORD
GEOIPUPDATE_LICENSE_KEY
```
Local secret management uses only `age-docker`. Recipient keys, groups, and
the rule for the encrypted bundle live in `.age-docker.toml`. The bundle is
currently encrypted for the personal `phg` age key and the `sbx0docker01` SSH
host key.
Edit the bundle with:
```bash
age-docker edit secrets/prod.env.age
```
When a configured recipient changes, re-encrypt the bundle and validate the
complete policy:
```bash
age-docker rekey secrets/prod.env.age
age-docker check
```
`age-docker` creates mode-`0600` temporary plaintext, removes it after the
editor exits or a handled signal occurs, produces armored ciphertext, and
verifies the new ciphertext with the personal identity before atomically
replacing the tracked file. It does not add implicit recipients: every key
with access is declared in `.age-docker.toml`.
Never add a plaintext secret file to Git. The Stack tracks the encrypted
bundle so that secret rotation counts as a deployment change, but it 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.
## Komodo 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-identity` credential;
- 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.
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.
## Upgrade
There is currently no long-lived test environment. Prepare and review every
upgrade on a branch before merging it into production. If a test Stack is
added later, perform the upgrade there first.
1. Confirm that the host backups are current and restorable.
2. Read the target version's [Authentik release notes](https://docs.goauthentik.io/releases/)
and upgrading section. Note all manual migrations, breaking configuration
changes, and outpost compatibility requirements before changing files.
3. Create an upgrade branch.
4. Download the target version's upstream Compose file alongside the tracked
file. Replace the example version with the intended target:
```bash
authentik_version="2025.8"
curl -fsSL \
"https://goauthentik.io/version/${authentik_version}/lifecycle/container/compose.yml" \
-o docker-compose.yml
```
5. Compare `docker-compose.yml` with the previously tracked version.
6. Review `docker-compose.override.yml` against the new base structure. Keep
the production networks, Traefik routing, volumes, and explicit image
declarations working with the new release.
7. Update `AUTHENTIK_TAG` in `env/common.env`. Review the supported PostgreSQL
and Redis versions and update `POSTGRES_TAG`, `REDIS_TAG`, and
`GEOIPUPDATE_TAG` only after checking their respective release and migration
notes. Plan to update all Authentik outposts to the same Authentik version.
8. Remove the downloaded comparison file and validate the resulting policy
and Compose model. Dummy values satisfy Compose's secret-source checks
without decrypting production secrets:
```bash
rm docker-compose.upstream.yml
age-docker check
PG_PASS=dummy \
AUTHENTIK_SECRET_KEY=dummy \
AUTHENTIK_EMAIL__PASSWORD=dummy \
GEOIPUPDATE_LICENSE_KEY=dummy \
docker compose \
--env-file env/common.env \
--file docker-compose.yml \
--file docker-compose.override.yml \
config --quiet
```
9. Review the complete diff, commit it, push the branch, and merge it through a
merge request. Do not run `docker compose pull`, `down`, or `up` manually on
the host; the merge triggers the ordered Komodo deployment flow.
10. Follow the `authentik-deploy` Procedure and `authentik-prod` Stack logs in
Komodo. Verify that all services become healthy, `https://sso.s1q.dev` is
reachable, and an SSO login succeeds. Update outposts to the matching
version and verify their connectivity.
Record any repository-specific manual migration steps in this README before
the rollout. None are currently documented.
### Rollback
For a configuration or image regression, revert the merge on `main`; the
webhook will run the same ordered deployment flow with the previous tracked
configuration. Do not assume that an application or database migration can be
downgraded. Follow the target release's rollback guidance and restore a tested
backup when a migration is not reversible.
## Backup and disaster recovery
This Compose project does not define a backup or `restore-cli` service. The
old container-based restore commands therefore no longer apply. Backups and
volume recovery must be provided and documented by the host infrastructure.
Before relying on the deployment in production, ensure that the PostgreSQL
data and all named volumes required by Authentik are covered by a tested
backup and recovery runbook. Keep the runbook with the infrastructure that
owns those backups and link it here once it exists.
## 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:
1. add `secrets/test.env.age`, encrypted to the test host and personal keys;
2. keep both Stack declarations in `komodo.toml` on `main`;
3. point `authentik-test` at branch `test` and its own encrypted/decrypted
paths; and
4. add `authentik-test` to 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.