223 lines
9.2 KiB
Markdown
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.
|