225 lines
7.2 KiB
Markdown
225 lines
7.2 KiB
Markdown
# authentik
|
|
|
|
Kubernetes deployment source-of-truth repository for `authentik`.
|
|
|
|
This template is intended for applications deployed with ArgoCD through an
|
|
`ApplicationSet`. It uses a `base/` plus `overlays/` layout, stores runtime
|
|
secrets as SOPS-encrypted manifests, and includes a Gitea workflow that checks
|
|
Kubernetes YAML syntax on every push and pull request. It also ships with a
|
|
dormant bootstrap `ApplicationSet` that only becomes active after an explicit
|
|
enable change and a promotion into `main`.
|
|
|
|
## Layout
|
|
|
|
```text
|
|
.
|
|
├── .gitea/
|
|
│ ├── template
|
|
│ └── workflows/validate.yaml
|
|
├── .github/
|
|
│ └── workflows/check-sops-sync.yml
|
|
├── .sops.yaml
|
|
├── bootstrap/
|
|
│ ├── applicationset.yaml
|
|
│ └── config.yaml
|
|
├── manifest/
|
|
│ ├── base/
|
|
│ │ ├── deployment.yaml
|
|
│ │ ├── kustomization.yaml
|
|
│ │ ├── namespace.yaml
|
|
│ │ └── service.yaml
|
|
│ ├── components/
|
|
│ │ └── example-component/
|
|
│ └── overlays/
|
|
│ └── production/
|
|
│ ├── ingressroute.yaml
|
|
│ ├── kustomization.yaml
|
|
│ ├── secret-generator.yaml
|
|
│ ├── secret.secret.yaml
|
|
│ └── storage/
|
|
│ ├── persistentvolumeclaim.yaml
|
|
│ └── persistentvolume-nfs.yaml
|
|
└── scripts/
|
|
├── ci/check-sops-sync
|
|
├── git-hooks/pre-commit
|
|
├── install-git-hooks
|
|
└── lib/
|
|
├── sops-path-regexes
|
|
└── sops-sync-lib
|
|
```
|
|
|
|
## First edits
|
|
|
|
Update these files before the first deployment:
|
|
|
|
1. `manifest/base/deployment.yaml`
|
|
Set the container image, ports, probes, resource requests, and any required
|
|
environment variables.
|
|
2. `manifest/overlays/production/ingressroute.yaml`
|
|
Set the real hostname, entry point names, and middleware references if used.
|
|
3. `manifest/overlays/production/storage/persistentvolumeclaim.yaml`
|
|
Set the requested size, access mode, and storage class for the environment.
|
|
4. `.sops.yaml`
|
|
Replace the sample age recipient with the public key that ArgoCD should use
|
|
for decryption.
|
|
5. `manifest/overlays/production/secret.secret.yaml`
|
|
Re-encrypt the placeholder secret values with your own data.
|
|
|
|
## Bootstrap activation model
|
|
|
|
This template is designed to start on an `init` branch. The intended flow is:
|
|
|
|
1. Create the repository from this template with `init` as the initial branch.
|
|
2. Tailor the manifests, secrets, storage, and ingress on `init`.
|
|
3. Keep `bootstrap/config.yaml` set to `enabled: false` while the repository is
|
|
being prepared.
|
|
4. Create `main` by merging `init` only when the repository is ready to be part
|
|
of the delivery pipeline.
|
|
5. Deliberately change `bootstrap/config.yaml` to `enabled: true` on `main` to
|
|
allow the workflow to apply the bootstrap `ApplicationSet`.
|
|
|
|
The workflow behavior is:
|
|
|
|
- `bootstrap/applicationset.yaml` is ignored by validation while bootstrap is disabled
|
|
- no cluster apply is attempted unless the workflow runs on `main`
|
|
- cluster apply also requires the `KUBECONFIG_B64` Gitea secret
|
|
|
|
## Bootstrap files
|
|
|
|
- `bootstrap/config.yaml`
|
|
Repository-local activation switch. Default is disabled.
|
|
- `bootstrap/applicationset.yaml`
|
|
ArgoCD `ApplicationSet` manifest template for this repository.
|
|
|
|
If the repository is created by copying files with plain Git instead of Gitea's
|
|
template expansion, you must replace the `${REPO_NAME...}` placeholders in
|
|
`bootstrap/applicationset.yaml` before enabling bootstrap.
|
|
|
|
## Storage model
|
|
|
|
Storage is overlay-owned, so each environment can request different backing
|
|
storage without sharing claims across environments.
|
|
|
|
- `manifest/overlays/production/storage/persistentvolumeclaim.yaml`
|
|
Default writable application storage. With the default `longhorn`
|
|
`storageClassName`, Kubernetes dynamically provisions the backing PV when the
|
|
claim is created.
|
|
- `manifest/overlays/production/storage/persistentvolume-nfs.yaml`
|
|
Optional static PV example for NFS-backed or pre-provisioned shared storage.
|
|
This file is not referenced by default. Add it to the overlay kustomization
|
|
only when you need a static PV.
|
|
|
|
The base deployment mounts the claim at `/data`.
|
|
|
|
If storage should be shared between multiple applications in different
|
|
repositories, keep those applications in the same namespace and have a single
|
|
repository own the PVC resource. The other repositories should reference the
|
|
same claim name from their deployments instead of creating a duplicate PVC.
|
|
|
|
## Secret workflow
|
|
|
|
1. Generate an age key pair.
|
|
2. Store the private key where ArgoCD / ksops can read it.
|
|
3. Update `.sops.yaml` with the matching public recipient.
|
|
4. Re-encrypt `manifest/overlays/production/secret.secret.yaml`.
|
|
|
|
Example:
|
|
|
|
```bash
|
|
age-keygen -o age-key.txt
|
|
sops --encrypt --in-place manifest/overlays/production/secret.secret.yaml
|
|
```
|
|
|
|
## Repo-managed SOPS sync
|
|
|
|
This repo also supports sync-managed secrets using the naming convention:
|
|
|
|
```text
|
|
foo.yaml -> foo.enc.yaml -> foo.enc.yaml.sync-hmac
|
|
```
|
|
|
|
- `foo.yaml`
|
|
Local plaintext source. Keep it gitignored and untracked.
|
|
- `foo.enc.yaml`
|
|
Tracked SOPS-encrypted file.
|
|
- `foo.enc.yaml.sync-hmac`
|
|
Tracked HMAC sidecar used to prove the encrypted file still matches the last
|
|
synced plaintext content.
|
|
|
|
Requirements:
|
|
|
|
- `SOPS_SYNC_HMAC_KEY` must be set locally for hooks and in CI
|
|
- `sops`, `openssl`, and either `yq` or `python3` with `PyYAML` must be available
|
|
- CI also needs access to the repo's SOPS decryption mechanism, such as an age key
|
|
|
|
Generate a local HMAC key:
|
|
|
|
```bash
|
|
openssl rand -hex 32
|
|
```
|
|
|
|
Set it for the current shell session:
|
|
|
|
```bash
|
|
export SOPS_SYNC_HMAC_KEY='<paste-generated-key-here>'
|
|
```
|
|
|
|
Persist it in `zsh`:
|
|
|
|
```bash
|
|
echo "export SOPS_SYNC_HMAC_KEY='<paste-generated-key-here>'" >> ~/.zshrc
|
|
source ~/.zshrc
|
|
```
|
|
|
|
Persist it in `bash`:
|
|
|
|
```bash
|
|
echo "export SOPS_SYNC_HMAC_KEY='<paste-generated-key-here>'" >> ~/.bashrc
|
|
source ~/.bashrc
|
|
```
|
|
|
|
Verify it is set:
|
|
|
|
```bash
|
|
printenv SOPS_SYNC_HMAC_KEY
|
|
```
|
|
|
|
Install the local hook:
|
|
|
|
```bash
|
|
scripts/install-git-hooks
|
|
```
|
|
|
|
Local workflow:
|
|
|
|
1. Keep your plaintext secret local-only, for example `manifest/overlays/production/secret.yaml`.
|
|
2. Stage either the plaintext file or its `.enc.yaml` twin.
|
|
3. On pre-commit, the hook encrypts the plaintext if needed, updates the
|
|
`.sync-hmac` sidecar, and stages both tracked files.
|
|
|
|
CI behavior:
|
|
|
|
- CI fails if a tracked `.enc.yaml` file is not valid SOPS content
|
|
- CI fails if the `.sync-hmac` sidecar is missing
|
|
- CI fails if decrypted plaintext no longer matches the tracked sidecar HMAC
|
|
|
|
Security tradeoffs:
|
|
|
|
- The sidecar is an HMAC, not a plain checksum, so the plaintext fingerprint is
|
|
keyed and not directly reusable without `SOPS_SYNC_HMAC_KEY`
|
|
- Anyone who can decrypt the repo secret files and also access the HMAC key can
|
|
recompute sidecars, so protect both inputs appropriately
|
|
- Plaintext local files must remain gitignored and never be committed
|
|
|
|
## ArgoCD ApplicationSet path
|
|
|
|
Point the generated ArgoCD `Application` at:
|
|
|
|
```text
|
|
manifest/overlays/production
|
|
```
|
|
|
|
If your `ApplicationSet` uses repository scanning, this repository is designed
|
|
to be the single source of truth for one application, with ArgoCD rendering the
|
|
selected overlay.
|