feat: добавить аддон authelia — SSO forward-auth и OIDC provider
Helm chart + Ansible role для Authelia 4.38: - Forward-auth для ingress-nginx через аннотации auth-url/auth-signin - OIDC provider: Gitea, Grafana, ArgoCD, MinIO, Vault, Nextcloud - SQLite default или PostgreSQL; опциональный Redis для сессий - RSA ключ OIDC генерируется автоматически если не задан в vault - ConfigMap authelia-forward-auth с готовыми аннотациями для любого сервиса - README: install, users, protect service, OIDC per-service, debug, test
This commit is contained in:
6
Makefile
6
Makefile
@@ -58,7 +58,7 @@ DOCKER_RUN := docker run --rm -it \
|
||||
addon-harbor addon-gitea addon-owncloud addon-nextcloud \
|
||||
addon-csi-s3 addon-csi-ceph addon-csi-glusterfs addon-vaultwarden \
|
||||
addon-smtp-relay addon-vault addon-external-secrets \
|
||||
addon-jenkins addon-netbird addon-mediaserver addon-hysteria2-server addon-splitgw addon-ingress-proxypass addon-ingress-add-domains addon-yandex-dns-controller addon-technitium-dns \
|
||||
addon-jenkins addon-netbird addon-mediaserver addon-hysteria2-server addon-splitgw addon-ingress-proxypass addon-ingress-add-domains addon-yandex-dns-controller addon-technitium-dns addon-authelia \
|
||||
add-node remove-node \
|
||||
add-etcd-node remove-etcd-node \
|
||||
etcd-backup etcd-restore etcd-list-snapshots \
|
||||
@@ -436,6 +436,10 @@ addon-technitium-dns: _check_env _check_image ## Technitium DNS HA — Primary+S
|
||||
@printf "$(CYAN)Устанавливаю Technitium DNS HA...$(NC)\n"
|
||||
$(DOCKER_RUN) addon technitium-dns $(ARGS)
|
||||
|
||||
addon-authelia: _check_env _check_image ## Authelia SSO — Forward-auth + OIDC provider
|
||||
@printf "$(CYAN)Устанавливаю Authelia SSO...$(NC)\n"
|
||||
$(DOCKER_RUN) addon authelia $(ARGS)
|
||||
|
||||
# Generic цель — любой аддон из addons/<name>/playbook.yml
|
||||
addon-%: _check_env _check_image
|
||||
@if [ ! -f "addons/$*/playbook.yml" ]; then \
|
||||
|
||||
494
addons/authelia/README.md
Normal file
494
addons/authelia/README.md
Normal file
@@ -0,0 +1,494 @@
|
||||
# authelia
|
||||
|
||||
Self-hosted authentication system providing **forward-auth** for ingress-nginx and an **OIDC provider** for Gitea, Grafana, ArgoCD, MinIO, Vault, and Nextcloud.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
User → ingress-nginx → (auth-url) → Authelia :9091 → allowed/denied
|
||||
↓
|
||||
Authelia portal
|
||||
auth.home.local
|
||||
↓
|
||||
users_database.yml
|
||||
(argon2id passwords)
|
||||
|
||||
OIDC flow:
|
||||
Service → Authelia /api/oidc/authorization → login → token → Service
|
||||
```
|
||||
|
||||
```
|
||||
Traffic routes:
|
||||
auth.home.local → authelia ClusterIP :9091 (Ingress, NO forward-auth)
|
||||
sonarr.home.local → sonarr service (Ingress + forward-auth annotations)
|
||||
gitea.home.local → gitea service (Ingress, no forward-auth — OIDC handles it)
|
||||
|
||||
Kubernetes objects:
|
||||
Deployment: authelia
|
||||
Service: authelia (ClusterIP :9091)
|
||||
Secrets: authelia-secrets (jwt, session, storage_encryption, oidc keys)
|
||||
authelia-config (configuration.yml — contains OIDC client secrets)
|
||||
authelia-users (users_database.yml)
|
||||
PVC: authelia-data (SQLite db + notification.txt)
|
||||
Ingress: authelia (auth.home.local)
|
||||
ConfigMap: authelia-forward-auth (copy-paste annotation reference)
|
||||
Deployment: authelia-redis (optional, redis.enabled=true)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Installation
|
||||
|
||||
### Step 1 — Generate secrets
|
||||
|
||||
Run these commands and save the output:
|
||||
```bash
|
||||
# Core secrets
|
||||
openssl rand -base64 64 # → authelia_jwt_secret
|
||||
openssl rand -base64 64 # → authelia_session_secret
|
||||
openssl rand -base64 32 # → authelia_storage_encryption_key
|
||||
openssl rand -base64 48 # → authelia_oidc_hmac_secret
|
||||
|
||||
# OIDC client secrets (one per enabled client)
|
||||
openssl rand -hex 32 # → authelia_oidc_secret_gitea
|
||||
openssl rand -hex 32 # → authelia_oidc_secret_grafana
|
||||
```
|
||||
|
||||
> The **OIDC RSA private key** is auto-generated during deploy. Leave `authelia_oidc_private_key: ""` in vault. After first deploy, retrieve and save it (see note at end of deploy output).
|
||||
|
||||
### Step 2 — Generate admin password hash
|
||||
|
||||
```bash
|
||||
docker run --rm authelia/authelia:latest authelia hash-password 'your-password'
|
||||
# Output: $argon2id$v=19$m=65536,t=3,p=4$...
|
||||
```
|
||||
|
||||
Copy the full `$argon2id$...` string.
|
||||
|
||||
### Step 3 — Edit vault.yml
|
||||
|
||||
```yaml
|
||||
# group_vars/all/vault.yml (ansible-vault encrypted)
|
||||
authelia_jwt_secret: "<output of openssl rand -base64 64>"
|
||||
authelia_session_secret: "<output of openssl rand -base64 64>"
|
||||
authelia_storage_encryption_key: "<output of openssl rand -base64 32>"
|
||||
authelia_oidc_hmac_secret: "<output of openssl rand -base64 48>"
|
||||
authelia_oidc_private_key: "" # auto-generated on first deploy
|
||||
|
||||
authelia_oidc_secret_gitea: "<output of openssl rand -hex 32>"
|
||||
authelia_oidc_secret_grafana: "<output of openssl rand -hex 32>"
|
||||
|
||||
authelia_user_admin_password_hash: "$argon2id$v=19$m=65536,t=3,p=4$..."
|
||||
```
|
||||
|
||||
### Step 4 — Configure addons.yml
|
||||
|
||||
```yaml
|
||||
# group_vars/all/addons.yml
|
||||
addon_authelia: true
|
||||
|
||||
authelia_host: "auth.home.local"
|
||||
authelia_domain: "home.local"
|
||||
|
||||
# OIDC clients to enable
|
||||
authelia_oidc_gitea_enabled: true
|
||||
authelia_oidc_grafana_enabled: true
|
||||
|
||||
# Domains to protect
|
||||
authelia_protected_domains:
|
||||
- sonarr.home.local
|
||||
- radarr.home.local
|
||||
- lidarr.home.local
|
||||
- prowlarr.home.local
|
||||
- pgadmin.home.local
|
||||
|
||||
# Domains requiring admin group
|
||||
authelia_admin_domains:
|
||||
- argocd.home.local
|
||||
- vault.home.local
|
||||
|
||||
# Public bypass (no auth)
|
||||
authelia_bypass_domains:
|
||||
- plex.home.local
|
||||
```
|
||||
|
||||
### Step 5 — Deploy
|
||||
|
||||
```bash
|
||||
make addon-authelia
|
||||
```
|
||||
|
||||
### Step 6 — Add DNS record
|
||||
|
||||
Add `auth.home.local` pointing to the kube-vip/ingress-nginx IP in Technitium DNS (or your DNS server).
|
||||
|
||||
---
|
||||
|
||||
## 2. Managing users
|
||||
|
||||
### Add a new user
|
||||
|
||||
1. Generate password hash:
|
||||
```bash
|
||||
docker run --rm authelia/authelia:latest authelia hash-password 'newpassword'
|
||||
```
|
||||
|
||||
2. Add to `group_vars/all/addons.yml`:
|
||||
```yaml
|
||||
authelia_users:
|
||||
admin:
|
||||
displayname: "Administrator"
|
||||
email: "admin@home.local"
|
||||
groups: [admins, users]
|
||||
alice:
|
||||
displayname: "Alice"
|
||||
email: "alice@home.local"
|
||||
groups: [users]
|
||||
```
|
||||
|
||||
3. Add to `vault.yml`:
|
||||
```yaml
|
||||
authelia_user_alice_password_hash: "$argon2id$..."
|
||||
```
|
||||
|
||||
4. Redeploy: `make addon-authelia`
|
||||
|
||||
### Groups
|
||||
|
||||
- `admins` — access to `authelia_admin_domains` (ArgoCD, Vault, Harbor, Dashboard)
|
||||
- `users` — access to `authelia_protected_domains` (Sonarr, Radarr, etc.)
|
||||
|
||||
OIDC claims include the `groups` scope, so Grafana/Gitea can use group-based role mapping.
|
||||
|
||||
---
|
||||
|
||||
## 3. Protect a new service with forward-auth
|
||||
|
||||
### Step 1 — Add annotations to the service's Ingress
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: myservice
|
||||
namespace: myservice
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
# ── Authelia forward-auth ──────────────────────────────────────────────
|
||||
nginx.ingress.kubernetes.io/auth-url: "http://authelia.authelia.svc.cluster.local:9091/api/authz/forward-auth"
|
||||
nginx.ingress.kubernetes.io/auth-signin: "http://auth.home.local/?rd=$scheme://$host$escaped_request_uri"
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: "Remote-User,Remote-Name,Remote-Groups,Remote-Email"
|
||||
nginx.ingress.kubernetes.io/auth-snippet: |
|
||||
proxy_set_header X-Forwarded-Method $request_method;
|
||||
spec:
|
||||
rules:
|
||||
- host: myservice.home.local
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: myservice
|
||||
port:
|
||||
number: 8080
|
||||
```
|
||||
|
||||
> Get the exact URLs for your deployment:
|
||||
> ```bash
|
||||
> kubectl get cm authelia-forward-auth -n authelia -o jsonpath='{.data.annotations\.yaml}'
|
||||
> ```
|
||||
|
||||
### Step 2 — Add to access control
|
||||
|
||||
Add `myservice.home.local` to the appropriate list in `addons.yml`:
|
||||
```yaml
|
||||
authelia_protected_domains:
|
||||
- myservice.home.local
|
||||
# ... existing entries
|
||||
```
|
||||
|
||||
Then redeploy: `make addon-authelia`
|
||||
|
||||
---
|
||||
|
||||
## 4. OIDC client configuration per service
|
||||
|
||||
### Gitea
|
||||
|
||||
In Gitea Admin → Site Administration → Authentication Sources → Add OAuth2:
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Name | `Authelia` |
|
||||
| OAuth2 Provider | `OpenID Connect` |
|
||||
| Client ID | `gitea` |
|
||||
| Client Secret | value of `authelia_oidc_secret_gitea` in vault |
|
||||
| OpenID Connect Auto Discovery URL | `http://auth.home.local/.well-known/openid-configuration` |
|
||||
| Scopes | `openid profile email groups` |
|
||||
|
||||
Restart Gitea. Users can now click "Sign in with Authelia" on the login page.
|
||||
|
||||
Optional — auto-create users and map admin group:
|
||||
```ini
|
||||
# app.ini
|
||||
[oauth2]
|
||||
USERNAME = preferred_username
|
||||
UPDATE_AVATAR = true
|
||||
|
||||
[openid]
|
||||
ENABLE_OPENID_SIGNIN = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Grafana
|
||||
|
||||
Add to Grafana's `grafana.ini` (or Helm values):
|
||||
|
||||
```ini
|
||||
[auth.generic_oauth]
|
||||
enabled = true
|
||||
name = Authelia
|
||||
client_id = grafana
|
||||
client_secret = <authelia_oidc_secret_grafana from vault>
|
||||
scopes = openid profile email groups
|
||||
auth_url = http://auth.home.local/api/oidc/authorization
|
||||
token_url = http://auth.home.local/api/oidc/token
|
||||
api_url = http://auth.home.local/api/oidc/userinfo
|
||||
|
||||
# Role mapping via Authelia groups
|
||||
role_attribute_path = contains(groups[*], 'admins') && 'GrafanaAdmin' || 'Viewer'
|
||||
allow_sign_up = true
|
||||
```
|
||||
|
||||
Or in Helm values for the prometheus-stack addon:
|
||||
```yaml
|
||||
# group_vars/all/addons.yml
|
||||
prometheus_grafana_oauth_enabled: true
|
||||
prometheus_grafana_oauth_client_id: "grafana"
|
||||
prometheus_grafana_oauth_client_secret: "..." # from vault
|
||||
prometheus_grafana_oauth_auth_url: "http://auth.home.local/api/oidc/authorization"
|
||||
prometheus_grafana_oauth_token_url: "http://auth.home.local/api/oidc/token"
|
||||
prometheus_grafana_oauth_api_url: "http://auth.home.local/api/oidc/userinfo"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ArgoCD
|
||||
|
||||
Enable ArgoCD OIDC in `argocd-cm` ConfigMap:
|
||||
```yaml
|
||||
# values override for argocd Helm chart
|
||||
server:
|
||||
config:
|
||||
oidc.config: |
|
||||
name: Authelia
|
||||
issuer: http://auth.home.local
|
||||
clientID: argocd
|
||||
clientSecret: $oidc.authelia.clientSecret
|
||||
requestedScopes:
|
||||
- openid
|
||||
- profile
|
||||
- email
|
||||
- groups
|
||||
requestedIDTokenClaims:
|
||||
groups:
|
||||
essential: true
|
||||
```
|
||||
|
||||
Map Authelia groups to ArgoCD RBAC:
|
||||
```yaml
|
||||
# argocd-rbac-cm
|
||||
policy.csv: |
|
||||
g, admins, role:admin
|
||||
policy.default: role:readonly
|
||||
```
|
||||
|
||||
Store the secret in a K8s Secret `argocd-secret`:
|
||||
```yaml
|
||||
data:
|
||||
oidc.authelia.clientSecret: <base64 of authelia_oidc_secret_argocd>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### MinIO
|
||||
|
||||
In MinIO Console → Identity → OpenID:
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Config URL | `http://auth.home.local/.well-known/openid-configuration` |
|
||||
| Client ID | `minio` |
|
||||
| Client Secret | value of `authelia_oidc_secret_minio` |
|
||||
| Claim Name | `groups` |
|
||||
| Scopes | `openid,profile,email` |
|
||||
|
||||
Map Authelia group `admins` to MinIO policy `consoleAdmin`.
|
||||
|
||||
---
|
||||
|
||||
### Vault
|
||||
|
||||
Configure Vault OIDC auth method:
|
||||
```bash
|
||||
vault auth enable oidc
|
||||
|
||||
vault write auth/oidc/config \
|
||||
oidc_discovery_url="http://auth.home.local" \
|
||||
oidc_client_id="vault" \
|
||||
oidc_client_secret="<authelia_oidc_secret_vault>" \
|
||||
default_role="reader"
|
||||
|
||||
vault write auth/oidc/role/reader \
|
||||
bound_audiences="vault" \
|
||||
allowed_redirect_uris="https://vault.home.local/ui/vault/auth/oidc/oidc/callback" \
|
||||
allowed_redirect_uris="https://vault.home.local/oidc/callback" \
|
||||
user_claim="sub" \
|
||||
groups_claim="groups" \
|
||||
token_policies="default"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Debugging auth issues
|
||||
|
||||
### Check Authelia logs (real-time)
|
||||
|
||||
```bash
|
||||
kubectl -n authelia logs -l app.kubernetes.io/name=authelia -f --tail=100
|
||||
```
|
||||
|
||||
Common log messages:
|
||||
- `"ALLOW"` — request was allowed
|
||||
- `"DENY"` — request was denied (check domain in `protectedDomains`)
|
||||
- `"Redirecting"` — unauthenticated user redirected to login
|
||||
- `"POST /api/firstfactor"` — login attempt
|
||||
|
||||
### Verify forward-auth endpoint is reachable
|
||||
|
||||
From within the cluster:
|
||||
```bash
|
||||
kubectl run curl-test --rm -it --image=curlimages/curl -- \
|
||||
curl -v http://authelia.authelia.svc.cluster.local:9091/api/health
|
||||
# Expected: {"status":"OK"}
|
||||
```
|
||||
|
||||
### Test that a domain's annotations are applied
|
||||
|
||||
```bash
|
||||
kubectl get ingress sonarr -n mediaserver -o yaml | grep auth-url
|
||||
```
|
||||
|
||||
### Check current access control configuration
|
||||
|
||||
```bash
|
||||
kubectl -n authelia exec deploy/authelia -- \
|
||||
cat /config/configuration.yml | grep -A 30 "access_control:"
|
||||
```
|
||||
|
||||
### Check active sessions (SQLite)
|
||||
|
||||
```bash
|
||||
kubectl -n authelia exec deploy/authelia -- \
|
||||
sqlite3 /data/db.sqlite3 "SELECT subject, ip, last_activity FROM user_opaque_identifier LIMIT 20;"
|
||||
```
|
||||
|
||||
### Notification log (if SMTP disabled)
|
||||
|
||||
```bash
|
||||
kubectl -n authelia exec deploy/authelia -- cat /data/notification.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Test the login flow
|
||||
|
||||
### Forward-auth flow (e.g., Sonarr)
|
||||
|
||||
1. Open `http://sonarr.home.local/` in a private browser window
|
||||
2. Should redirect to `http://auth.home.local/?rd=http%3A%2F%2Fsonarr.home.local%2F`
|
||||
3. Enter credentials → should redirect back to Sonarr
|
||||
4. Access granted
|
||||
|
||||
Test from command line:
|
||||
```bash
|
||||
# Step 1: login request (expect redirect to Authelia)
|
||||
curl -I http://sonarr.home.local/
|
||||
# Expected: 302 to auth.home.local
|
||||
|
||||
# Step 2: verify endpoint directly
|
||||
curl -I -H "X-Forwarded-Host: sonarr.home.local" \
|
||||
-H "X-Forwarded-URI: /" \
|
||||
-H "X-Forwarded-Proto: http" \
|
||||
http://authelia.authelia.svc.cluster.local:9091/api/authz/forward-auth
|
||||
# Expected: 401 (unauthenticated) or 200 (if session cookie provided)
|
||||
```
|
||||
|
||||
### OIDC flow (e.g., Gitea)
|
||||
|
||||
1. Open `http://gitea.home.local/user/oauth2/Authelia`
|
||||
(or click "Sign in with Authelia" on Gitea login page)
|
||||
2. Should redirect to Authelia login form
|
||||
3. Login with admin credentials
|
||||
4. Authelia redirects back to Gitea with authorization code
|
||||
5. Gitea exchanges code for token — user is logged in
|
||||
|
||||
### Check OIDC discovery endpoint
|
||||
|
||||
```bash
|
||||
curl -s http://auth.home.local/.well-known/openid-configuration | jq .
|
||||
# Should return JSON with issuer, authorization_endpoint, token_endpoint, etc.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Variables reference
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `authelia_host` | `auth.home.local` | Portal hostname |
|
||||
| `authelia_domain` | `home.local` | Session cookie domain |
|
||||
| `authelia_theme` | `dark` | UI theme |
|
||||
| `authelia_two_factor_enabled` | `false` | Require TOTP/WebAuthn |
|
||||
| `authelia_storage_type` | `sqlite` | `sqlite` or `postgresql` |
|
||||
| `authelia_redis_enabled` | `false` | Built-in Redis for sessions |
|
||||
| `authelia_smtp_enabled` | `false` | SMTP for 2FA/password-reset emails |
|
||||
| `authelia_oidc_enabled` | `true` | Enable OIDC provider |
|
||||
| `authelia_oidc_gitea_enabled` | `true` | Gitea OIDC client |
|
||||
| `authelia_oidc_grafana_enabled` | `true` | Grafana OIDC client |
|
||||
| `authelia_oidc_argocd_enabled` | `false` | ArgoCD OIDC client |
|
||||
| `authelia_oidc_minio_enabled` | `false` | MinIO OIDC client |
|
||||
| `authelia_oidc_vault_enabled` | `false` | Vault OIDC client |
|
||||
| `authelia_ingress_tls_enabled` | `false` | TLS on auth portal |
|
||||
| `authelia_protected_domains` | `[sonarr, radarr…]` | Domains requiring login |
|
||||
| `authelia_admin_domains` | `[argocd, vault…]` | Admin-only domains |
|
||||
| `authelia_bypass_domains` | `[]` | Public bypass domains |
|
||||
| `authelia_oidc_domains` | `[gitea, grafana, minio]` | OIDC bypass (forward-auth off) |
|
||||
|
||||
### Vault secrets required
|
||||
|
||||
| Variable | Notes |
|
||||
|----------|-------|
|
||||
| `authelia_jwt_secret` | min 64 chars — `openssl rand -base64 64` |
|
||||
| `authelia_session_secret` | min 64 chars |
|
||||
| `authelia_storage_encryption_key` | min 20 chars — `openssl rand -base64 32` |
|
||||
| `authelia_oidc_hmac_secret` | min 32 chars — `openssl rand -base64 48` |
|
||||
| `authelia_oidc_private_key` | RSA PEM — leave empty, auto-generated |
|
||||
| `authelia_oidc_secret_gitea` | `openssl rand -hex 32` |
|
||||
| `authelia_oidc_secret_grafana` | `openssl rand -hex 32` |
|
||||
| `authelia_user_admin_password_hash` | argon2id hash from `authelia hash-password` |
|
||||
|
||||
---
|
||||
|
||||
## Saving the auto-generated OIDC private key
|
||||
|
||||
After the first deploy, save the key to vault for reproducibility:
|
||||
|
||||
```bash
|
||||
kubectl -n authelia get secret authelia-secrets \
|
||||
-o jsonpath='{.data.oidc_private_key}' | base64 -d
|
||||
```
|
||||
|
||||
Paste the PEM output into `vault.yml` as `authelia_oidc_private_key: |` (multiline YAML).
|
||||
7
addons/authelia/playbook.yml
Normal file
7
addons/authelia/playbook.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Install Authelia SSO
|
||||
hosts: k3s_master[0]
|
||||
gather_facts: false
|
||||
become: true
|
||||
roles:
|
||||
- role: "{{ playbook_dir }}/role"
|
||||
21
addons/authelia/role/chart/Chart.yaml
Normal file
21
addons/authelia/role/chart/Chart.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
apiVersion: v2
|
||||
name: authelia
|
||||
description: |
|
||||
Self-hosted authentication portal with forward-auth for ingress-nginx
|
||||
and OIDC provider for Gitea, Grafana, ArgoCD, MinIO, Vault, Nextcloud.
|
||||
Supports file-based users, SQLite/PostgreSQL storage, optional Redis sessions.
|
||||
type: application
|
||||
version: 1.0.0
|
||||
appVersion: "4.38.17"
|
||||
keywords:
|
||||
- authentication
|
||||
- sso
|
||||
- oidc
|
||||
- forward-auth
|
||||
- 2fa
|
||||
home: https://www.authelia.com
|
||||
sources:
|
||||
- https://github.com/authelia/authelia
|
||||
- https://git.antropoff.ru/DevOpsTools/K3S
|
||||
maintainers:
|
||||
- name: k3s-ansible
|
||||
203
addons/authelia/role/chart/files/configuration.yml.tpl
Normal file
203
addons/authelia/role/chart/files/configuration.yml.tpl
Normal file
@@ -0,0 +1,203 @@
|
||||
---
|
||||
# Authelia configuration — rendered by Helm via tpl()
|
||||
# Secrets (jwt, session, storage_encryption, oidc_hmac, oidc_private_key)
|
||||
# are injected via AUTHELIA_*_FILE environment variables — not in this file.
|
||||
|
||||
server:
|
||||
host: 0.0.0.0
|
||||
port: 9091
|
||||
path: ""
|
||||
buffers:
|
||||
read: 4096
|
||||
write: 4096
|
||||
timeouts:
|
||||
read: 6s
|
||||
write: 6s
|
||||
idle: 30s
|
||||
|
||||
log:
|
||||
level: info
|
||||
format: text
|
||||
|
||||
theme: {{ .Values.theme | quote }}
|
||||
|
||||
totp:
|
||||
disable: false
|
||||
issuer: {{ .Values.totp.issuer | quote }}
|
||||
algorithm: sha1
|
||||
digits: 6
|
||||
period: {{ .Values.totp.period }}
|
||||
skew: {{ .Values.totp.skew }}
|
||||
secret_size: 32
|
||||
|
||||
webauthn:
|
||||
disable: false
|
||||
display_name: {{ .Values.domain | quote }}
|
||||
attestation_conveyance_preference: indirect
|
||||
user_verification: preferred
|
||||
timeout: 60s
|
||||
|
||||
authentication_backend:
|
||||
password_reset:
|
||||
disable: false
|
||||
refresh_interval: 5m
|
||||
file:
|
||||
path: /config/users_database.yml
|
||||
watch: false
|
||||
password:
|
||||
algorithm: argon2id
|
||||
iterations: 3
|
||||
memory: 65536
|
||||
parallelism: 4
|
||||
key_length: 32
|
||||
salt_length: 16
|
||||
|
||||
session:
|
||||
name: {{ .Values.session.name | quote }}
|
||||
domain: {{ .Values.session.domain | quote }}
|
||||
same_site: {{ .Values.session.sameSite | quote }}
|
||||
expiration: {{ .Values.session.expiration | quote }}
|
||||
inactivity: {{ .Values.session.inactivity | quote }}
|
||||
remember_me_duration: {{ .Values.session.rememberMeDuration | quote }}
|
||||
{{- if .Values.redis.enabled }}
|
||||
redis:
|
||||
host: {{ printf "%s-redis" (include "authelia.name" .) | quote }}
|
||||
port: 6379
|
||||
{{- end }}
|
||||
|
||||
regulation:
|
||||
max_retries: 3
|
||||
find_time: 2m
|
||||
ban_time: 5m
|
||||
|
||||
storage:
|
||||
{{- if eq .Values.storage.type "postgresql" }}
|
||||
postgres:
|
||||
host: {{ .Values.storage.postgresql.host | quote }}
|
||||
port: {{ .Values.storage.postgresql.port }}
|
||||
database: {{ .Values.storage.postgresql.database | quote }}
|
||||
schema: {{ .Values.storage.postgresql.schema | quote }}
|
||||
username: {{ .Values.storage.postgresql.username | quote }}
|
||||
tls:
|
||||
skip_verify: true
|
||||
{{- else }}
|
||||
local:
|
||||
path: {{ .Values.storage.sqlite.path | quote }}
|
||||
{{- end }}
|
||||
|
||||
notifier:
|
||||
disable_startup_check: true
|
||||
{{- if .Values.notifier.smtp.enabled }}
|
||||
smtp:
|
||||
host: {{ .Values.notifier.smtp.host | quote }}
|
||||
port: {{ .Values.notifier.smtp.port }}
|
||||
username: {{ .Values.notifier.smtp.username | quote }}
|
||||
sender: {{ .Values.notifier.smtp.sender | quote }}
|
||||
tls:
|
||||
skip_verify: {{ .Values.notifier.smtp.tls.skipVerify }}
|
||||
{{- else }}
|
||||
filesystem:
|
||||
filename: /data/notification.txt
|
||||
{{- end }}
|
||||
|
||||
access_control:
|
||||
default_policy: {{ .Values.accessControl.defaultPolicy | quote }}
|
||||
rules:
|
||||
|
||||
# Authelia portal — always bypass (prevents auth loop)
|
||||
- domain: {{ .Values.authHost | quote }}
|
||||
policy: bypass
|
||||
|
||||
# Health check endpoints — bypass for monitoring
|
||||
- domain: "*.{{ .Values.domain }}"
|
||||
resources:
|
||||
- "^/healthz(.*)$"
|
||||
- "^/api/healthz(.*)$"
|
||||
- "^/health$"
|
||||
policy: bypass
|
||||
|
||||
{{- if .Values.accessControl.bypassDomains }}
|
||||
# Public services — no authentication required
|
||||
- domain:
|
||||
{{- range .Values.accessControl.bypassDomains }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
policy: bypass
|
||||
{{- end }}
|
||||
|
||||
{{- if .Values.accessControl.oidcDomains }}
|
||||
# OIDC-enabled services — bypass forward-auth (OIDC handles authentication)
|
||||
- domain:
|
||||
{{- range .Values.accessControl.oidcDomains }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
policy: bypass
|
||||
{{- end }}
|
||||
|
||||
{{- if .Values.accessControl.adminDomains }}
|
||||
# Admin-only services — require 'admins' group
|
||||
- domain:
|
||||
{{- range .Values.accessControl.adminDomains }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
subject:
|
||||
- "group:admins"
|
||||
policy: {{ if .Values.twoFactor.enabled }}two_factor{{ else }}one_factor{{ end }}
|
||||
{{- end }}
|
||||
|
||||
{{- if .Values.accessControl.protectedDomains }}
|
||||
# Protected services — login required
|
||||
- domain:
|
||||
{{- range .Values.accessControl.protectedDomains }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
policy: {{ if .Values.twoFactor.enabled }}two_factor{{ else }}one_factor{{ end }}
|
||||
{{- end }}
|
||||
|
||||
{{- if .Values.oidc.enabled }}
|
||||
identity_providers:
|
||||
oidc:
|
||||
access_token_lifespan: {{ .Values.oidc.accessTokenLifespan | quote }}
|
||||
authorize_code_lifespan: {{ .Values.oidc.authorizeCodeLifespan | quote }}
|
||||
id_token_lifespan: {{ .Values.oidc.idTokenLifespan | quote }}
|
||||
refresh_token_lifespan: {{ .Values.oidc.refreshTokenLifespan | quote }}
|
||||
enable_client_debug_messages: false
|
||||
minimum_parameter_entropy: 8
|
||||
cors:
|
||||
endpoints:
|
||||
- authorization
|
||||
- token
|
||||
- revocation
|
||||
- introspection
|
||||
allowed_origins_from_client_redirect_uris: true
|
||||
clients:
|
||||
{{- range $name, $client := .Values.oidc.clients }}
|
||||
{{- if $client.enabled }}
|
||||
- id: {{ $client.id | quote }}
|
||||
description: {{ $client.description | default $name | quote }}
|
||||
# $plaintext$ prefix — Authelia 4.38+ plain-text client secret marker
|
||||
secret: {{ printf "$plaintext$%s" $client.secret | quote }}
|
||||
public: false
|
||||
authorization_policy: one_factor
|
||||
scopes:
|
||||
{{- range $client.scopes }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
redirect_uris:
|
||||
{{- range $client.redirectUris }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
grant_types:
|
||||
{{- range $client.grantTypes }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
response_types:
|
||||
- code
|
||||
response_modes:
|
||||
- form_post
|
||||
- query
|
||||
- fragment
|
||||
userinfo_signing_algorithm: none
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
56
addons/authelia/role/chart/templates/NOTES.txt
Normal file
56
addons/authelia/role/chart/templates/NOTES.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ Authelia SSO — Deployed ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
|
||||
Portal: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.authHost }}/
|
||||
Namespace: {{ .Release.Namespace }}
|
||||
OIDC: {{ if .Values.oidc.enabled }}enabled{{ else }}disabled{{ end }}
|
||||
Redis: {{ if .Values.redis.enabled }}enabled{{ else }}disabled (memory sessions){{ end }}
|
||||
Storage: {{ .Values.storage.type }}
|
||||
|
||||
─── Protect a new service ──────────────────────────────────────
|
||||
|
||||
Add to its Ingress:
|
||||
nginx.ingress.kubernetes.io/auth-url: "http://{{ include "authelia.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:9091/api/authz/forward-auth"
|
||||
nginx.ingress.kubernetes.io/auth-signin: "http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.authHost }}/?rd=$scheme://$host$escaped_request_uri"
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: "Remote-User,Remote-Name,Remote-Groups,Remote-Email"
|
||||
nginx.ingress.kubernetes.io/auth-snippet: "proxy_set_header X-Forwarded-Method $request_method;"
|
||||
|
||||
Or get the full reference:
|
||||
kubectl get cm {{ include "authelia.name" . }}-forward-auth -n {{ .Release.Namespace }} -o jsonpath='{.data.annotations\.yaml}'
|
||||
|
||||
─── OIDC Issuer ────────────────────────────────────────────────
|
||||
|
||||
http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.authHost }}
|
||||
|
||||
Discovery: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.authHost }}/.well-known/openid-configuration
|
||||
|
||||
─── Logs / Debug ───────────────────────────────────────────────
|
||||
|
||||
kubectl -n {{ .Release.Namespace }} logs -l app.kubernetes.io/name={{ include "authelia.name" . }} -f
|
||||
|
||||
─── First login ────────────────────────────────────────────────
|
||||
|
||||
Open: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.authHost }}/
|
||||
User: admin (or as configured in authelia_users)
|
||||
Pass: the plaintext password whose hash you set in vault.yml
|
||||
|
||||
─── Access control rules ────────────────────────────────────────
|
||||
{{- if .Values.accessControl.protectedDomains }}
|
||||
Protected (login required):
|
||||
{{- range .Values.accessControl.protectedDomains }}
|
||||
- {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.accessControl.adminDomains }}
|
||||
Admin-only (group: admins):
|
||||
{{- range .Values.accessControl.adminDomains }}
|
||||
- {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.accessControl.bypassDomains }}
|
||||
Bypass (public):
|
||||
{{- range .Values.accessControl.bypassDomains }}
|
||||
- {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
37
addons/authelia/role/chart/templates/_helpers.tpl
Normal file
37
addons/authelia/role/chart/templates/_helpers.tpl
Normal file
@@ -0,0 +1,37 @@
|
||||
{{- define "authelia.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "authelia.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "authelia.labels" -}}
|
||||
helm.sh/chart: {{ include "authelia.chart" . }}
|
||||
{{ include "authelia.selectorLabels" . }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "authelia.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "authelia.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Forward-auth URL for use in ingress-nginx annotations.
|
||||
Returns the full URL to Authelia's authz endpoint inside the cluster.
|
||||
*/}}
|
||||
{{- define "authelia.forwardAuthUrl" -}}
|
||||
{{- printf "http://%s.%s.svc.cluster.local:9091/api/authz/forward-auth" (include "authelia.name" .) .Release.Namespace }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Sign-in URL for redirecting unauthenticated users.
|
||||
*/}}
|
||||
{{- define "authelia.signinUrl" -}}
|
||||
{{- if .Values.ingress.tls.enabled -}}
|
||||
{{- printf "https://%s/?rd=$scheme://$host$escaped_request_uri" .Values.authHost }}
|
||||
{{- else -}}
|
||||
{{- printf "http://%s/?rd=$scheme://$host$escaped_request_uri" .Values.authHost }}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
# Reference ConfigMap: forward-auth annotations for ingress-nginx.
|
||||
# Copy-paste these annotations onto any Ingress you want to protect with Authelia.
|
||||
# Usage: kubectl get cm authelia-forward-auth -n {{ .Release.Namespace }} -o yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}-forward-auth
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
data:
|
||||
# ── Paste these onto protected Ingress resources ─────────────────────────────
|
||||
annotations.yaml: |
|
||||
# Required on EVERY protected Ingress:
|
||||
nginx.ingress.kubernetes.io/auth-url: "{{ include "authelia.forwardAuthUrl" . }}"
|
||||
nginx.ingress.kubernetes.io/auth-signin: "{{ include "authelia.signinUrl" . }}"
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: "Remote-User,Remote-Name,Remote-Groups,Remote-Email"
|
||||
nginx.ingress.kubernetes.io/auth-snippet: |
|
||||
proxy_set_header X-Forwarded-Method $request_method;
|
||||
|
||||
# ── OIDC Issuer URL (for configuring OIDC clients) ────────────────────────────
|
||||
oidc-issuer: |
|
||||
{{ if .Values.ingress.tls.enabled }}https{{ else }}http{{ end }}://{{ .Values.authHost }}
|
||||
|
||||
# ── OIDC discovery endpoint ───────────────────────────────────────────────────
|
||||
oidc-discovery: |
|
||||
{{ if .Values.ingress.tls.enabled }}https{{ else }}http{{ end }}://{{ .Values.authHost }}/.well-known/openid-configuration
|
||||
|
||||
# ── Quick reference: protect a new service ────────────────────────────────────
|
||||
howto: |
|
||||
To protect myservice.home.local:
|
||||
|
||||
1. Add these annotations to the Service's Ingress:
|
||||
nginx.ingress.kubernetes.io/auth-url: "{{ include "authelia.forwardAuthUrl" . }}"
|
||||
nginx.ingress.kubernetes.io/auth-signin: "{{ include "authelia.signinUrl" . }}"
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: "Remote-User,Remote-Name,Remote-Groups,Remote-Email"
|
||||
|
||||
2. Add the domain to accessControl.protectedDomains in addons.yml and re-run:
|
||||
make addon-authelia
|
||||
|
||||
3. Add to Technitium DNS (or /etc/hosts):
|
||||
<kube-vip-IP> myservice.home.local
|
||||
115
addons/authelia/role/chart/templates/deployment.yaml
Normal file
115
addons/authelia/role/chart/templates/deployment.yaml
Normal file
@@ -0,0 +1,115 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: 1
|
||||
# Recreate required for ReadWriteOnce PVC (SQLite)
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "authelia.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "authelia.selectorLabels" . | nindent 8 }}
|
||||
annotations:
|
||||
# Force pod restart when config/users secrets change
|
||||
checksum/config: {{ tpl (.Files.Get "files/configuration.yml.tpl") . | sha256sum }}
|
||||
checksum/secrets: {{ .Values.secrets | toJson | sha256sum }}
|
||||
checksum/users: {{ .Values.users | toJson | sha256sum }}
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 8000
|
||||
runAsGroup: 8000
|
||||
fsGroup: 8000
|
||||
containers:
|
||||
- name: authelia
|
||||
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
command:
|
||||
- authelia
|
||||
- --config=/config/configuration.yml
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 9091
|
||||
protocol: TCP
|
||||
env:
|
||||
# Core secrets — read from mounted files
|
||||
- name: AUTHELIA_JWT_SECRET_FILE
|
||||
value: /secrets/jwt_secret
|
||||
- name: AUTHELIA_SESSION_SECRET_FILE
|
||||
value: /secrets/session_secret
|
||||
- name: AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE
|
||||
value: /secrets/storage_encryption_key
|
||||
{{- if .Values.oidc.enabled }}
|
||||
- name: AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE
|
||||
value: /secrets/oidc_hmac_secret
|
||||
- name: AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE
|
||||
value: /secrets/oidc_private_key
|
||||
{{- end }}
|
||||
{{- if eq .Values.storage.type "postgresql" }}
|
||||
- name: AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE
|
||||
value: /secrets/db_password
|
||||
{{- end }}
|
||||
{{- if .Values.notifier.smtp.enabled }}
|
||||
- name: AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE
|
||||
value: /secrets/smtp_password
|
||||
{{- end }}
|
||||
- name: TZ
|
||||
value: Europe/Moscow
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
readOnly: true
|
||||
- name: users
|
||||
mountPath: /config/users_database.yml
|
||||
subPath: users_database.yml
|
||||
readOnly: true
|
||||
- name: secrets
|
||||
mountPath: /secrets
|
||||
readOnly: true
|
||||
- name: data
|
||||
mountPath: /data
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 9091
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 9091
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
failureThreshold: 3
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 9091
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
failureThreshold: 12
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumes:
|
||||
- name: config
|
||||
secret:
|
||||
secretName: {{ include "authelia.name" . }}-config
|
||||
- name: users
|
||||
secret:
|
||||
secretName: {{ include "authelia.name" . }}-users
|
||||
- name: secrets
|
||||
secret:
|
||||
secretName: {{ include "authelia.name" . }}-secrets
|
||||
defaultMode: 0400
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "authelia.name" . }}-data
|
||||
36
addons/authelia/role/chart/templates/ingress.yaml
Normal file
36
addons/authelia/role/chart/templates/ingress.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
{{- if .Values.ingress.enabled }}
|
||||
---
|
||||
# Authelia portal ingress — accessible at authHost
|
||||
# No forward-auth annotation here (would cause an auth loop)
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: {{ .Values.ingress.ingressClass | quote }}
|
||||
nginx.ingress.kubernetes.io/proxy-buffer-size: "128k"
|
||||
{{- if .Values.ingress.tls.certManager.enabled }}
|
||||
cert-manager.io/cluster-issuer: {{ .Values.ingress.tls.certManager.issuer | quote }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.tls.enabled }}
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ .Values.authHost | quote }}
|
||||
secretName: {{ .Values.ingress.tls.secretName | default (printf "%s-tls" (include "authelia.name" .)) | quote }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: {{ .Values.authHost | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "authelia.name" . }}
|
||||
port:
|
||||
number: 9091
|
||||
{{- end }}
|
||||
18
addons/authelia/role/chart/templates/pvc.yaml
Normal file
18
addons/authelia/role/chart/templates/pvc.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
# PVC for Authelia data directory: SQLite database, notification log
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}-data
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
{{- if .Values.storage.storageClassName }}
|
||||
storageClassName: {{ .Values.storage.storageClassName | quote }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.storage.size }}
|
||||
64
addons/authelia/role/chart/templates/redis.yaml
Normal file
64
addons/authelia/role/chart/templates/redis.yaml
Normal file
@@ -0,0 +1,64 @@
|
||||
{{- if .Values.redis.enabled }}
|
||||
---
|
||||
# Redis Deployment — session storage for Authelia (optional but recommended)
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}-redis
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: redis
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "authelia.selectorLabels" . | nindent 6 }}
|
||||
app.kubernetes.io/component: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "authelia.selectorLabels" . | nindent 8 }}
|
||||
app.kubernetes.io/component: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: {{ .Values.redis.image }}
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- redis-server
|
||||
- --save ""
|
||||
- --appendonly no
|
||||
ports:
|
||||
- name: redis
|
||||
containerPort: 6379
|
||||
protocol: TCP
|
||||
resources:
|
||||
{{- toYaml .Values.redis.resources | nindent 12 }}
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: [redis-cli, ping]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}-redis
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: redis
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
{{- include "authelia.selectorLabels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: redis
|
||||
ports:
|
||||
- name: redis
|
||||
port: 6379
|
||||
targetPort: 6379
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
14
addons/authelia/role/chart/templates/secret-config.yaml
Normal file
14
addons/authelia/role/chart/templates/secret-config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
# Authelia configuration.yml stored as Secret because it contains OIDC client secrets.
|
||||
# Rendered via Helm tpl() from files/configuration.yml.tpl
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}-config
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
configuration.yml: |
|
||||
{{ tpl (.Files.Get "files/configuration.yml.tpl") . | indent 4 }}
|
||||
24
addons/authelia/role/chart/templates/secret-main.yaml
Normal file
24
addons/authelia/role/chart/templates/secret-main.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
# Authelia core secrets — mounted as files, read via AUTHELIA_*_FILE env vars.
|
||||
# Contains: jwt_secret, session_secret, storage_encryption_key, oidc_hmac_secret,
|
||||
# oidc_private_key (RSA PEM), db_password (optional), smtp_password (optional)
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}-secrets
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
jwt_secret: {{ .Values.secrets.jwtSecret | quote }}
|
||||
session_secret: {{ .Values.secrets.sessionSecret | quote }}
|
||||
storage_encryption_key: {{ .Values.secrets.storageEncryptionKey | quote }}
|
||||
oidc_hmac_secret: {{ .Values.secrets.oidcHmacSecret | quote }}
|
||||
oidc_private_key: {{ .Values.secrets.oidcPrivateKey | quote }}
|
||||
{{- if eq .Values.storage.type "postgresql" }}
|
||||
db_password: {{ .Values.secrets.dbPassword | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.notifier.smtp.enabled }}
|
||||
smtp_password: {{ .Values.secrets.smtpPassword | quote }}
|
||||
{{- end }}
|
||||
25
addons/authelia/role/chart/templates/secret-users.yaml
Normal file
25
addons/authelia/role/chart/templates/secret-users.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
# Authelia users_database.yml — file-based authentication backend.
|
||||
# Passwords must be Argon2id hashes (see README for generation command).
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}-users
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
users_database.yml: |
|
||||
users:
|
||||
{{- range $username, $user := .Values.users }}
|
||||
{{ $username }}:
|
||||
disabled: {{ $user.disabled | default false }}
|
||||
displayname: {{ $user.displayname | quote }}
|
||||
password: {{ $user.password | quote }}
|
||||
email: {{ $user.email | quote }}
|
||||
groups:
|
||||
{{- range $user.groups }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
17
addons/authelia/role/chart/templates/service.yaml
Normal file
17
addons/authelia/role/chart/templates/service.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "authelia.name" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "authelia.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
{{- include "authelia.selectorLabels" . | nindent 4 }}
|
||||
ports:
|
||||
- name: http
|
||||
port: 9091
|
||||
targetPort: 9091
|
||||
protocol: TCP
|
||||
229
addons/authelia/role/chart/values.yaml
Normal file
229
addons/authelia/role/chart/values.yaml
Normal file
@@ -0,0 +1,229 @@
|
||||
# Authelia — SSO + Forward Auth
|
||||
# Configure via: group_vars/all/addons.yml → authelia_* variables
|
||||
# Secrets in: group_vars/all/vault.yml → authelia_* secrets
|
||||
|
||||
image:
|
||||
repository: authelia/authelia
|
||||
tag: "4.38.17"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
# ── Domain ────────────────────────────────────────────────────────────────────
|
||||
# Base domain and public hostname for the Authelia portal
|
||||
domain: "home.local"
|
||||
authHost: "auth.home.local"
|
||||
|
||||
# ── Theme / UX ────────────────────────────────────────────────────────────────
|
||||
theme: dark # light | dark | grey | auto
|
||||
|
||||
# ── Secrets (all provided via Ansible vault, never hardcoded here) ────────────
|
||||
# These are placed in a dedicated K8s Secret and mounted as files.
|
||||
# Authelia reads them via AUTHELIA_*_FILE environment variables.
|
||||
secrets:
|
||||
jwtSecret: "" # min 64 chars — `openssl rand -base64 64`
|
||||
sessionSecret: "" # min 64 chars — `openssl rand -base64 64`
|
||||
storageEncryptionKey: "" # min 20 chars — `openssl rand -base64 32`
|
||||
oidcHmacSecret: "" # min 32 chars — `openssl rand -base64 48`
|
||||
oidcPrivateKey: "" # RSA-4096 PEM — auto-generated during deploy
|
||||
dbPassword: "" # only if storage.type=postgresql
|
||||
smtpPassword: "" # only if notifier.smtp.enabled
|
||||
|
||||
# ── Users (file-based auth backend) ──────────────────────────────────────────
|
||||
# Passwords must be Argon2id hashes.
|
||||
# Generate: docker run authelia/authelia:latest authelia hash-password 'yourpassword'
|
||||
# Or: authelia hash-password 'yourpassword' (if installed locally)
|
||||
users:
|
||||
admin:
|
||||
disabled: false
|
||||
displayname: "Administrator"
|
||||
password: "" # set in vault: authelia_user_admin_password_hash (argon2id)
|
||||
email: "admin@home.local"
|
||||
groups:
|
||||
- admins
|
||||
- users
|
||||
|
||||
# ── TOTP ──────────────────────────────────────────────────────────────────────
|
||||
totp:
|
||||
issuer: "home.local"
|
||||
period: 30
|
||||
skew: 1
|
||||
|
||||
# ── 2FA policy ────────────────────────────────────────────────────────────────
|
||||
# false = one_factor (password only) for protected/admin domains
|
||||
# true = two_factor (password + TOTP/WebAuthn) for protected/admin domains
|
||||
twoFactor:
|
||||
enabled: false
|
||||
|
||||
# ── Session ───────────────────────────────────────────────────────────────────
|
||||
session:
|
||||
name: authelia_session
|
||||
domain: "home.local"
|
||||
sameSite: lax
|
||||
expiration: 1h
|
||||
inactivity: 5m
|
||||
rememberMeDuration: 1M
|
||||
|
||||
# ── Storage ───────────────────────────────────────────────────────────────────
|
||||
storage:
|
||||
type: sqlite # sqlite | postgresql
|
||||
size: 1Gi
|
||||
storageClassName: ""
|
||||
sqlite:
|
||||
path: /data/db.sqlite3
|
||||
postgresql:
|
||||
host: "postgresql.postgresql.svc.cluster.local"
|
||||
port: 5432
|
||||
database: authelia
|
||||
username: authelia
|
||||
schema: public
|
||||
|
||||
# ── Redis (built-in, optional) ────────────────────────────────────────────────
|
||||
# When enabled, deploys a Redis sidecar for persistent session storage.
|
||||
# Recommended for production; not required for homelab.
|
||||
redis:
|
||||
enabled: false
|
||||
image: "redis:7-alpine"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 20m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
||||
|
||||
# ── Notifier ──────────────────────────────────────────────────────────────────
|
||||
notifier:
|
||||
# SMTP — for password reset and 2FA enrollment emails
|
||||
smtp:
|
||||
enabled: false
|
||||
host: ""
|
||||
port: 587
|
||||
username: ""
|
||||
sender: "authelia@home.local"
|
||||
tls:
|
||||
skipVerify: false
|
||||
# Filesystem — fallback: writes to /data/notification.txt (no email)
|
||||
filesystem:
|
||||
enabled: true
|
||||
|
||||
# ── Access Control ────────────────────────────────────────────────────────────
|
||||
# Rules evaluated top-to-bottom. First match wins.
|
||||
accessControl:
|
||||
defaultPolicy: deny
|
||||
|
||||
# Public services — no authentication required
|
||||
bypassDomains: []
|
||||
# - plex.home.local
|
||||
|
||||
# OIDC-enabled services — bypass forward-auth (OIDC handles its own auth)
|
||||
oidcDomains:
|
||||
- gitea.home.local
|
||||
- grafana.home.local
|
||||
- minio.home.local
|
||||
|
||||
# Services requiring admin group membership
|
||||
adminDomains:
|
||||
- argocd.home.local
|
||||
- vault.home.local
|
||||
- harbor.home.local
|
||||
- kubernetes-dashboard.home.local
|
||||
|
||||
# Services requiring login (one_factor or two_factor per twoFactor.enabled)
|
||||
protectedDomains:
|
||||
- sonarr.home.local
|
||||
- radarr.home.local
|
||||
- lidarr.home.local
|
||||
- bazarr.home.local
|
||||
- prowlarr.home.local
|
||||
- pgadmin.home.local
|
||||
- phpmyadmin.home.local
|
||||
|
||||
# ── OIDC Provider ─────────────────────────────────────────────────────────────
|
||||
oidc:
|
||||
enabled: true
|
||||
accessTokenLifespan: 1h
|
||||
authorizeCodeLifespan: 1m
|
||||
idTokenLifespan: 1h
|
||||
refreshTokenLifespan: 90m
|
||||
|
||||
clients:
|
||||
gitea:
|
||||
enabled: true
|
||||
id: gitea
|
||||
secret: "" # set in vault: authelia_oidc_secret_gitea
|
||||
description: "Gitea"
|
||||
redirectUris:
|
||||
- https://gitea.home.local/user/oauth2/Authelia/callback
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
|
||||
grafana:
|
||||
enabled: true
|
||||
id: grafana
|
||||
secret: "" # set in vault: authelia_oidc_secret_grafana
|
||||
description: "Grafana"
|
||||
redirectUris:
|
||||
- https://grafana.home.local/login/generic_oauth
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
|
||||
argocd:
|
||||
enabled: false
|
||||
id: argocd
|
||||
secret: "" # set in vault: authelia_oidc_secret_argocd
|
||||
description: "ArgoCD"
|
||||
redirectUris:
|
||||
- https://argocd.home.local/auth/callback
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
|
||||
minio:
|
||||
enabled: false
|
||||
id: minio
|
||||
secret: "" # set in vault: authelia_oidc_secret_minio
|
||||
description: "MinIO"
|
||||
redirectUris:
|
||||
- https://minio.home.local/oauth_callback
|
||||
scopes: [openid, profile, email]
|
||||
grantTypes: [authorization_code]
|
||||
|
||||
vault:
|
||||
enabled: false
|
||||
id: vault
|
||||
secret: "" # set in vault: authelia_oidc_secret_vault
|
||||
description: "Vault"
|
||||
redirectUris:
|
||||
- https://vault.home.local/ui/vault/auth/oidc/oidc/callback
|
||||
- https://vault.home.local/oidc/callback
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
|
||||
nextcloud:
|
||||
enabled: false
|
||||
id: nextcloud
|
||||
secret: "" # set in vault: authelia_oidc_secret_nextcloud
|
||||
description: "Nextcloud"
|
||||
redirectUris:
|
||||
- https://nextcloud.home.local/apps/user_oidc/code
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
|
||||
# ── Ingress ───────────────────────────────────────────────────────────────────
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClass: nginx
|
||||
tls:
|
||||
enabled: false
|
||||
secretName: ""
|
||||
certManager:
|
||||
enabled: false
|
||||
issuer: ""
|
||||
issuerKind: ClusterIssuer
|
||||
|
||||
# ── Resources ─────────────────────────────────────────────────────────────────
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
123
addons/authelia/role/defaults/main.yml
Normal file
123
addons/authelia/role/defaults/main.yml
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
# ── Namespace / release ───────────────────────────────────────────────────────
|
||||
authelia_namespace: authelia
|
||||
authelia_release_name: authelia
|
||||
|
||||
# ── Domain ────────────────────────────────────────────────────────────────────
|
||||
authelia_domain: "home.local"
|
||||
authelia_host: "auth.home.local"
|
||||
|
||||
# ── Theme ─────────────────────────────────────────────────────────────────────
|
||||
authelia_theme: dark # light | dark | grey | auto
|
||||
|
||||
# ── 2FA enforcement ───────────────────────────────────────────────────────────
|
||||
authelia_two_factor_enabled: false
|
||||
|
||||
# ── Session ───────────────────────────────────────────────────────────────────
|
||||
authelia_session_expiration: "1h"
|
||||
authelia_session_inactivity: "5m"
|
||||
authelia_session_remember_me: "1M"
|
||||
|
||||
# ── Storage ───────────────────────────────────────────────────────────────────
|
||||
authelia_storage_type: sqlite # sqlite | postgresql
|
||||
authelia_storage_size: 1Gi
|
||||
authelia_storage_class: ""
|
||||
|
||||
# PostgreSQL (only if authelia_storage_type=postgresql)
|
||||
authelia_db_host: "postgresql.postgresql.svc.cluster.local"
|
||||
authelia_db_port: 5432
|
||||
authelia_db_name: authelia
|
||||
authelia_db_user: authelia
|
||||
|
||||
# ── Redis session storage (optional) ─────────────────────────────────────────
|
||||
authelia_redis_enabled: false
|
||||
|
||||
# ── SMTP notifier (optional) ──────────────────────────────────────────────────
|
||||
authelia_smtp_enabled: false
|
||||
authelia_smtp_host: ""
|
||||
authelia_smtp_port: 587
|
||||
authelia_smtp_username: ""
|
||||
authelia_smtp_sender: "authelia@home.local"
|
||||
authelia_smtp_tls_skip_verify: false
|
||||
|
||||
# ── Ingress ───────────────────────────────────────────────────────────────────
|
||||
authelia_ingress_enabled: true
|
||||
authelia_ingress_class: nginx
|
||||
authelia_ingress_tls_enabled: false
|
||||
authelia_ingress_tls_secret: ""
|
||||
authelia_ingress_cert_manager_enabled: false
|
||||
authelia_ingress_cert_manager_issuer: ""
|
||||
|
||||
# ── OIDC provider ─────────────────────────────────────────────────────────────
|
||||
authelia_oidc_enabled: true
|
||||
|
||||
# Per-client toggles — secrets come from vault.yml
|
||||
authelia_oidc_gitea_enabled: true
|
||||
authelia_oidc_gitea_redirect: "https://gitea.home.local/user/oauth2/Authelia/callback"
|
||||
|
||||
authelia_oidc_grafana_enabled: true
|
||||
authelia_oidc_grafana_redirect: "https://grafana.home.local/login/generic_oauth"
|
||||
|
||||
authelia_oidc_argocd_enabled: false
|
||||
authelia_oidc_argocd_redirect: "https://argocd.home.local/auth/callback"
|
||||
|
||||
authelia_oidc_minio_enabled: false
|
||||
authelia_oidc_minio_redirect: "https://minio.home.local/oauth_callback"
|
||||
|
||||
authelia_oidc_vault_enabled: false
|
||||
authelia_oidc_vault_redirect_1: "https://vault.home.local/ui/vault/auth/oidc/oidc/callback"
|
||||
authelia_oidc_vault_redirect_2: "https://vault.home.local/oidc/callback"
|
||||
|
||||
authelia_oidc_nextcloud_enabled: false
|
||||
authelia_oidc_nextcloud_redirect: "https://nextcloud.home.local/apps/user_oidc/code"
|
||||
|
||||
# ── Access control ────────────────────────────────────────────────────────────
|
||||
authelia_bypass_domains: []
|
||||
# - plex.home.local
|
||||
|
||||
authelia_oidc_domains:
|
||||
- gitea.home.local
|
||||
- grafana.home.local
|
||||
- minio.home.local
|
||||
|
||||
authelia_admin_domains:
|
||||
- argocd.home.local
|
||||
- vault.home.local
|
||||
- harbor.home.local
|
||||
- kubernetes-dashboard.home.local
|
||||
|
||||
authelia_protected_domains:
|
||||
- sonarr.home.local
|
||||
- radarr.home.local
|
||||
- lidarr.home.local
|
||||
- bazarr.home.local
|
||||
- prowlarr.home.local
|
||||
- pgadmin.home.local
|
||||
- phpmyadmin.home.local
|
||||
|
||||
# ── Users ─────────────────────────────────────────────────────────────────────
|
||||
# Passwords are Argon2id hashes — set in vault.yml: authelia_user_*_password_hash
|
||||
# Generate: docker run authelia/authelia:latest authelia hash-password 'yourpassword'
|
||||
authelia_users:
|
||||
admin:
|
||||
displayname: "Administrator"
|
||||
email: "admin@home.local"
|
||||
groups:
|
||||
- admins
|
||||
- users
|
||||
|
||||
# ── Secrets — ALL must be set in vault.yml ────────────────────────────────────
|
||||
# authelia_jwt_secret: "" # openssl rand -base64 64
|
||||
# authelia_session_secret: "" # openssl rand -base64 64
|
||||
# authelia_storage_encryption_key: "" # openssl rand -base64 32
|
||||
# authelia_oidc_hmac_secret: "" # openssl rand -base64 48
|
||||
# authelia_oidc_private_key: "" # auto-generated during deploy if empty
|
||||
# authelia_oidc_secret_gitea: "" # openssl rand -hex 32
|
||||
# authelia_oidc_secret_grafana: "" # openssl rand -hex 32
|
||||
# authelia_oidc_secret_argocd: ""
|
||||
# authelia_oidc_secret_minio: ""
|
||||
# authelia_oidc_secret_vault: ""
|
||||
# authelia_oidc_secret_nextcloud: ""
|
||||
# authelia_user_admin_password_hash: "" # argon2id hash
|
||||
# authelia_db_password: "" # only if storage_type=postgresql
|
||||
# authelia_smtp_password: "" # only if smtp_enabled=true
|
||||
206
addons/authelia/role/tasks/main.yml
Normal file
206
addons/authelia/role/tasks/main.yml
Normal file
@@ -0,0 +1,206 @@
|
||||
---
|
||||
# ── Validate required secrets ─────────────────────────────────────────────────
|
||||
|
||||
- name: Validate Authelia required secrets are set
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- authelia_jwt_secret is defined and authelia_jwt_secret | length >= 64
|
||||
- authelia_session_secret is defined and authelia_session_secret | length >= 64
|
||||
- authelia_storage_encryption_key is defined and authelia_storage_encryption_key | length >= 20
|
||||
- authelia_oidc_hmac_secret is defined and authelia_oidc_hmac_secret | length >= 32
|
||||
fail_msg: >
|
||||
Required secrets are missing or too short in vault.yml.
|
||||
authelia_jwt_secret (min 64 chars) : openssl rand -base64 64
|
||||
authelia_session_secret (min 64 chars) : openssl rand -base64 64
|
||||
authelia_storage_encryption_key (min 20 chars): openssl rand -base64 32
|
||||
authelia_oidc_hmac_secret (min 32 chars) : openssl rand -base64 48
|
||||
|
||||
- name: Validate admin user password hash is set
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- authelia_user_admin_password_hash is defined
|
||||
- authelia_user_admin_password_hash | length > 0
|
||||
fail_msg: >
|
||||
authelia_user_admin_password_hash is not set in vault.yml.
|
||||
Generate with: docker run authelia/authelia:latest authelia hash-password 'yourpassword'
|
||||
Then paste the $argon2id$... hash into vault.yml
|
||||
|
||||
- name: Validate OIDC client secrets when OIDC is enabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- authelia_oidc_secret_gitea is defined and authelia_oidc_secret_gitea | length >= 16
|
||||
- authelia_oidc_secret_grafana is defined and authelia_oidc_secret_grafana | length >= 16
|
||||
fail_msg: >
|
||||
authelia_oidc_secret_gitea and authelia_oidc_secret_grafana must be set in vault.yml.
|
||||
Generate with: openssl rand -hex 32
|
||||
when: authelia_oidc_enabled | bool
|
||||
|
||||
# ── Generate OIDC RSA private key if not provided ────────────────────────────
|
||||
|
||||
- name: Check if OIDC private key is already set
|
||||
ansible.builtin.set_fact:
|
||||
_authelia_oidc_key_provided: >-
|
||||
{{ authelia_oidc_private_key is defined and
|
||||
authelia_oidc_private_key | length > 0 }}
|
||||
|
||||
- name: Generate RSA-4096 OIDC private key (if not in vault)
|
||||
ansible.builtin.command: openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096
|
||||
register: _generated_oidc_key
|
||||
changed_when: true
|
||||
no_log: true
|
||||
when:
|
||||
- authelia_oidc_enabled | bool
|
||||
- not _authelia_oidc_key_provided | bool
|
||||
|
||||
- name: Use generated OIDC private key
|
||||
ansible.builtin.set_fact:
|
||||
_authelia_oidc_private_key_final: "{{ _generated_oidc_key.stdout }}"
|
||||
no_log: true
|
||||
when:
|
||||
- authelia_oidc_enabled | bool
|
||||
- not _authelia_oidc_key_provided | bool
|
||||
|
||||
- name: Use vault-provided OIDC private key
|
||||
ansible.builtin.set_fact:
|
||||
_authelia_oidc_private_key_final: "{{ authelia_oidc_private_key }}"
|
||||
no_log: true
|
||||
when: _authelia_oidc_key_provided | bool
|
||||
|
||||
- name: Set empty OIDC private key (OIDC disabled)
|
||||
ansible.builtin.set_fact:
|
||||
_authelia_oidc_private_key_final: ""
|
||||
when: not (authelia_oidc_enabled | bool)
|
||||
|
||||
# ── Create namespace ──────────────────────────────────────────────────────────
|
||||
|
||||
- name: Create authelia namespace
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl create namespace {{ authelia_namespace }}
|
||||
--dry-run=client -o yaml | k3s kubectl apply -f -
|
||||
become: true
|
||||
changed_when: false
|
||||
|
||||
# ── Copy Helm chart ───────────────────────────────────────────────────────────
|
||||
|
||||
- name: Ensure chart temp directory is clean
|
||||
ansible.builtin.file:
|
||||
path: /tmp/authelia-chart
|
||||
state: absent
|
||||
become: true
|
||||
|
||||
- name: Create chart temp directory
|
||||
ansible.builtin.file:
|
||||
path: /tmp/authelia-chart
|
||||
state: directory
|
||||
mode: "0755"
|
||||
become: true
|
||||
|
||||
- name: Copy Helm chart to master
|
||||
ansible.builtin.copy:
|
||||
src: "{{ role_path }}/chart/"
|
||||
dest: /tmp/authelia-chart/
|
||||
mode: preserve
|
||||
become: true
|
||||
|
||||
# ── Template Helm values ──────────────────────────────────────────────────────
|
||||
|
||||
- name: Template Helm values
|
||||
ansible.builtin.template:
|
||||
src: values.yaml.j2
|
||||
dest: /tmp/authelia-values.yaml
|
||||
mode: "0600"
|
||||
become: true
|
||||
no_log: true
|
||||
|
||||
# ── Lint chart ────────────────────────────────────────────────────────────────
|
||||
|
||||
- name: Lint Helm chart
|
||||
ansible.builtin.command: >
|
||||
helm lint /tmp/authelia-chart
|
||||
--values /tmp/authelia-values.yaml
|
||||
become: true
|
||||
changed_when: false
|
||||
register: _helm_lint
|
||||
failed_when: _helm_lint.rc != 0
|
||||
|
||||
# ── Deploy chart ──────────────────────────────────────────────────────────────
|
||||
|
||||
- name: Deploy Authelia via Helm
|
||||
ansible.builtin.command: >
|
||||
helm upgrade --install {{ authelia_release_name }}
|
||||
/tmp/authelia-chart
|
||||
--namespace {{ authelia_namespace }}
|
||||
--values /tmp/authelia-values.yaml
|
||||
--atomic
|
||||
--wait
|
||||
--timeout 180s
|
||||
become: true
|
||||
register: _helm_result
|
||||
changed_when: true
|
||||
|
||||
# ── Cleanup ───────────────────────────────────────────────────────────────────
|
||||
|
||||
- name: Remove temp values file (contains secrets)
|
||||
ansible.builtin.file:
|
||||
path: /tmp/authelia-values.yaml
|
||||
state: absent
|
||||
become: true
|
||||
|
||||
# ── Wait for readiness ────────────────────────────────────────────────────────
|
||||
|
||||
- name: Wait for Authelia pod to be ready
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ authelia_namespace }} rollout status
|
||||
deployment/authelia --timeout=120s
|
||||
become: true
|
||||
changed_when: false
|
||||
|
||||
# ── Get status ────────────────────────────────────────────────────────────────
|
||||
|
||||
- name: Get pod status
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ authelia_namespace }} get pods,svc,ingress -o wide
|
||||
become: true
|
||||
changed_when: false
|
||||
register: _status
|
||||
|
||||
- name: Get forward-auth annotations reference
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl -n {{ authelia_namespace }} get cm authelia-forward-auth
|
||||
-o jsonpath='{.data.annotations\.yaml}'
|
||||
become: true
|
||||
changed_when: false
|
||||
register: _annotations
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────────────────────────
|
||||
|
||||
- name: "=== Authelia SSO Ready ==="
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "╔══════════════════════════════════════════════════════════════╗"
|
||||
- "║ Authelia SSO — Deployed ║"
|
||||
- "╚══════════════════════════════════════════════════════════════╝"
|
||||
- ""
|
||||
- " Portal : http{{ 's' if authelia_ingress_tls_enabled else '' }}://{{ authelia_host }}/"
|
||||
- " Namespace : {{ authelia_namespace }}"
|
||||
- " OIDC : {{ 'enabled' if authelia_oidc_enabled else 'disabled' }}"
|
||||
- " Storage : {{ authelia_storage_type }}"
|
||||
- " Redis : {{ 'enabled' if authelia_redis_enabled else 'disabled' }}"
|
||||
- ""
|
||||
- " ── Protect a new service (add to its Ingress) ──"
|
||||
- "{{ _annotations.stdout_lines | to_yaml }}"
|
||||
- ""
|
||||
- " ── OIDC Issuer URL ──"
|
||||
- " http{{ 's' if authelia_ingress_tls_enabled else '' }}://{{ authelia_host }}"
|
||||
- ""
|
||||
- " ── Pods / Services ──"
|
||||
- "{{ _status.stdout_lines | to_yaml }}"
|
||||
- ""
|
||||
- " Login: open http{{ 's' if authelia_ingress_tls_enabled else '' }}://{{ authelia_host }}/"
|
||||
- " user: admin | pass: <your vault plaintext password>"
|
||||
- ""
|
||||
- "{% if not _authelia_oidc_key_provided %}"
|
||||
- " ⚠ OIDC private key was AUTO-GENERATED. Save it to vault.yml for reproducibility:"
|
||||
- " kubectl -n {{ authelia_namespace }} get secret authelia-secrets \\"
|
||||
- " -o jsonpath='{.data.oidc_private_key}' | base64 -d"
|
||||
- "{% endif %}"
|
||||
154
addons/authelia/role/templates/values.yaml.j2
Normal file
154
addons/authelia/role/templates/values.yaml.j2
Normal file
@@ -0,0 +1,154 @@
|
||||
# Generated by Ansible — do not edit manually.
|
||||
# Configure via: group_vars/all/addons.yml → authelia_* variables
|
||||
# Secrets from: group_vars/all/vault.yml → authelia_* secrets
|
||||
|
||||
domain: {{ authelia_domain | quote }}
|
||||
authHost: {{ authelia_host | quote }}
|
||||
theme: {{ authelia_theme | quote }}
|
||||
|
||||
secrets:
|
||||
jwtSecret: {{ authelia_jwt_secret | quote }}
|
||||
sessionSecret: {{ authelia_session_secret | quote }}
|
||||
storageEncryptionKey: {{ authelia_storage_encryption_key | quote }}
|
||||
oidcHmacSecret: {{ authelia_oidc_hmac_secret | quote }}
|
||||
oidcPrivateKey: {{ _authelia_oidc_private_key_final | quote }}
|
||||
dbPassword: {{ authelia_db_password | default('') | quote }}
|
||||
smtpPassword: {{ authelia_smtp_password | default('') | quote }}
|
||||
|
||||
# Users — passwords are Argon2id hashes from vault.yml
|
||||
users:
|
||||
{% for username, user in authelia_users.items() %}
|
||||
{{ username }}:
|
||||
disabled: false
|
||||
displayname: {{ user.displayname | quote }}
|
||||
password: {{ vars['authelia_user_' + username + '_password_hash'] | default('') | quote }}
|
||||
email: {{ user.email | quote }}
|
||||
groups:
|
||||
{{ user.groups | to_yaml | indent(6, True) }}
|
||||
{% endfor %}
|
||||
|
||||
totp:
|
||||
issuer: {{ authelia_domain | quote }}
|
||||
period: 30
|
||||
skew: 1
|
||||
|
||||
twoFactor:
|
||||
enabled: {{ authelia_two_factor_enabled | string | lower }}
|
||||
|
||||
session:
|
||||
name: authelia_session
|
||||
domain: {{ authelia_domain | quote }}
|
||||
sameSite: lax
|
||||
expiration: {{ authelia_session_expiration | quote }}
|
||||
inactivity: {{ authelia_session_inactivity | quote }}
|
||||
rememberMeDuration: {{ authelia_session_remember_me | quote }}
|
||||
|
||||
storage:
|
||||
type: {{ authelia_storage_type | quote }}
|
||||
size: {{ authelia_storage_size | quote }}
|
||||
storageClassName: {{ authelia_storage_class | quote }}
|
||||
sqlite:
|
||||
path: /data/db.sqlite3
|
||||
postgresql:
|
||||
host: {{ authelia_db_host | quote }}
|
||||
port: {{ authelia_db_port }}
|
||||
database: {{ authelia_db_name | quote }}
|
||||
username: {{ authelia_db_user | quote }}
|
||||
schema: public
|
||||
|
||||
redis:
|
||||
enabled: {{ authelia_redis_enabled | string | lower }}
|
||||
|
||||
notifier:
|
||||
smtp:
|
||||
enabled: {{ authelia_smtp_enabled | string | lower }}
|
||||
host: {{ authelia_smtp_host | quote }}
|
||||
port: {{ authelia_smtp_port }}
|
||||
username: {{ authelia_smtp_username | quote }}
|
||||
sender: {{ authelia_smtp_sender | quote }}
|
||||
tls:
|
||||
skipVerify: {{ authelia_smtp_tls_skip_verify | string | lower }}
|
||||
|
||||
accessControl:
|
||||
defaultPolicy: deny
|
||||
bypassDomains:
|
||||
{{ authelia_bypass_domains | to_yaml | indent(4, True) }}
|
||||
oidcDomains:
|
||||
{{ authelia_oidc_domains | to_yaml | indent(4, True) }}
|
||||
adminDomains:
|
||||
{{ authelia_admin_domains | to_yaml | indent(4, True) }}
|
||||
protectedDomains:
|
||||
{{ authelia_protected_domains | to_yaml | indent(4, True) }}
|
||||
|
||||
oidc:
|
||||
enabled: {{ authelia_oidc_enabled | string | lower }}
|
||||
accessTokenLifespan: 1h
|
||||
authorizeCodeLifespan: 1m
|
||||
idTokenLifespan: 1h
|
||||
refreshTokenLifespan: 90m
|
||||
clients:
|
||||
gitea:
|
||||
enabled: {{ authelia_oidc_gitea_enabled | string | lower }}
|
||||
id: gitea
|
||||
secret: {{ authelia_oidc_secret_gitea | default('') | quote }}
|
||||
description: "Gitea"
|
||||
redirectUris:
|
||||
- {{ authelia_oidc_gitea_redirect | quote }}
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
grafana:
|
||||
enabled: {{ authelia_oidc_grafana_enabled | string | lower }}
|
||||
id: grafana
|
||||
secret: {{ authelia_oidc_secret_grafana | default('') | quote }}
|
||||
description: "Grafana"
|
||||
redirectUris:
|
||||
- {{ authelia_oidc_grafana_redirect | quote }}
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
argocd:
|
||||
enabled: {{ authelia_oidc_argocd_enabled | string | lower }}
|
||||
id: argocd
|
||||
secret: {{ authelia_oidc_secret_argocd | default('') | quote }}
|
||||
description: "ArgoCD"
|
||||
redirectUris:
|
||||
- {{ authelia_oidc_argocd_redirect | quote }}
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
minio:
|
||||
enabled: {{ authelia_oidc_minio_enabled | string | lower }}
|
||||
id: minio
|
||||
secret: {{ authelia_oidc_secret_minio | default('') | quote }}
|
||||
description: "MinIO"
|
||||
redirectUris:
|
||||
- {{ authelia_oidc_minio_redirect | quote }}
|
||||
scopes: [openid, profile, email]
|
||||
grantTypes: [authorization_code]
|
||||
vault:
|
||||
enabled: {{ authelia_oidc_vault_enabled | string | lower }}
|
||||
id: vault
|
||||
secret: {{ authelia_oidc_secret_vault | default('') | quote }}
|
||||
description: "Vault"
|
||||
redirectUris:
|
||||
- {{ authelia_oidc_vault_redirect_1 | quote }}
|
||||
- {{ authelia_oidc_vault_redirect_2 | quote }}
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
nextcloud:
|
||||
enabled: {{ authelia_oidc_nextcloud_enabled | string | lower }}
|
||||
id: nextcloud
|
||||
secret: {{ authelia_oidc_secret_nextcloud | default('') | quote }}
|
||||
description: "Nextcloud"
|
||||
redirectUris:
|
||||
- {{ authelia_oidc_nextcloud_redirect | quote }}
|
||||
scopes: [openid, profile, email, groups]
|
||||
grantTypes: [refresh_token, authorization_code]
|
||||
|
||||
ingress:
|
||||
enabled: {{ authelia_ingress_enabled | string | lower }}
|
||||
ingressClass: {{ authelia_ingress_class | quote }}
|
||||
tls:
|
||||
enabled: {{ authelia_ingress_tls_enabled | string | lower }}
|
||||
secretName: {{ authelia_ingress_tls_secret | quote }}
|
||||
certManager:
|
||||
enabled: {{ authelia_ingress_cert_manager_enabled | string | lower }}
|
||||
issuer: {{ authelia_ingress_cert_manager_issuer | quote }}
|
||||
@@ -45,6 +45,7 @@ addon_ingress_proxypass: false # External Services Ingress Proxy —
|
||||
addon_ingress_add_domains: false # Ingress-only — добавить домены к существующим сервисам кластера
|
||||
addon_yandex_dns_controller: false # Yandex 360 DNS controller — управление DNS через ConfigMap (safe mode)
|
||||
addon_technitium_dns: false # Technitium DNS HA — Primary+Secondary с kube-vip LB, зональный sync
|
||||
addon_authelia: false # Authelia SSO — Forward-auth + OIDC provider для всех сервисов
|
||||
|
||||
# ─── NFS Server ───────────────────────────────────────────────────────────────
|
||||
nfs_exports:
|
||||
@@ -381,6 +382,32 @@ minio_api_ingress_host: "s3.example.com"
|
||||
# technitium_dns_externaldns_policy: "upsert-only" # sync | upsert-only
|
||||
# technitium_dns_externaldns_txt_owner_id: "k3s-home"
|
||||
|
||||
# ─── Authelia SSO ────────────────────────────────────────────────────────────
|
||||
# Централизованная аутентификация: forward-auth для ingress-nginx + OIDC provider.
|
||||
# Все секреты — в vault.yml (authelia_jwt_secret, authelia_session_secret, и др.)
|
||||
# authelia_host: "auth.home.local" # URL портала авторизации
|
||||
# authelia_domain: "home.local" # базовый домен (session cookie domain)
|
||||
# authelia_two_factor_enabled: false # включить 2FA для защищённых сервисов
|
||||
# authelia_storage_type: "sqlite" # sqlite | postgresql
|
||||
# authelia_redis_enabled: false # Redis для хранения сессий
|
||||
# authelia_smtp_enabled: false # SMTP для сброса пароля и 2FA email
|
||||
# Домены с защитой (forward-auth):
|
||||
# authelia_protected_domains: [sonarr.home.local, radarr.home.local, ...]
|
||||
# authelia_admin_domains: [argocd.home.local, vault.home.local, ...]
|
||||
# authelia_bypass_domains: [plex.home.local]
|
||||
# OIDC клиенты (включить нужные):
|
||||
# authelia_oidc_gitea_enabled: true # + authelia_oidc_secret_gitea в vault
|
||||
# authelia_oidc_grafana_enabled: true # + authelia_oidc_secret_grafana в vault
|
||||
# authelia_oidc_argocd_enabled: false
|
||||
# authelia_oidc_minio_enabled: false
|
||||
# authelia_oidc_vault_enabled: false
|
||||
# Пользователи (хэши паролей — в vault.yml):
|
||||
# authelia_users:
|
||||
# admin:
|
||||
# displayname: "Administrator"
|
||||
# email: "admin@home.local"
|
||||
# groups: [admins, users]
|
||||
|
||||
# ─── etcd backup ──────────────────────────────────────────────────────────────
|
||||
etcd_backup_dir: "{{ k3s_data_dir }}/server/db/snapshots"
|
||||
etcd_backup_retention: 5 # сколько снимков хранить
|
||||
|
||||
@@ -135,3 +135,26 @@ yandex_dns:
|
||||
|
||||
# ── Technitium DNS HA ─────────────────────────────────────────────────────────
|
||||
technitium_dns_admin_password: "ЗАМЕНИ_НА_ПАРОЛЬ" # минимум 8 символов
|
||||
|
||||
# ── Authelia SSO ──────────────────────────────────────────────────────────────
|
||||
# Generate secrets:
|
||||
# openssl rand -base64 64 → jwt_secret, session_secret
|
||||
# openssl rand -base64 32 → storage_encryption_key
|
||||
# openssl rand -base64 48 → oidc_hmac_secret
|
||||
# openssl rand -hex 32 → each OIDC client secret
|
||||
# Generate password hash:
|
||||
# docker run authelia/authelia:latest authelia hash-password 'yourpassword'
|
||||
authelia_jwt_secret: "ЗАМЕНИ_openssl_rand_-base64_64"
|
||||
authelia_session_secret: "ЗАМЕНИ_openssl_rand_-base64_64"
|
||||
authelia_storage_encryption_key: "ЗАМЕНИ_openssl_rand_-base64_32"
|
||||
authelia_oidc_hmac_secret: "ЗАМЕНИ_openssl_rand_-base64_48"
|
||||
authelia_oidc_private_key: "" # оставь пустым — будет сгенерирован автоматически
|
||||
# OIDC client secrets (генерируй для каждого включённого клиента):
|
||||
authelia_oidc_secret_gitea: "ЗАМЕНИ_openssl_rand_-hex_32"
|
||||
authelia_oidc_secret_grafana: "ЗАМЕНИ_openssl_rand_-hex_32"
|
||||
authelia_oidc_secret_argocd: ""
|
||||
authelia_oidc_secret_minio: ""
|
||||
authelia_oidc_secret_vault: ""
|
||||
authelia_oidc_secret_nextcloud: ""
|
||||
# User password hashes (argon2id):
|
||||
authelia_user_admin_password_hash: "ЗАМЕНИ_НА_ARGON2ID_ХЭШ"
|
||||
|
||||
@@ -327,3 +327,11 @@
|
||||
when: addon_technitium_dns | default(false) | bool
|
||||
roles:
|
||||
- role: "{{ playbook_dir }}/../addons/technitium-dns/role"
|
||||
|
||||
- name: Install Authelia SSO
|
||||
hosts: k3s_master[0]
|
||||
gather_facts: false
|
||||
become: true
|
||||
when: addon_authelia | default(false) | bool
|
||||
roles:
|
||||
- role: "{{ playbook_dir }}/../addons/authelia/role"
|
||||
|
||||
Reference in New Issue
Block a user