Add Authentik SSO service and refactor Valkey configuration to use native tools and consolidated systemd service

This commit is contained in:
2025-11-22 21:36:23 +01:00
parent 500224b5de
commit d814369c99
21 changed files with 769 additions and 74 deletions

119
roles/authentik/README.md Normal file
View File

@@ -0,0 +1,119 @@
# Authentik Authentication Service Role
Containerized Authentik authentication service for rick-infra using Podman quadlets and following established architectural patterns.
## Features
-**Containerized deployment**: Uses Podman with systemd quadlets
-**Self-contained**: Manages its own database and configuration
-**Service-specific user**: Runs as dedicated `authentik` user with subuid/subgid
-**Infrastructure integration**: PostgreSQL database, Valkey cache, Caddy proxy
-**Bridge networking**: Container-to-host communication via host.containers.internal
-**Security hardened**: Rootless containers with proper isolation
-**Production ready**: HTTPS, proper health checks, restart policies
## Architecture
- **Dependencies**: PostgreSQL, Valkey, Podman, Caddy infrastructure roles
- **Database**: Self-managed authentik database and user in PostgreSQL
- **Cache**: Uses Valkey database 1 for session/cache storage
- **Containers**: 2-container pod (server + worker) deployed via quadlets
- **Network**: Custom bridge network with host gateway access
- **Web access**: https://auth.domain.com (via Caddy reverse proxy)
## Configuration
Key variables (defaults in `defaults/main.yml`):
```yaml
# Service
authentik_service_enabled: true
authentik_http_port: 9000
authentik_https_port: 9443
# Domain
authentik_subdomain: "auth"
authentik_domain: "{{ caddy_domain }}"
# Container
authentik_image_server: "ghcr.io/goauthentik/server"
authentik_image_tag: "2024.8.3"
authentik_user: "authentik"
# Database (self-managed)
authentik_db_name: "authentik"
authentik_db_user: "authentik"
authentik_db_password: "{{ vault_authentik_db_password }}"
# Cache (Valkey)
authentik_redis_host: "host.containers.internal"
authentik_redis_db: 1
# Security
authentik_secret_key: "{{ vault_authentik_secret_key }}"
```
## Container Architecture
### Pod Structure
```
authentik-pod
├── authentik-server (web interface, API)
└── authentik-worker (background tasks)
```
### Networking
- **Bridge network**: `authentik-net` with DNS resolution
- **Host gateway**: `host.containers.internal` → host system
- **Port mapping**: 9000:9000 (HTTP), 9443:9443 (HTTPS)
### Storage
- **Config**: `~authentik/.config/containers/` (quadlets, env)
- **Data**: `~authentik/data/` (persistent application data)
- **Media**: `~authentik/media/` (uploaded files)
## Usage
1. **Add vault secrets**: Set required vault variables in host_vars:
```yaml
vault_authentik_db_password: "secure_db_password"
vault_authentik_secret_key: "long_random_secret_key"
```
2. **Deploy**: `ansible-playbook site.yml --tags authentik`
3. **Access**: Visit https://auth.yourdomain.com/if/flow/initial-setup/
## Dependencies
- PostgreSQL infrastructure role (database)
- Valkey infrastructure role (cache)
- Podman infrastructure role (containers)
- Caddy web server (HTTPS reverse proxy)
- Vault secrets: `vault_authentik_db_password`, `vault_authentik_secret_key`
## Containerized Service Pattern
This role establishes rick-infra's containerized service pattern:
### User Management
- Service-specific user (`authentik`) with dedicated home directory
- Subuid/subgid ranges (100000-165535) for rootless containers
- Systemd user session with lingering enabled
### Container Integration
- Podman quadlets for native systemd integration
- Custom bridge networks with host gateway access
- Container-to-host connectivity via `host.containers.internal`
- Persistent storage mounted from user home directory
### Infrastructure Integration
- Self-managed database creation via PostgreSQL role
- Cache integration with Valkey infrastructure
- Reverse proxy deployment via Caddy sites-enabled
- Follows rick-infra self-contained service patterns
---
**Rick-Infra Authentik Service**
SSO authentication and authorization platform for modern applications.

View File

