Dogfooding it, Pt 7 - Authentik
I was trying to avoid this. Getting into authentication is a rabbit hole. For the moment, I am choosing Authentik because it is reasonably easy to configure, and supports the features I want.
For a good start, Authentik uses Redis / Valkey as memory cache. So, we'll need to set that up. The relevant compose file fragment is
valkey:
command: "valkey-server --save 30 1"
deploy:
replicas: 1
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
start_period: 20s
interval: 30s
timeout: 3s
image: "valkey/valkey:7.2-alpine"
logging:
driver: journald
networks:
- homelab
restart: unless-stopped
user: "10007:10007"
volumes:
- "/srv/data/docker/redis/data:/data"
And create the directory for the Docker volume:
$ mkdir -p /srv/data/docker/valkey/data
$ sudo chown -R 10007:10007 /srv/data/docker/valkey
$ sudo chmod -R g-rwx,o-rwx /srv/data/docker/valkey
With the preparation for Authentik done, it's time to deploy the Authentik application itself. For the moment, we will NOT allow Authentik to manage its own outposts - we'll simply use the one integrated in the server. Authentik consists of two containers, the frontend server and a worker; they share some volumes, but we will not locate these in /srv/data/shared, as they are all internal to Authentik and will not be exported to and by a variety of unrelated services.
$ mkdir -p /srv/data/docker/authentik/{certs,custom-templates,media}
$ sudo chown -R 10008:10008 /srv/data/docker/authentik
$ sudo chmod -R g-rwx,o-rwx /srv/data/docker/authentik
$ pwgen > secrets/authentik-postgres
$ cat /dev/urandom | tr -cd [:graph:] | fold -w 60 | head -n1 > secrets/authentik-secret
Add those to the docker-compose secrets, in the root secrets section:
authentik-postgres:
- file: /home/<your user>/secrets/authentik-postgres
authentik-secret:
- file: /home/<your user>/secrets/authentik-secret
And, add in the postgres section, to the secrets section:
- authentik-postgres
Now, redeploy the stack, to make the changes live:
$ docker stack deploy -c <dockerfile> --prune homelab
Now create the database for Authentik:
$ docker container ls
$ docker container exec -it <postgres container ID> bash
$ $ psql -U postgres << EOSQL
create role authentik with login password '$(cat /run/secrets/authentik-postgres)';
create database authentik with owner authentik;
grant all privileges on database authentik to authentik;
EOSQL
Finally, we set up the containers for Authentik:
authentik-server:
command: server
depends_on:
- postgres
- redis
deploy:
labels:
traefik.enable: "true"
traefik.http.routers.authentik-rtr.rule: 'Host(`authentik.your.domain`) || HostRegexp(`{subdomain:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?}.your.domain`) && PathPrefix(`/outpost.goauthentik.io/`)'
traefik.http.routers.authentik-rtr.service: "authentik"
traefik.http.services.authentik.loadbalancer.server.port: "9000"
traefik.http.middlewares.authentik.forwardauth.address: "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
traefik.http.middlewares.authentik.forwardauth.trustForwardHeader: "true"
traefik.http.middlewares.authentik.forwardauth.authResponseHeaders: "X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version"
replicas: 1
environment:
AUTHENTIK_POSTGRESQL__HOST: "postgres"
AUTHENTIK_POSTGRESQL__NAME: "authentik"
AUTHENTIK_POSTGRESQL__USER: "authentik"
AUTHENTIK_POSTGRESQL__PASSWORD: "file:///run/secrets/authentik-postgres"
AUTHENTIK_REDIS__HOST: "valkey"
AUTHENTIK_SECRET_KEY: "file:///run/secrets/authentik-secret"
AUTHENTIK_EMAIL__HOST: "munin.your.domain"
AUTHENTIK_EMAIL__FROM: "authentik@turriff.net"
hostname: "authentik-server"
image: "ghcr.io/goauthentik/server:2024.4"
logging:
driver: journald
networks:
- homelab
restart: on-failure
secrets:
- authentik-postgres
- authentik-secret
user: "10008:10008"
volumes:
- "/srv/data/docker/authentik/media:/media"
- "/srv/data/docker/authentik/custom-templates:/templates"
authentik-worker:
command: worker
depends_on:
- postgres
- redis
deploy:
replicas: 1
environment:
AUTHENTIK_POSTGRESQL__HOST: "postgres"
AUTHENTIK_POSTGRESQL__NAME: "authentik"
AUTHENTIK_POSTGRESQL__USER: "authentik"
AUTHENTIK_POSTGRESQL__PASSWORD: "file:///run/secrets/authentik-postgres"
AUTHENTIK_REDIS__HOST: "valkey"
AUTHENTIK_SECRET_KEY: "file:///run/secrets/authentik-secret"
AUTHENTIK_EMAIL__HOST: "munin.your.domain"
AUTHENTIK_EMAIL__FROM: "authentik@turriff.net"
image: "ghcr.io/goauthentik/server:2024.4"
logging:
driver: journald
networks:
- homelab
restart: on-failure
secrets:
- authentik-postgres
- authentik-secret
user: "10008:10008"
volumes:
- "/srv/data/docker/authentik/media:/media"
- "/srv/data/docker/authentik/certs:/certs"
- "/srv/data/docker/authentik/custom-templates:/templates"
Most of this should be pretty self-explanatory at this point. We do configure a Traefik middleware when the Authentik frontend comes up, to implement forward-auth.
Time to bring the stack up.
$ docker stack deploy -c <compose file> --prune homelab
Authentik needs a bit of configuration, which I'll demonstrate for Traefik's dashboard. Any other application requiring forward-auth (you can tell by it being labeled to use the middleware authentik@swarm) will work analog to this.
First, though, we need to do the basic Authentik configuration. You'll need to access https://authentik.<your domain>/if/flow/initial-setup/ and follow the steps to set up your built-in admin user. You can add other users by going to the Admin Interface > Directory > Users > Create. For the moment, all users so created will have access to all applications you protect. Policy setup for Authentik is beyond my scope at the moment.
Next, we'll want to properly protect Traefik's dashboard, instead of relying on IP whitelisting. The steps are
- Go to the Admin Interface
- Go to Applications > Providers > Create
- Select Proxy Provider
- Name: Traefik
- Authentication Flow: default-authentication-flow
- Authorization Flow: default-provider-authorization-explicit-consent
- Forward Auth (Single Application)
- External Host: https://traefik.your.domain
- Finish
- Go to Applications > Applications > Create
- Name: Traefik
- Slug: traefik-slug
- Finish
Now modify Traefik's section in your docker compose file, and replace the current middleware line with
traefik.http.routers.traefik-rtr.middlewares: "authentik@swarm"
Hitting https://traefik.your.domain should now greet you with an Authentik logon.