Add Authelia config and SOPS-encrypted secrets

- Add configuration.yml from running helsinki-a deployment
- Replace example secrets with real SOPS-encrypted config.enc.yml
- Add LDAP and SMTP password file env vars to docker-compose
  (all secrets now via file mounts, zero inline passwords)
- Update README with secret mapping and deployment steps

Closes PESO-89
This commit is contained in:
Rasmus Wejlgaard 2026-03-28 17:42:07 +00:00
parent 8163b226b3
commit 8bb91032f3
5 changed files with 228 additions and 14 deletions

View file

@ -2,12 +2,49 @@
SSO authentication portal with LLDAP directory and MariaDB backend. SSO authentication portal with LLDAP directory and MariaDB backend.
- **Host:** helsinki-a - **Host:** helsinki-a (100.67.6.27)
- **URL:** https://auth.pez.sh (integrated via Caddy forward_auth) - **URL:** https://auth.pez.sh / https://auth.pez.solutions
- **Components:** - **Components:**
- **Authelia** — SSO portal (port 9091, localhost only) - **Authelia** — SSO portal (port 9091, localhost only)
- **LLDAP** — Lightweight LDAP directory (port 3890 LDAP, port 17170 web UI) - **LLDAP** — Lightweight LDAP directory (port 3890 LDAP, port 17170 web UI)
- **MariaDB 11** — Session/config storage - **MariaDB 11** — Session/config storage
- **Config:** `/root/authelia/config/` - **Config:** `/root/authelia/config/configuration.yml`
- **Secrets:** `/root/authelia/secrets/` (JWT, session, encryption keys, passwords) - **Secrets:** `/root/authelia/secrets/` (mounted into containers)
- **LDAP base DN:** `dc=pez,dc=sh` - **LDAP base DN:** `dc=pez,dc=sh`
## Secrets
All secrets are stored in `config.enc.yml` (SOPS-encrypted with age).
To decrypt: `sops -d config.enc.yml`
Secret files expected in `/root/authelia/secrets/` on helsinki-a:
| File | Source key in config.enc.yml | Used by |
|------|------------------------------|---------|
| `JWT_SECRET` | `jwt_secret` | Authelia (password reset JWT) |
| `SESSION_SECRET` | `session_secret` | Authelia (session encryption) |
| `STORAGE_ENCRYPTION_KEY` | `storage_encryption_key` | Authelia (DB encryption) |
| `MYSQL_PASSWORD` | `mysql_password` | Authelia + MariaDB |
| `MYSQL_ROOT_PASSWORD` | `mysql_root_password` | MariaDB |
| `LLDAP_ADMIN_PASSWORD` | `lldap_admin_password` | LLDAP + Authelia (LDAP bind) |
| `LLDAP_JWT_SECRET` | `lldap_jwt_secret` | LLDAP |
| `SMTP_PASSWORD` | `smtp_password` | Authelia (email notifications) |
## Access Control
Default policy: **deny**. Per-service access via LLDAP groups (e.g. `pez_grafana_users`).
Domains covered: `*.pez.sh` and `*.pez.solutions` (mirrors).
## Deployment
1. Decrypt secrets: `sops -d config.enc.yml > /tmp/secrets.yml`
2. Write each key as a file to `/root/authelia/secrets/<FILENAME>`
3. Copy `configuration.yml` to `/root/authelia/config/`
4. Copy `docker-compose.yml` to `/root/authelia/`
5. `docker compose up -d`
> **Note:** The current deployment lives at `/root/authelia/` (not `/opt/docker/authelia/`).
> The Ansible `docker_services` role deploys to `/opt/docker/` — if adding authelia
> to `docker_services` in host_vars, the paths in docker-compose.yml or the deploy
> target would need to be reconciled.

View file