@@ -0,0 +1,107 @@
---
# =================================================================
# Authentik Authentication Service Role - Container Configuration
# =================================================================
# Containerized Authentik deployment using Podman quadlets
# Follows rick-infra patterns for pragmatic service deployment
# =================================================================
# Service Configuration
# =================================================================
# Service Management
authentik_service_enabled: true
authentik_service_state: "started"
# User and Container Configuration
authentik_user: "authentik"
authentik_group: "authentik"
authentik_home: "/var/lib/authentik"
authentik_subuid_start: 100000
authentik_subgid_start: 100000
authentik_subuid_size: 65536
authentik_subgid_size: 65536
# Container Configuration
authentik_image_server: "ghcr.io/goauthentik/server"
authentik_image_tag: "2024.8.3"
authentik_pod_name: "authentik"
authentik_network_name: "authentik-net"
# =================================================================
# Domain and Caddy Integration
# =================================================================
# Domain setup (follows rick-infra pattern)
authentik_subdomain: "auth"
authentik_domain: "{{ caddy_domain | default('localhost') }}"
authentik_full_domain: "{{ authentik_subdomain }}.{{ authentik_domain }}"
# Caddy integration
caddy_sites_enabled_dir: "/etc/caddy/sites-enabled"
# =================================================================
# Database Configuration (Self-Contained)
# =================================================================
# Authentik manages its own database
authentik_db_engine: "postgresql"
authentik_db_host: "host.containers.internal"
authentik_db_port: 5432
authentik_db_name: "authentik"
authentik_db_user: "authentik"
authentik_db_password: "{{ vault_authentik_db_password }}"
# =================================================================
# Cache Configuration (Valkey/Redis)
# =================================================================
# Valkey/Redis cache configuration
authentik_redis_host: "host.containers.internal"
authentik_redis_port: 6379
authentik_redis_db: 1
authentik_redis_password: "" # Valkey has no auth by default
# =================================================================
# Application Settings
# =================================================================
# Authentik core configuration
authentik_secret_key: "{{ vault_authentik_secret_key }}"
authentik_error_reporting_enabled: false
authentik_disable_update_check: true
authentik_disable_startup_analytics: true
# Email configuration (disabled by default)
authentik_email_host: ""
authentik_email_port: 587
authentik_email_username: ""
authentik_email_password: ""
authentik_email_use_tls: true
authentik_email_from: "authentik@{{ authentik_domain }}"
# Worker configuration
authentik_worker_concurrency: 2
# =================================================================
# Container Networking
# =================================================================
# Port mappings
authentik_http_port: 9000
authentik_https_port: 9443
# Network configuration
authentik_bridge_network: true
authentik_enable_host_gateway: true
# =================================================================
# Rick-Infra Integration Notes
# =================================================================
# This role:
# - Depends on PostgreSQL, Valkey, Podman, and Caddy infrastructure roles
# - Creates its own database and user in PostgreSQL
# - Uses Valkey database 1 for caching
# - Deploys Caddy configuration to sites-enabled
# - Uses Podman quadlets for systemd integration
# - Follows containerized service pattern with service-specific user

View File

@@ -0,0 +1,28 @@
---
# Authentik Role Handlers
- name: reload systemd
systemd:
daemon_reload: yes
- name: reload user systemd for authentik
systemd:
daemon_reload: yes
scope: user
become: yes
become_user: "{{ authentik_user }}"
- name: restart authentik pod
systemd:
name: "{{ authentik_pod_name }}"
state: restarted
scope: user
become: yes
become_user: "{{ authentik_user }}"
when: authentik_service_state == "started"
- name: reload caddy
systemd:
name: caddy
state: reloaded
when: caddy_service_enabled | default(false)

View File

@@ -0,0 +1,37 @@
---
galaxy_info:
author: Rick's Infrastructure Team
description: Containerized Authentik authentication service for rick-infra
company: Personal Infrastructure
license: MIT
min_ansible_version: "2.9"
platforms:
- name: ArchLinux
versions:
- all
galaxy_tags:
- authentication
- authentik
- containers
- podman
- archlinux
dependencies:
- role: postgresql
tags: ['postgresql', 'infrastructure']
- role: valkey
tags: ['valkey', 'infrastructure']
- role: podman
tags: ['podman', 'infrastructure']
- role: caddy
tags: ['caddy']
# Containerized Authentik authentication service
# - Creates its own database using PostgreSQL infrastructure
# - Uses Valkey for caching (database 1)
# - Deployed via Podman quadlets with service-specific user
# - Integrated with Caddy reverse proxy

