tech_notes

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

authentik.md (6663B)


      1 # Authentik
      2 
      3 We document the setup of [Authentik](https://goauthentik.io), the identity provider used to protect the private services at `piribauer.ch`.
      4 Authentik runs in Docker and exposes its server on `127.0.0.1:9000`.
      5 All protected apps delegate authentication to it via nginx forward-auth — they never implement any login logic themselves.
      6 
      7 We assume Docker and nginx are already installed, and that a wildcard or per-subdomain TLS certificate is in place (we use Certbot).
      8 
      9 ## Docker Compose
     10 
     11 We create the working directory and place the compose file there:
     12 
     13 ```console
     14 $ mkdir -p /home/julian/authentik
     15 $ cd /home/julian/authentik
     16 ```
     17 
     18 The `docker-compose.yml` runs four services: PostgreSQL, Redis, and the Authentik server and worker.
     19 The server is bound to `127.0.0.1:9000` so it is only reachable locally.
     20 
     21 ```yaml
     22 services:
     23   postgresql:
     24     image: docker.io/library/postgres:16-alpine
     25     restart: unless-stopped
     26     volumes:
     27       - database:/var/lib/postgresql/data
     28     environment:
     29       POSTGRES_PASSWORD: ${PG_PASS}
     30       POSTGRES_USER: ${PG_USER:-authentik}
     31       POSTGRES_DB: ${PG_DB:-authentik}
     32 
     33   redis:
     34     image: docker.io/library/redis:alpine
     35     command: --save 60 1 --loglevel warning
     36     restart: unless-stopped
     37     volumes:
     38       - redis:/data
     39 
     40   server:
     41     image: ghcr.io/goauthentik/server:2024.12.3
     42     restart: unless-stopped
     43     command: server
     44     environment:
     45       AUTHENTIK_REDIS__HOST: redis
     46       AUTHENTIK_POSTGRESQL__HOST: postgresql
     47       AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
     48       AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
     49       AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
     50       AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
     51       AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
     52     volumes:
     53       - ./media:/media
     54       - ./custom-templates:/templates
     55     ports:
     56       - "127.0.0.1:9000:9000"
     57       - "127.0.0.1:9443:9443"
     58 
     59   worker:
     60     image: ghcr.io/goauthentik/server:2024.12.3
     61     restart: unless-stopped
     62     command: worker
     63     user: root
     64     environment:
     65       AUTHENTIK_REDIS__HOST: redis
     66       AUTHENTIK_POSTGRESQL__HOST: postgresql
     67       AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
     68       AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
     69       AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
     70       AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
     71       AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
     72     volumes:
     73       - /var/run/docker.sock:/var/run/docker.sock
     74       - ./media:/media
     75       - ./certs:/certs
     76       - ./custom-templates:/templates
     77 
     78 volumes:
     79   database:
     80     driver: local
     81   redis:
     82     driver: local
     83 ```
     84 
     85 ## Environment file
     86 
     87 We create `/home/julian/authentik/.env` with the database credentials and a secret key.
     88 The secret key should be a long random string — it is used to sign sessions and must not change after the first start.
     89 
     90 ```bash
     91 PG_PASS=<random password>
     92 PG_USER=authentik
     93 PG_DB=authentik
     94 AUTHENTIK_SECRET_KEY=<long random string>
     95 AUTHENTIK_ERROR_REPORTING__ENABLED=false
     96 ```
     97 
     98 ## Starting Authentik
     99 
    100 ```console
    101 $ cd /home/julian/authentik
    102 $ docker compose up -d
    103 ```
    104 
    105 Since docker.service is enabled in systemd and all containers have `restart: unless-stopped`, Authentik comes back up automatically after a reboot.
    106 
    107 On first start, we navigate to `https://auth.piribauer.ch/if/flow/initial-setup/` to set the admin password.
    108 
    109 ## Nginx — Authentik itself
    110 
    111 We proxy `auth.piribauer.ch` to the local server:
    112 
    113 ```nginx
    114 server {
    115     server_name auth.piribauer.ch;
    116 
    117     location / {
    118         proxy_pass         http://127.0.0.1:9000;
    119         proxy_http_version 1.1;
    120         proxy_set_header   Upgrade $http_upgrade;
    121         proxy_set_header   Connection "upgrade";
    122         proxy_set_header   Host $host;
    123         proxy_set_header   X-Real-IP $remote_addr;
    124         proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    125         proxy_set_header   X-Forwarded-Proto $scheme;
    126         proxy_buffering    off;
    127     }
    128 
    129     listen 443 ssl;
    130     ssl_certificate     /etc/letsencrypt/live/piribauer.ch/fullchain.pem;
    131     ssl_certificate_key /etc/letsencrypt/live/piribauer.ch/privkey.pem;
    132     include             /etc/letsencrypt/options-ssl-nginx.conf;
    133     ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;
    134 }
    135 ```
    136 
    137 ## Creating a protected application
    138 
    139 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/`.
    140 
    141 Under **Providers**, we create a new *Proxy Provider* in forward-auth mode for the application's domain (e.g. `https://hub.piribauer.ch`).
    142 We then create an **Application** linked to that provider.
    143 Finally, under **Outposts**, we add the application to the embedded outpost so the outpost begins serving the forward-auth endpoint for it.
    144 
    145 ## Nginx — forward-auth for protected apps
    146 
    147 Each protected app follows the same nginx pattern.
    148 We add two locations and an `auth_request` directive to the main location:
    149 
    150 ```nginx
    151 # Authentik outpost endpoint
    152 location /outpost.goauthentik.io {
    153     proxy_pass              http://127.0.0.1:9000/outpost.goauthentik.io;
    154     proxy_set_header        Host $host;
    155     proxy_set_header        X-Original-URL $scheme://$host$request_uri;
    156     proxy_set_header        X-Real-IP $remote_addr;
    157     proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    158     proxy_set_header        X-Forwarded-Proto $scheme;
    159     proxy_pass_request_body off;
    160     proxy_set_header        Content-Length "";
    161     add_header              Set-Cookie $auth_cookie;
    162     auth_request_set        $auth_cookie $upstream_http_set_cookie;
    163 }
    164 
    165 # Redirect unauthenticated requests to the login page
    166 location @goauthentik_proxy_signin {
    167     internal;
    168     add_header Set-Cookie $auth_cookie;
    169     return 302 /outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
    170 }
    171 
    172 location / {
    173     # ... proxy_pass or root directive ...
    174 
    175     auth_request      /outpost.goauthentik.io/auth/nginx;
    176     error_page 401  = @goauthentik_proxy_signin;
    177 
    178     auth_request_set $auth_cookie           $upstream_http_set_cookie;
    179     add_header       Set-Cookie $auth_cookie;
    180 
    181     # Optional: expose user attributes to the backend
    182     auth_request_set $authentik_username  $upstream_http_x_authentik_username;
    183     auth_request_set $authentik_email     $upstream_http_x_authentik_email;
    184     proxy_set_header X-Authentik-Username $authentik_username;
    185     proxy_set_header X-Authentik-Email    $authentik_email;
    186 }
    187 ```
    188 
    189 On each request, nginx calls the outpost's `/auth/nginx` endpoint.
    190 If the user has a valid session, the outpost returns 200 and nginx proceeds, injecting the username and email as headers.
    191 If not, nginx returns 401 which triggers the redirect to the login flow.