@ -0,0 +1,34 @@
#ENC[AES256_GCM,data:f4XAqp11z+JcQ1A2e7rsATxc+tnVCWg/JtBkcLxHcZU=,iv:ZoV32fp3Qqa4NpfCxGIr9aBN58WWGKsPY4ejLAYHpSA=,tag:sGNmx6TLU8c33TKrgq97cg==,type:comment]
#ENC[AES256_GCM,data:EkK1hWr3EBWDTlmOKyLZRnHSNApZIGjik/ZzXcU2toSYu/E7DvXi8w6SXZYXN1tIbhH/r/4QF2PF,iv:0RWjiWaYqfbnTpWreP0Fsa1U4bTMosxSK4DLBxTuu0s=,tag:2UrxDFqaRyfIiHo02xonmA==,type:comment]
#ENC[AES256_GCM,data:CsMgleHImUSz6KC4BEc4M96s1Y+m/hAmNnAL3C/cAwYcnWhj7YDR1n2iHqPYEEWkSwhpoLYGzRJdlpEjyIPEgetSo7utb3su,iv:zoX+VKI1RC2gYpdY92cN5hVc372clLJu/mve8x2atiM=,tag:UG6fi/dyK3Y9nWyvdWA3Ow==,type:comment]
#ENC[AES256_GCM,data:cfPV8c2VcH8rBzzU11bvmYZ8OO3SmIXGO1Tig0SEiO/FjI/dOPmIGNSgTmNUk00XRD5dNtXLeUd1fznodrOanzCWxTiVAI1F,iv:cBGoO9Mr7dYUpqPB1ioIB2sNAdLalcKY7z3jfPG6NKc=,tag:Teit2AbaY+PLus1PIpUwpg==,type:comment]
#ENC[AES256_GCM,data:vAB4tNVW8eh1bYXw60nwDEMQ9DjNayzNCN/R20om795rqyspx0X1MO3I+W7k2LzW/n1xJ+KtksAEZsdhXO4K3CvgPKZxJAY=,iv:NEp+L8uh0Kr5r8Uns/Lo3j/hO6Sd5f7T6b7RvZjcmdM=,tag:hJH+A2UWHUj8EZkwEHN7Aw==,type:comment]
#ENC[AES256_GCM,data:Z1fFA2XXQrPMQNzDZRkxc9gUuWql2W9Tbhg/TMVsHUphrhfSVzH8Am99BQ==,iv:NXIeTN9RiesRo3MKJS5L4rNHCaekD66NXG1DB86OzaE=,tag:TwpWL/BmfmDp+zAqQEa1mg==,type:comment]
jwt_secret: ENC[AES256_GCM,data:L7A61rbtbIHsvhXuvZ/pVTJV0opnbR69IxXCaxi9BttYik0xi3INq2mD82VSw6llVf9QwguXK/hiVoFkZ11rdrNmxXInBwDQeSRPcQ==,iv:XSJEte+tdUNv5046HVRdjJsPiqhFvSzs0NIG6KrONLg=,tag:9yZXEM2gHwz8qP3rzSYg1Q==,type:str]
session_secret: ENC[AES256_GCM,data:geGxpL6UOF6OzmWAt+QP9lGSBEFWuZ5iKEvsk6N8U79p2/sc2dTNyrbovM6nSDaFsL0C5aSdVdbJIW1eplyk8OQFrf19Z8B3lzTvzA==,iv:VVxm2VXX82U4HFtJs/gOVQYd8+q/eFRoP5ZcHssKgvs=,tag:MB10TdI6tSYNfVl0qe+6Sw==,type:str]
storage_encryption_key: ENC[AES256_GCM,data:JvNDndLFS+kgnv5p5bDQXNSwyXdBJQu7+UI5kEu3U9NCg1RJfTY9tNnHeOZ43Ijy8SK56JndzEG7yH/pRqUawVwRxvvTNGK1kGJmCg==,iv:a69EyxwABrKyXfEHPx3bZ9YxDzk50L+xy6WCb93vG4M=,tag:yXwVyh5Vh67WtuNzXgtYJg==,type:str]
mysql_password: ENC[AES256_GCM,data:gYdzDcN1nzxGLKm5kVTCxDwGiSNk3OYG/0p7yyCgqxyRZ60zGwYx65BJRlg=,iv:XioHoIF1w1+mUnYQpBi/YlIVp/wv2ESWg/TBUgWS4XA=,tag:qtjJq/Qxk02JqmGcXIsgIw==,type:str]
mysql_root_password: ENC[AES256_GCM,data:JZ7N/EA3loQ1L1Cu5JwTANkHuuItBf3UpkEa9ZEJkJmqArtjAwmjJm1tpLc=,iv:Xmqyg2iT2n1g/8yKORWg1e/W7xGlbCCBkvgP6LSkTSM=,tag:PQIsjOf87Y0WdOIc+ToT9A==,type:str]
lldap_admin_password: ENC[AES256_GCM,data:xcSWQzn3YuKYGylXAApzC1C7jGzn+MXV1f6fveQGpLVSKqkRK64cJqHfX1k=,iv:8Y8BIaETeOpYn4yRx8mILfR7h32mjlZqu0b0VbcKxXg=,tag:OqHtG648vAhWaI1p54KB6A==,type:str]
lldap_jwt_secret: ENC[AES256_GCM,data:2LOlBwoj8IeZZhRUKs+4BIN46cDJYVLi3a3nJj7tY81RK40cEBrRdSP9Aak=,iv:QKQl3wkg9nli0wtemOxOido1EvozUjiXCDtdnKaDllw=,tag:htQHhRCkT9TguP6vizwEbA==,type:str]
smtp_password: ENC[AES256_GCM,data:JLpSehtbZDwo,iv:fNQViKgcm399HrH51QbOWZld4vAfFxlMbq2PFd9sBD8=,tag:KJ29U5QopPThw/iqUCNYvQ==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1r8uh2w2qad2z5sgq9q7l73962q2sp8zz9hdnh6sjuvanxl565vmswn8squ
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJWndmK2Y1L05IQURqbFJl
c1ZpNFpiakEvUldzL2RxckZ1YXdsbExwTFJnCk54eFRJTEw0V1I1QWRyZFBHU3Z2
ekZUcCtaWVFZdVRoOGlTb3R0bTB4bmcKLS0tIGZyNFNOb01wRk5xbW1YanFZSlUr
TXE0dXNFV2tDd041TUpKblFUWmpvT0kKXCoy2S2gkB1329x9vYVq5xh+j8hc+daL
oMt05DKN7v3uMe8ScFnXGdoAq72HbVQRZE+46fTl02JsNH1787/6rw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-03-28T17:39:56Z"
mac: ENC[AES256_GCM,data:1g6jER1nu+DlDIXL89Y8nm5sat/Ig20wdcfWEr4QzPpos+HOMtGLlFC98lZWLMd63wkBpP9U+lbj7Rx66AJ7lJIjaiTXcNRR9awjLu0/9voNOmzZ0aaa120xH3KnYMI7aHBBfBUtUQzcHa/1vRhS5zLVMzDBEEK9qdsxvQWEGc0=,iv:14WPvCdQXBBmq6OSLBNrHf/v9h8KOp0/4MFYLy3PZQQ=,tag:50+ZhmZIOmN99MR3Q+/NDQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.9.4

View file

@ -1,10 +0,0 @@
# Example: services/authelia/config.enc.yml
# Authelia secrets — encrypt with: sops -e -i config.enc.yml
---
jwt_secret: CHANGEME
session_secret: CHANGEME
storage_encryption_key: CHANGEME
lldap_admin_password: CHANGEME
mariadb_root_password: CHANGEME
mariadb_authelia_password: CHANGEME
oidc_hmac_secret: CHANGEME

View file

@ -0,0 +1,151 @@
---
###############################################################################
## Authelia Configuration — pez.sh ##
###############################################################################
# Host: helsinki-a (100.67.6.27)
# URL: https://auth.pez.sh
#
# Secrets are mounted via Docker environment variables pointing to /secrets/.
# The LDAP bind password and SMTP password are referenced from the same
# secrets directory. See config.enc.yml for encrypted values.
#
# This file is deployed to /root/authelia/config/configuration.yml
server:
address: 'tcp://:9091/'
log:
level: 'info'
format: 'text'
file_path: '/config/authelia.log'
keep_stdout: true
identity_validation:
reset_password:
##
## Authentication Backend — LLDAP
##
authentication_backend:
ldap:
address: 'ldap://lldap:3890'
implementation: 'lldap'
timeout: '20 seconds'
start_tls: false
base_dn: 'dc=pez,dc=sh'
additional_users_dn: 'ou=people'
additional_groups_dn: 'ou=groups'
user: 'cn=admin,ou=people,dc=pez,dc=sh'
# Password provided via AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE env var
##
## Access Control — default deny, per-service groups
##
access_control:
default_policy: 'deny'
rules:
# pez.sh domains
- domain: 'grafana.pez.sh'
subject: 'group:pez_grafana_users'
policy: 'one_factor'
- domain: 'prometheus.pez.sh'
subject: 'group:pez_prometheus_users'
policy: 'one_factor'
- domain: 'radarr.pez.sh'
subject: 'group:pez_radarr_users'
policy: 'one_factor'
- domain: 'sonarr.pez.sh'
subject: 'group:pez_sonarr_users'
policy: 'one_factor'
- domain: 'lidarr.pez.sh'
subject: 'group:pez_lidarr_users'
policy: 'one_factor'
- domain: 'readarr.pez.sh'
subject: 'group:pez_readarr_users'
policy: 'one_factor'
- domain: 'download.pez.sh'
subject: 'group:pez_download_users'
policy: 'one_factor'
- domain: 'rss.pez.sh'
subject: 'group:pez_rss_users'
policy: 'one_factor'
- domain: 'soulseek.pez.sh'
subject: 'group:pez_soulseek_users'
policy: 'one_factor'
- domain: 'prowlarr.pez.sh'
subject: 'group:pez_prowlarr_users'
policy: 'one_factor'
- domain: 'git.pez.sh'
subject: 'group:pez_git_users'
policy: 'one_factor'
# pez.solutions domains (mirrors)
- domain: 'grafana.pez.solutions'
subject: 'group:pez_grafana_users'
policy: 'one_factor'
- domain: 'prometheus.pez.solutions'
subject: 'group:pez_prometheus_users'
policy: 'one_factor'
- domain: 'radarr.pez.solutions'
subject: 'group:pez_radarr_users'
policy: 'one_factor'
- domain: 'sonarr.pez.solutions'
subject: 'group:pez_sonarr_users'
policy: 'one_factor'
- domain: 'lidarr.pez.solutions'
subject: 'group:pez_lidarr_users'
policy: 'one_factor'
- domain: 'readarr.pez.solutions'
subject: 'group:pez_readarr_users'
policy: 'one_factor'
- domain: 'download.pez.solutions'
subject: 'group:pez_download_users'
policy: 'one_factor'
- domain: 'soulseek.pez.solutions'
subject: 'group:pez_soulseek_users'
policy: 'one_factor'
- domain: 'prowlarr.pez.solutions'
subject: 'group:pez_prowlarr_users'
policy: 'one_factor'
# Shared apps portals
- domain: 'apps.pez.sh'
subject: 'group:pez_plebs'
policy: 'one_factor'
- domain: 'apps.pez.solutions'
subject: 'group:pez_plebs'
policy: 'one_factor'
##
## Session — cookie domains
##
session:
cookies:
- domain: 'pez.sh'
authelia_url: 'https://auth.pez.sh'
- domain: 'pez.solutions'
authelia_url: 'https://auth.pez.solutions'
##
## Storage — MariaDB
##
storage:
mysql:
address: 'tcp://mariadb:3306'
database: 'authelia'
username: 'authelia'
timeout: '10 seconds'
# Password provided via AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE env var
##
## Notifier — SMTP via poste.io on nuremberg-a
##
notifier:
disable_startup_check: true
smtp:
address: 'smtp://mail.pez.sh'
username: 'pez'
# Password provided via AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE env var
sender: 'Authelia <pez@pez.sh>'
tls:
server_name: 'mail.pez.sh'

View file

@ -16,6 +16,8 @@ services:
AUTHELIA_SESSION_SECRET_FILE: /secrets/SESSION_SECRET AUTHELIA_SESSION_SECRET_FILE: /secrets/SESSION_SECRET
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: /secrets/STORAGE_ENCRYPTION_KEY AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: /secrets/STORAGE_ENCRYPTION_KEY
AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE: /secrets/MYSQL_PASSWORD AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE: /secrets/MYSQL_PASSWORD
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE: /secrets/LLDAP_ADMIN_PASSWORD
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE: /secrets/SMTP_PASSWORD
TZ: UTC TZ: UTC
volumes: volumes:
- /root/authelia/config:/config - /root/authelia/config:/config