View File

@@ -0,0 +1,69 @@
---
# Authentik Container Deployment - Podman Quadlets
- name: Deploy authentik environment file
template:
src: authentik.env.j2
dest: "{{ authentik_home }}/.config/containers/authentik.env"
owner: "{{ authentik_user }}"
group: "{{ authentik_group }}"
mode: '0600'
notify: restart authentik pod
- name: Deploy authentik pod quadlet
template:
src: authentik.pod.j2
dest: "{{ authentik_home }}/.config/containers/systemd/{{ authentik_pod_name }}.pod"
owner: "{{ authentik_user }}"
group: "{{ authentik_group }}"
mode: '0644'
notify:
- reload user systemd for authentik
- restart authentik pod
- name: Deploy authentik server container quadlet
template:
src: authentik-server.container.j2
dest: "{{ authentik_home }}/.config/containers/systemd/authentik-server.container"
owner: "{{ authentik_user }}"
group: "{{ authentik_group }}"
mode: '0644'
notify:
- reload user systemd for authentik
- restart authentik pod
- name: Deploy authentik worker container quadlet
template:
src: authentik-worker.container.j2
dest: "{{ authentik_home }}/.config/containers/systemd/authentik-worker.container"
owner: "{{ authentik_user }}"
group: "{{ authentik_group }}"
mode: '0644'
notify:
- reload user systemd for authentik
- restart authentik pod
- name: Reload user systemd to recognize quadlets
systemd:
daemon_reload: yes
scope: user
become: yes
become_user: "{{ authentik_user }}"
- name: Enable and start authentik pod
systemd:
name: "{{ authentik_pod_name }}-pod"
enabled: "{{ authentik_service_enabled }}"
state: "{{ authentik_service_state }}"
scope: user
become: yes
become_user: "{{ authentik_user }}"
- name: Wait for Authentik to be ready
uri:
url: "http://127.0.0.1:{{ authentik_http_port }}/if/flow/initial-setup/"
method: GET
status_code: [200, 302]
retries: 30
delay: 2
when: authentik_service_state == "started"

View File

@@ -0,0 +1,28 @@
---
# Authentik Database Management - Self-Contained Database Setup
- name: Create Authentik database user
postgresql_user:
name: "{{ authentik_db_user }}"
password: "{{ authentik_db_password }}"
encrypted: yes
become: yes
become_user: postgres
- name: Create Authentik database
postgresql_db:
name: "{{ authentik_db_name }}"
owner: "{{ authentik_db_user }}"
encoding: UTF8
template: template0
become: yes
become_user: postgres
- name: Grant all privileges on Authentik database to user
postgresql_privs:
db: "{{ authentik_db_name }}"
privs: ALL
type: database
role: "{{ authentik_db_user }}"
become: yes
become_user: postgres

View File

@@ -0,0 +1,36 @@
---
# Authentik Authentication Service Role - Containerized Implementation
# Manages Authentik using Podman with self-contained database
- name: Create authentik user and configure subuid/subgid
include_tasks: user.yml
- name: Set up authentik database
include_tasks: database.yml
- name: Configure container networking
include_tasks: networking.yml
- name: Deploy authentik containers via quadlets
include_tasks: containers.yml
- name: Deploy Caddy configuration for Authentik
template:
src: authentik.caddy.j2
dest: "{{ caddy_sites_enabled_dir }}/authentik.caddy"
mode: '0644'
notify: reload caddy
when: caddy_sites_enabled_dir is defined
- name: Display Authentik service status
debug:
msg: |
✅ Authentik authentication service deployed successfully!
🌐 Web Interface: https://{{ authentik_full_domain }}
🔐 Admin Interface: https://{{ authentik_full_domain }}/if/admin/
📦 Local HTTP: http://127.0.0.1:{{ authentik_http_port }}
🗄️ Database: {{ authentik_db_name }} (self-managed)
🚀 Cache: Valkey database {{ authentik_redis_db }}
🏗️ Authentication service ready for SSO integration!

