feat: enhance Authentik deployment with age-encrypted secrets; change deployment to komodo
This commit is contained in:
parent
c3689404bd
commit
71736f8dd9
9 changed files with 254 additions and 369 deletions
216
README.md
216
README.md
|
|
@ -1,37 +1,68 @@
|
|||
# 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.
|
||||
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.
|
||||
|
||||
## Layout
|
||||
## 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
|
||||
.
|
||||
├── docker-compose.yml
|
||||
├── docker-compose.override.yml
|
||||
├── komodo.toml
|
||||
├── .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
|
||||
│ └── common.env # Tracked non-secret configuration and versions
|
||||
├── komodo.toml # authentik-prod Stack declaration
|
||||
├── secrets/
|
||||
│ └── prod.env.age
|
||||
├── scripts/
|
||||
│ ├── compose.sh
|
||||
│ └── init.sh
|
||||
└── tests/
|
||||
└── compose-secrets.sh
|
||||
│ └── prod.env.age # Encrypted production secrets
|
||||
└── data/authentik/custom-templates/
|
||||
```
|
||||
|
||||
`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.
|
||||
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.
|
||||
|
||||
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.
|
||||
`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
|
||||
|
||||
|
|
@ -44,33 +75,33 @@ 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:
|
||||
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
|
||||
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
|
||||
age-docker edit 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
|
||||
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
|
||||
|
|
@ -80,10 +111,10 @@ 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 Compose wrapper
|
||||
|
||||
Komodo wraps `config`, `pull`, `up`, and `run` with
|
||||
the NixOS-provided `with-age-env` command. The 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;
|
||||
|
|
@ -95,15 +126,86 @@ 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.
|
||||
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.
|
||||
## 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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue