tech_notes

A collection of notes on setups and implementations.
Log | Files | Refs | README

commit 715e4894982c7f06e7fcb525db74bb43cf1ad57c
parent 388dc48a1d8fb7e10b30a0e7d2a578737ee178ab
Author: Julian Piribauer <julian.piribauer@gmail.com>
Date:   Sat,  2 May 2026 07:18:58 +0000

Add Authentik setup note

Diffstat:
Aauthentik.md | 191+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 191 insertions(+), 0 deletions(-)

diff --git a/authentik.md b/authentik.md @@ -0,0 +1,191 @@ +# Authentik + +We document the setup of [Authentik](https://goauthentik.io), the identity provider used to protect the private services at `piribauer.ch`. +Authentik runs in Docker and exposes its server on `127.0.0.1:9000`. +All protected apps delegate authentication to it via nginx forward-auth — they never implement any login logic themselves. + +We assume Docker and nginx are already installed, and that a wildcard or per-subdomain TLS certificate is in place (we use Certbot). + +## Docker Compose + +We create the working directory and place the compose file there: + +```console +$ mkdir -p /home/julian/authentik +$ cd /home/julian/authentik +``` + +The `docker-compose.yml` runs four services: PostgreSQL, Redis, and the Authentik server and worker. +The server is bound to `127.0.0.1:9000` so it is only reachable locally. + +```yaml +services: + postgresql: + image: docker.io/library/postgres:16-alpine + restart: unless-stopped + volumes: + - database:/var/lib/postgresql/data + environment: + POSTGRES_PASSWORD: ${PG_PASS} + POSTGRES_USER: ${PG_USER:-authentik} + POSTGRES_DB: ${PG_DB:-authentik} + + redis: + image: docker.io/library/redis:alpine + command: --save 60 1 --loglevel warning + restart: unless-stopped + volumes: + - redis:/data + + server: + image: ghcr.io/goauthentik/server:2024.12.3 + restart: unless-stopped + command: server + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} + AUTHENTIK_ERROR_REPORTING__ENABLED: "false" + volumes: + - ./media:/media + - ./custom-templates:/templates + ports: + - "127.0.0.1:9000:9000" + - "127.0.0.1:9443:9443" + + worker: + image: ghcr.io/goauthentik/server:2024.12.3 + restart: unless-stopped + command: worker + user: root + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} + AUTHENTIK_ERROR_REPORTING__ENABLED: "false" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./media:/media + - ./certs:/certs + - ./custom-templates:/templates + +volumes: + database: + driver: local + redis: + driver: local +``` + +## Environment file + +We create `/home/julian/authentik/.env` with the database credentials and a secret key. +The secret key should be a long random string — it is used to sign sessions and must not change after the first start. + +```bash +PG_PASS=<random password> +PG_USER=authentik +PG_DB=authentik +AUTHENTIK_SECRET_KEY=<long random string> +AUTHENTIK_ERROR_REPORTING__ENABLED=false +``` + +## Starting Authentik + +```console +$ cd /home/julian/authentik +$ docker compose up -d +``` + +Since docker.service is enabled in systemd and all containers have `restart: unless-stopped`, Authentik comes back up automatically after a reboot. + +On first start, we navigate to `https://auth.piribauer.ch/if/flow/initial-setup/` to set the admin password. + +## Nginx — Authentik itself + +We proxy `auth.piribauer.ch` to the local server: + +```nginx +server { + server_name auth.piribauer.ch; + + location / { + proxy_pass http://127.0.0.1:9000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + } + + listen 443 ssl; + ssl_certificate /etc/letsencrypt/live/piribauer.ch/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/piribauer.ch/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; +} +``` + +## Creating a protected application + +For each service we want to protect, we create an application and a proxy provider in the Authentik admin UI at `https://auth.piribauer.ch/if/admin/`. + +Under **Providers**, we create a new *Proxy Provider* in forward-auth mode for the application's domain (e.g. `https://hub.piribauer.ch`). +We then create an **Application** linked to that provider. +Finally, under **Outposts**, we add the application to the embedded outpost so the outpost begins serving the forward-auth endpoint for it. + +## Nginx — forward-auth for protected apps + +Each protected app follows the same nginx pattern. +We add two locations and an `auth_request` directive to the main location: + +```nginx +# Authentik outpost endpoint +location /outpost.goauthentik.io { + proxy_pass http://127.0.0.1:9000/outpost.goauthentik.io; + proxy_set_header Host $host; + proxy_set_header X-Original-URL $scheme://$host$request_uri; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + add_header Set-Cookie $auth_cookie; + auth_request_set $auth_cookie $upstream_http_set_cookie; +} + +# Redirect unauthenticated requests to the login page +location @goauthentik_proxy_signin { + internal; + add_header Set-Cookie $auth_cookie; + return 302 /outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri; +} + +location / { + # ... proxy_pass or root directive ... + + auth_request /outpost.goauthentik.io/auth/nginx; + error_page 401 = @goauthentik_proxy_signin; + + auth_request_set $auth_cookie $upstream_http_set_cookie; + add_header Set-Cookie $auth_cookie; + + # Optional: expose user attributes to the backend + auth_request_set $authentik_username $upstream_http_x_authentik_username; + auth_request_set $authentik_email $upstream_http_x_authentik_email; + proxy_set_header X-Authentik-Username $authentik_username; + proxy_set_header X-Authentik-Email $authentik_email; +} +``` + +On each request, nginx calls the outpost's `/auth/nginx` endpoint. +If the user has a valid session, the outpost returns 200 and nginx proceeds, injecting the username and email as headers. +If not, nginx returns 401 which triggers the redirect to the login flow.