View File

@@ -0,0 +1,23 @@
---
# Authentik Container Networking - Bridge Network Setup
- name: Create authentik bridge network
containers.podman.podman_network:
name: "{{ authentik_network_name }}"
driver: bridge
internal: false
state: present
become: yes
become_user: "{{ authentik_user }}"
- name: Check if authentik network exists
command: podman network ls --format json
become: yes
become_user: "{{ authentik_user }}"
register: network_list
changed_when: false
- name: Ensure host gateway is available in network
debug:
msg: "Network {{ authentik_network_name }} configured for host.containers.internal access"
when: authentik_enable_host_gateway | default(true)

View File

@@ -0,0 +1,60 @@
---
# Authentik User Management - Service-Specific User Setup
- name: Create authentik group
group:
name: "{{ authentik_group }}"
system: yes
- name: Create authentik user
user:
name: "{{ authentik_user }}"
group: "{{ authentik_group }}"
system: yes
shell: /bin/bash
home: "{{ authentik_home }}"
create_home: yes
comment: "Authentik authentication service"
- name: Set up subuid for authentik user
lineinfile:
path: /etc/subuid
line: "{{ authentik_user }}:{{ authentik_subuid_start }}:{{ authentik_subuid_size }}"
create: yes
mode: '0644'
- name: Set up subgid for authentik user
lineinfile:
path: /etc/subgid
line: "{{ authentik_user }}:{{ authentik_subgid_start }}:{{ authentik_subgid_size }}"
create: yes
mode: '0644'
- name: Create authentik directories
file:
path: "{{ item }}"
state: directory
owner: "{{ authentik_user }}"
group: "{{ authentik_group }}"
mode: '0755'
loop:
- "{{ authentik_home }}"
- "{{ authentik_home }}/.config"
- "{{ authentik_home }}/.config/systemd"
- "{{ authentik_home }}/.config/systemd/user"
- "{{ authentik_home }}/.config/containers"
- "{{ authentik_home }}/.config/containers/systemd"
- "{{ authentik_home }}/data"
- "{{ authentik_home }}/media"
- name: Enable lingering for authentik user
command: loginctl enable-linger {{ authentik_user }}
args:
creates: "/var/lib/systemd/linger/{{ authentik_user }}"
- name: Initialize user systemd for authentik
systemd:
daemon_reload: yes
scope: user
become: yes
become_user: "{{ authentik_user }}"

View File

@@ -0,0 +1,37 @@
# Authentik Server Container Quadlet
# Generated by rick-infra Ansible role
[Unit]
Description=Authentik Server Container
Requires={{ authentik_pod_name }}-pod.service
After={{ authentik_pod_name }}-pod.service
[Container]
ContainerName=authentik-server
Image={{ authentik_image_server }}:{{ authentik_image_tag }}
Pod={{ authentik_pod_name }}.pod
# Environment configuration
EnvironmentFile={{ authentik_home }}/.config/containers/authentik.env
Environment=AUTHENTIK_LISTEN__HTTP=0.0.0.0:9000
Environment=AUTHENTIK_LISTEN__HTTPS=0.0.0.0:9443
# Server command
Exec=server
# Volumes for persistent data
Volume={{ authentik_home }}/data:/data:Z
Volume={{ authentik_home }}/media:/media:Z
# Health check
HealthCmd=ak healthcheck
HealthInterval=30s
HealthTimeout=10s
HealthRetries=3
[Service]
Restart=always
RestartSec=10
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,35 @@
# Authentik Worker Container Quadlet
# Generated by rick-infra Ansible role
[Unit]
Description=Authentik Worker Container
Requires={{ authentik_pod_name }}-pod.service authentik-server.service
After={{ authentik_pod_name }}-pod.service authentik-server.service
[Container]
ContainerName=authentik-worker
Image={{ authentik_image_server }}:{{ authentik_image_tag }}
Pod={{ authentik_pod_name }}.pod
# Environment configuration
EnvironmentFile={{ authentik_home }}/.config/containers/authentik.env
# Worker command
Exec=worker
# Volumes for persistent data
Volume={{ authentik_home }}/data:/data:Z
Volume={{ authentik_home }}/media:/media:Z
# Health check
HealthCmd=ak healthcheck
HealthInterval=30s
HealthTimeout=10s
HealthRetries=3
[Service]
Restart=always
RestartSec=10
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,34 @@
# Authentik Caddy Configuration - Rick-Infra
# Generated by Ansible Authentik role
# Deployed to {{ caddy_sites_enabled_dir }}/authentik.caddy
{{ authentik_full_domain }} {
# Reverse proxy to Authentik
reverse_proxy 127.0.0.1:{{ authentik_http_port }}
# Security headers for authentication service
header {
# Enable HSTS
Strict-Transport-Security max-age=31536000;
# Prevent embedding in frames
X-Frame-Options DENY
# Prevent content type sniffing
X-Content-Type-Options nosniff
# XSS protection
X-XSS-Protection "1; mode=block"
# Referrer policy for privacy
Referrer-Policy strict-origin-when-cross-origin
}
# Logging
log {
output file /var/log/caddy/authentik_access.log
}
# Optional: Custom error pages
handle_errors {
respond "Authentication service temporarily unavailable" 503
}
}
# Rick-Infra: Containerized Authentik authentication service with Caddy reverse proxy

View File

@@ -0,0 +1,39 @@
# Authentik Environment Configuration
# Generated by rick-infra Ansible role
# Database Configuration
AUTHENTIK_POSTGRESQL__HOST={{ authentik_db_host }}
AUTHENTIK_POSTGRESQL__PORT={{ authentik_db_port }}
AUTHENTIK_POSTGRESQL__NAME={{ authentik_db_name }}
AUTHENTIK_POSTGRESQL__USER={{ authentik_db_user }}
AUTHENTIK_POSTGRESQL__PASSWORD={{ authentik_db_password }}
# Cache Configuration (Valkey/Redis)
AUTHENTIK_REDIS__HOST={{ authentik_redis_host }}
AUTHENTIK_REDIS__PORT={{ authentik_redis_port }}
AUTHENTIK_REDIS__DB={{ authentik_redis_db }}
{% if authentik_redis_password %}
AUTHENTIK_REDIS__PASSWORD={{ authentik_redis_password }}
{% endif %}
# Core Configuration
AUTHENTIK_SECRET_KEY={{ authentik_secret_key }}
AUTHENTIK_ERROR_REPORTING__ENABLED={{ authentik_error_reporting_enabled | lower }}
AUTHENTIK_DISABLE_UPDATE_CHECK={{ authentik_disable_update_check | lower }}
AUTHENTIK_DISABLE_STARTUP_ANALYTICS={{ authentik_disable_startup_analytics | lower }}
# Worker Configuration
AUTHENTIK_WORKER__CONCURRENCY={{ authentik_worker_concurrency }}
# Email Configuration
{% if authentik_email_host %}
AUTHENTIK_EMAIL__HOST={{ authentik_email_host }}
AUTHENTIK_EMAIL__PORT={{ authentik_email_port }}
AUTHENTIK_EMAIL__USERNAME={{ authentik_email_username }}
AUTHENTIK_EMAIL__PASSWORD={{ authentik_email_password }}
AUTHENTIK_EMAIL__USE_TLS={{ authentik_email_use_tls | lower }}
AUTHENTIK_EMAIL__FROM={{ authentik_email_from }}
{% endif %}
# Trust reverse proxy headers
AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS=127.0.0.1/32,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16

View File

@@ -0,0 +1,21 @@
# Authentik Pod Quadlet
# Generated by rick-infra Ansible role
[Unit]
Description=Authentik Authentication Service Pod
Wants=network-online.target
After=network-online.target
[Pod]
PodName={{ authentik_pod_name }}
Network={{ authentik_network_name }}
{% if authentik_enable_host_gateway | default(true) %}
AddHost=host.containers.internal:host-gateway
{% endif %}
# Published ports for web access
PublishPort={{ authentik_http_port }}:9000
PublishPort={{ authentik_https_port }}:9443
[Install]
WantedBy=default.target