- Add authentik-deployment-guide.md: Complete step-by-step deployment guide - Add architecture-decisions.md: Document native DB vs containerized rationale - Add authentication-architecture.md: SSO strategy and integration patterns - Update deployment-guide.md: Integrate authentik deployment procedures - Update security-hardening.md: Add multi-layer security documentation - Update service-integration-guide.md: Add authentik integration examples - Update README.md: Professional project overview with architecture benefits - Update authentik role: Fix HTTP binding, add security configs, improve templates - Remove unused authentik task files: containers.yml, networking.yml Key improvements: * Document security benefits of native databases over containers * Document Unix socket IPC architecture advantages * Provide comprehensive troubleshooting and deployment procedures * Add forward auth integration patterns for services * Fix authentik HTTP binding from 127.0.0.1 to 0.0.0.0 * Add shared memory and IPC security configurations
27 KiB
Authentication Architecture
This document describes the comprehensive authentication and authorization strategy implemented in rick-infra, focusing on centralized SSO with Authentik and forward authentication integration.
Overview
Rick-infra implements a modern, security-focused authentication architecture that provides:
- Centralized SSO: Single sign-on across all services via Authentik
- Forward Authentication: Transparent protection without application changes
- Zero Network Exposure: Database and cache communication via Unix sockets
- Granular Authorization: Fine-grained access control through groups and policies
- Standards Compliance: OAuth2, OIDC, SAML support for enterprise integration
Architecture Components
┌─────────────────────────────────────────────────────────────────┐
│ rick-infra Authentication Architecture │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Users │ │ Caddy │ │ Authentik │ │
│ │ │───▶│ (Proxy) │───▶│ (Auth Server) │ │
│ │ Web Browser │ │ Forward Auth│ │ OAuth2/OIDC/SAML │ │
│ │ Mobile Apps │ │ TLS Term │ │ User Management │ │
│ │ API Clients │ │ Load Balance│ │ Policy Engine │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Protected Services │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
│ │ │ Gitea │ │ Gallery │ │ Custom Services │ │ │
│ │ │ (Git Repos) │ │ (Media) │ │ (Applications) │ │ │
│ │ │ Receives: │ │ Receives: │ │ Receives: │ │ │
│ │ │ Remote-User │ │ Remote-User │ │ Remote-User │ │ │
│ │ │ Remote-Email│ │ Remote-Name │ │ Remote-Groups │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Infrastructure (Unix Sockets) │ │
│ │ PostgreSQL Database • Valkey Cache • systemd Services │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Authentication Flow
Standard User Authentication Flow
sequenceDiagram
participant U as User
participant C as Caddy
participant A as Authentik
participant S as Protected Service
U->>C: GET /dashboard
C->>A: Forward Auth Request
A->>C: 401 Unauthorized
C->>U: 302 Redirect to Login
U->>A: Login Form Request
A->>U: Login Form
U->>A: Credentials
A->>U: Set Session Cookie
U->>C: GET /dashboard (with cookie)
C->>A: Forward Auth Request (with cookie)
A->>C: 200 OK + User Headers
C->>S: GET /dashboard + Headers
S->>C: Dashboard Content
C->>U: Dashboard Content
API Authentication Flow
sequenceDiagram
participant API as API Client
participant A as Authentik
participant C as Caddy
participant S as API Service
API->>A: POST /application/o/token/
Note over API,A: OAuth2 Client Credentials
A->>API: Access Token
API->>C: GET /api/data
Note over API,C: Authorization: Bearer {token}
C->>A: Token Validation
A->>C: Token Valid + Claims
C->>S: GET /api/data + Headers
S->>API: API Response
Service Integration Patterns
Pattern 1: Forward Authentication (Recommended)
Use Case: Existing HTTP services that don't need to handle authentication
Benefits:
- No application code changes required
- Consistent authentication across all services
- Service receives authenticated user information via headers
- Centralized session management
Implementation:
# Caddy configuration
myservice.jnss.me {
forward_auth https://auth.jnss.me {
uri /outpost.goauthentik.io/auth/caddy
copy_headers Remote-User Remote-Name Remote-Email Remote-Groups
}
reverse_proxy localhost:8080
}
Service Code Example (Python Flask):
from flask import Flask, request
app = Flask(__name__)
@app.route('/dashboard')
def dashboard():
# Authentication handled by Caddy/Authentik
username = request.headers.get('Remote-User')
user_name = request.headers.get('Remote-Name')
user_email = request.headers.get('Remote-Email')
user_groups = request.headers.get('Remote-Groups', '').split(',')
# Authorization based on groups
if 'admins' not in user_groups:
return "Access denied", 403
return f"Welcome {user_name} ({username})"
Pattern 2: OAuth2/OIDC Integration
Use Case: Applications that can implement OAuth2 client functionality
Benefits:
- More control over authentication flow
- Better integration with application user models
- Support for API access tokens
- Offline access via refresh tokens
Implementation:
# Example OAuth2 configuration
OAUTH2_CONFIG = {
'client_id': 'your-client-id',
'client_secret': 'your-client-secret',
'server_metadata_url': 'https://auth.jnss.me/application/o/your-app/.well-known/openid_configuration',
'client_kwargs': {
'scope': 'openid email profile groups'
}
}
# OAuth2 flow implementation
from authlib.integrations.flask_client import OAuth
oauth = OAuth(app)
oauth.register('authentik', **OAUTH2_CONFIG)
@app.route('/login')
def login():
redirect_uri = url_for('callback', _external=True)
return oauth.authentik.authorize_redirect(redirect_uri)
@app.route('/callback')
def callback():
token = oauth.authentik.authorize_access_token()
user_info = oauth.authentik.parse_id_token(token)
# Store user info in session
session['user'] = user_info
return redirect('/dashboard')
Pattern 3: API-Only Authentication
Use Case: REST APIs, mobile app backends, microservices
Benefits:
- Stateless authentication via tokens
- Machine-to-machine authentication support
- Fine-grained API scope control
- Easy integration with mobile/SPA applications
Implementation:
# Authentik Provider Configuration
name: "API Provider"
authorization_flow: "default-provider-authorization-explicit-consent"
client_type: "confidential"
client_id: "api-client-id"
redirect_uris: ["http://localhost:8080/callback"]
# API service with token validation
import requests
from flask import Flask, request, jsonify
def validate_token(token):
"""Validate token with Authentik introspection endpoint"""
response = requests.post(
'https://auth.jnss.me/application/o/introspect/',
headers={'Authorization': f'Bearer {token}'},
data={'token': token}
)
return response.json() if response.status_code == 200 else None
@app.route('/api/data')
def api_data():
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):
return jsonify({'error': 'Missing or invalid authorization header'}), 401
token = auth_header.split(' ')[1]
token_info = validate_token(token)
if not token_info or not token_info.get('active'):
return jsonify({'error': 'Invalid or expired token'}), 401
# Extract user information from token
username = token_info.get('username')
scope = token_info.get('scope', '').split()
if 'read:data' not in scope:
return jsonify({'error': 'Insufficient permissions'}), 403
return jsonify({'message': f'Hello {username}', 'data': 'sensitive-data'})
Pattern 4: Service-to-Service Authentication
Use Case: Backend services communicating with each other
Benefits:
- Machine-to-machine authentication
- Service identity and authorization
- API rate limiting and monitoring
- Secure inter-service communication
Implementation:
# Authentik Service Account Configuration
name: "Backend Service Account"
username: "backend-service"
groups: ["services", "backend-access"]
token_validity: "8760h" # 1 year
# Service-to-service authentication
import os
import requests
class ServiceClient:
def __init__(self):
self.client_id = os.getenv('SERVICE_CLIENT_ID')
self.client_secret = os.getenv('SERVICE_CLIENT_SECRET')
self.token_url = 'https://auth.jnss.me/application/o/token/'
self.access_token = None
def get_access_token(self):
if not self.access_token:
response = requests.post(self.token_url, data={
'grant_type': 'client_credentials',
'client_id': self.client_id,
'client_secret': self.client_secret,
'scope': 'service:read service:write'
})
self.access_token = response.json()['access_token']
return self.access_token
def call_service(self, endpoint):
token = self.get_access_token()
response = requests.get(
f'https://api.jnss.me{endpoint}',
headers={'Authorization': f'Bearer {token}'}
)
return response.json()
User Management Strategy
User Lifecycle Management
User Creation and Onboarding
# Authentik User Configuration
email: "user@company.com"
username: "user.name"
name: "User Full Name"
is_active: true
groups: ["employees", "developers"] # Assigned based on role
attributes:
department: "Engineering"
team: "Backend"
hire_date: "2024-01-15"
Group-Based Authorization
# Group Structure
groups:
- name: "employees"
description: "All company employees"
permissions: ["basic_access"]
- name: "developers"
description: "Software development team"
permissions: ["git_access", "deploy_staging"]
parent_group: "employees"
- name: "admins"
description: "System administrators"
permissions: ["admin_access", "deploy_production"]
parent_group: "employees"
- name: "contractors"
description: "External contractors"
permissions: ["limited_access"]
Access Control Policies
# Authentik Policy Configuration
policies:
- name: "Business Hours Access"
type: "time"
parameters:
start_time: "08:00"
end_time: "18:00"
days: ["monday", "tuesday", "wednesday", "thursday", "friday"]
- name: "Admin IP Restriction"
type: "source_ip"
parameters:
cidr: "10.0.0.0/8"
- name: "MFA Required for Admin"
type: "group_membership"
parameters:
group: "admins"
require_mfa: true
Multi-Factor Authentication
MFA Configuration
# Authentik MFA Settings
mfa_policies:
- name: "Admin MFA Requirement"
bound_to: "group:admins"
authenticators: ["totp", "webauthn"]
enforce: true
- name: "Developer Optional MFA"
bound_to: "group:developers"
authenticators: ["totp", "sms"]
enforce: false
Supported MFA Methods
-
TOTP (Time-based One-Time Passwords)
- Google Authenticator, Authy, 1Password
- RFC 6238 compliant
- Configurable validity period
-
WebAuthn/FIDO2
- Hardware security keys (YubiKey, etc.)
- Biometric authentication
- Passwordless authentication support
-
SMS Authentication
- SMS-based verification codes
- International phone number support
- Rate limiting and abuse protection
-
Email Authentication
- Email-based verification codes
- Fallback authentication method
- Configurable code validity
Security Considerations
Session Management
Session Configuration
# Authentik Session Settings
session_settings:
cookie_age: 3600 # 1 hour
cookie_secure: true # HTTPS only
cookie_samesite: "Strict"
remember_me_duration: 86400 # 24 hours
concurrent_sessions: 3 # Max simultaneous sessions
Session Security Features
- Session Fixation Protection: New session ID on authentication
- Concurrent Session Control: Limit simultaneous sessions per user
- Session Timeout: Automatic logout after inactivity
- Secure Cookies: HTTP-only, secure, SameSite attributes
- Session Invalidation: Logout invalidates all sessions
Authorization Security
Policy-Based Access Control
# Example Authorization Policy
policy_bindings:
- application: "admin_dashboard"
policies:
- "group_membership:admins"
- "mfa_required"
- "business_hours_access"
- "ip_whitelist:office"
order: 0
- application: "developer_tools"
policies:
- "group_membership:developers"
- "rate_limiting:api_calls"
order: 1
Security Headers
# Headers passed to protected services
Remote-User: john.doe
Remote-Name: John Doe
Remote-Email: john.doe@company.com
Remote-Groups: employees,developers
Remote-Authenticated: true
Remote-Auth-Time: 2025-12-11T17:52:31Z
Remote-Session-ID: sess_abc123def456
Audit and Logging
Authentication Events
{
"timestamp": "2025-12-11T17:52:31Z",
"event_type": "authentication_success",
"user_id": "user123",
"username": "john.doe",
"source_ip": "192.168.1.100",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"application": "dashboard.jnss.me",
"auth_method": "password+totp",
"session_id": "sess_abc123def456"
}
Authorization Events
{
"timestamp": "2025-12-11T17:52:45Z",
"event_type": "authorization_denied",
"user_id": "user456",
"username": "contractor.user",
"source_ip": "203.0.113.100",
"application": "admin_dashboard",
"reason": "insufficient_privileges",
"required_groups": ["admins"],
"user_groups": ["contractors"]
}
System Events
{
"timestamp": "2025-12-11T17:53:00Z",
"event_type": "policy_violation",
"user_id": "user789",
"username": "admin.user",
"source_ip": "198.51.100.50",
"policy": "ip_whitelist:office",
"violation": "access_from_unauthorized_ip",
"action": "access_denied"
}
Integration Examples
Example 1: Protecting Gitea with Groups
Objective: Protect Git repository access with role-based permissions
# Caddy configuration for Gitea
git.jnss.me {
forward_auth https://auth.jnss.me {
uri /outpost.goauthentik.io/auth/caddy
copy_headers Remote-User Remote-Groups
}
reverse_proxy localhost:3000
}
Gitea Configuration:
# app.ini - Gitea configuration
[auth]
REVERSE_PROXY_AUTHENTICATION = true
REVERSE_PROXY_AUTO_REGISTRATION = true
[auth.reverse_proxy]
USER_HEADER = Remote-User
EMAIL_HEADER = Remote-Email
FULL_NAME_HEADER = Remote-Name
Group Mapping:
# Authentik group configuration for Gitea
groups:
- name: "git_users"
description: "Git repository users"
applications: ["gitea"]
- name: "git_admins"
description: "Git administrators"
applications: ["gitea"]
permissions: ["admin"]
Example 2: API Service with Scoped Access
Objective: REST API with OAuth2 authentication and scoped permissions
# Caddy configuration for API
api.jnss.me {
# No forward auth - API handles OAuth2 directly
reverse_proxy localhost:8080
}
API Service Configuration:
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
def verify_token_scope(token, required_scope):
"""Verify token has required scope"""
response = requests.post(
'https://auth.jnss.me/application/o/introspect/',
data={
'token': token,
'client_id': 'api-client-id',
'client_secret': 'api-client-secret'
}
)
if response.status_code != 200:
return False
token_data = response.json()
if not token_data.get('active'):
return False
scopes = token_data.get('scope', '').split()
return required_scope in scopes
@app.route('/api/public')
def public_endpoint():
"""Public endpoint - no authentication required"""
return jsonify({'message': 'This is public data'})
@app.route('/api/user/profile')
def user_profile():
"""User profile endpoint - requires user:read scope"""
auth_header = request.headers.get('Authorization', '')
if not auth_header.startswith('Bearer '):
return jsonify({'error': 'Missing authorization header'}), 401
token = auth_header[7:] # Remove 'Bearer ' prefix
if not verify_token_scope(token, 'user:read'):
return jsonify({'error': 'Insufficient permissions'}), 403
return jsonify({'message': 'User profile data'})
@app.route('/api/admin/users')
def admin_users():
"""Admin endpoint - requires admin:read scope"""
auth_header = request.headers.get('Authorization', '')
if not auth_header.startswith('Bearer '):
return jsonify({'error': 'Missing authorization header'}), 401
token = auth_header[7:]
if not verify_token_scope(token, 'admin:read'):
return jsonify({'error': 'Admin privileges required'}), 403
return jsonify({'message': 'Admin user data'})
OAuth2 Scopes Configuration:
# Authentik OAuth2 Provider scopes
scopes:
- name: "user:read"
description: "Read user profile information"
- name: "user:write"
description: "Modify user profile information"
- name: "admin:read"
description: "Read administrative data"
- name: "admin:write"
description: "Modify administrative settings"
Example 3: Static Site with Selective Protection
Objective: Protect portions of a static site while keeping some content public
# Caddy configuration for mixed static/protected site
docs.jnss.me {
# Public documentation - no authentication
handle /public/* {
root * /var/www/docs
file_server
}
# Public API documentation
handle /api-docs {
root * /var/www/docs
file_server
}
# Protected internal documentation
handle /internal/* {
forward_auth https://auth.jnss.me {
uri /outpost.goauthentik.io/auth/caddy
copy_headers Remote-Groups
}
# Only employees can access internal docs
@not_employee {
not header Remote-Groups "*employees*"
}
respond @not_employee "Access denied" 403
root * /var/www/docs
file_server
}
# Admin documentation - requires admin group
handle /admin/* {
forward_auth https://auth.jnss.me {
uri /outpost.goauthentik.io/auth/caddy
copy_headers Remote-Groups
}
# Only admins can access admin docs
@not_admin {
not header Remote-Groups "*admins*"
}
respond @not_admin "Admin access required" 403
root * /var/www/docs
file_server
}
# Default: Require authentication for everything else
handle {
forward_auth https://auth.jnss.me {
uri /outpost.goauthentik.io/auth/caddy
copy_headers Remote-User Remote-Groups
}
root * /var/www/docs
file_server
}
}
Performance Considerations
Authentication Performance
Caching Strategy
- Session Caching: Redis-backed session storage for fast lookup
- Token Caching: Cache valid tokens to reduce introspection calls
- User Information Caching: Cache user attributes and group memberships
- Policy Evaluation Caching: Cache authorization decision results
Performance Optimization
# Authentik Performance Settings
cache_settings:
default_timeout: 300 # 5 minutes
session_timeout: 1800 # 30 minutes
token_introspection_cache: 60 # 1 minute
user_info_cache: 600 # 10 minutes
Load Balancing Considerations
- Session Affinity: Not required - sessions stored in shared cache
- Health Checks: Monitor authentik container health
- Failover: Graceful degradation strategies for auth service outages
- Rate Limiting: Protect against authentication abuse
Database Performance
Connection Optimization
# PostgreSQL connection settings for Authentik
database_settings:
max_connections: 100
connection_timeout: 30
idle_timeout: 300
query_timeout: 60
connection_pool_size: 20
Index Optimization
-- Recommended PostgreSQL indexes for Authentik performance
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_authentik_core_user_email ON authentik_core_user(email);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_authentik_core_token_key ON authentik_core_token(key);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_authentik_sessions_expire_date ON django_session(expire_date);
Troubleshooting Guide
Common Authentication Issues
Issue: Users Cannot Access Protected Services
Symptoms:
- 401 Unauthorized errors
- Redirect loops to login page
- "Access denied" messages
Diagnostic Steps:
# Check authentik service status
ssh root@your-vps "systemctl --user -M authentik@ status authentik-server"
# Test authentik HTTP endpoint
curl -I https://auth.jnss.me/
# Check forward auth endpoint
curl -I https://auth.jnss.me/outpost.goauthentik.io/auth/caddy
# Verify Caddy configuration
ssh root@your-vps "caddy validate --config /etc/caddy/Caddyfile"
Issue: Group-Based Authorization Not Working
Symptoms:
- Users with correct groups denied access
- Group headers not passed to services
- Authorization policies not applied
Diagnostic Steps:
# Check user group membership
curl -H "Authorization: Bearer $TOKEN" https://auth.jnss.me/api/v3/core/users/me/
# Test header passing
curl -H "Cookie: authentik_session=$SESSION" https://auth.jnss.me/outpost.goauthentik.io/auth/caddy -v
# Verify Caddy header configuration
ssh root@your-vps "grep -A5 'copy_headers' /etc/caddy/sites-enabled/*.caddy"
Issue: OAuth2 Token Validation Failing
Symptoms:
- "Invalid token" errors for valid tokens
- Token introspection returning false
- API calls failing with 401
Diagnostic Steps:
# Test token introspection directly
curl -X POST https://auth.jnss.me/application/o/introspect/ \
-H "Authorization: Bearer $CLIENT_TOKEN" \
-d "token=$USER_TOKEN"
# Check client credentials
curl -X POST https://auth.jnss.me/application/o/token/ \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET"
Performance Issues
Issue: Slow Authentication Response
Symptoms:
- Long delays on login
- Timeouts during authentication
- Poor user experience
Diagnostic Steps:
# Check authentik container resources
ssh root@your-vps "sudo -u authentik podman stats authentik-server"
# Monitor database performance
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'SELECT * FROM pg_stat_activity;'"
# Check network latency
curl -w "@curl-format.txt" -o /dev/null https://auth.jnss.me/
Issue: High Database Load
Symptoms:
- Slow database queries
- High CPU usage on database
- Authentication timeouts
Solutions:
-- Optimize authentication queries
VACUUM ANALYZE authentik_core_user;
VACUUM ANALYZE authentik_core_token;
VACUUM ANALYZE django_session;
-- Check for missing indexes
SELECT schemaname, tablename, attname, n_distinct, correlation
FROM pg_stats
WHERE schemaname = 'public'
AND tablename LIKE 'authentik_%';
Future Considerations
Scalability Planning
Horizontal Scaling
- Load Balancer: Multiple authentik instances behind load balancer
- Database Clustering: PostgreSQL replication for read scaling
- Cache Clustering: Valkey cluster for session storage
- Geographic Distribution: Regional authentik deployments
Performance Monitoring
# Monitoring metrics to track
metrics:
authentication:
- login_success_rate
- login_response_time
- failed_login_attempts
- concurrent_sessions
authorization:
- authorization_success_rate
- policy_evaluation_time
- access_denied_rate
system:
- database_connection_pool_usage
- cache_hit_rate
- container_memory_usage
- response_time_p95
Enterprise Integration
SAML Federation
# SAML Provider configuration for enterprise SSO
saml_provider:
name: "Corporate SAML"
acs_url: "https://auth.jnss.me/source/saml/corporate/acs/"
issuer: "https://auth.jnss.me"
signing_certificate: "{{ saml_signing_cert }}"
encryption_certificate: "{{ saml_encryption_cert }}"
LDAP/Active Directory Integration
# LDAP source configuration
ldap_source:
name: "Corporate LDAP"
server_uri: "ldaps://ldap.company.com:636"
bind_dn: "CN=authentik,OU=Service Accounts,DC=company,DC=com"
bind_password: "{{ ldap_bind_password }}"
base_dn: "DC=company,DC=com"
user_search: "(&(objectClass=user)(sAMAccountName=%(user)s))"
group_search: "(&(objectClass=group)(member=%(user_dn)s))"
This comprehensive authentication architecture provides a robust, scalable, and secure foundation for managing access to all services in the rick-infra environment, with emphasis on security, performance, and operational excellence.
References
- Authentik Deployment Guide - Detailed deployment instructions
- Architecture Decisions - Technical decision rationale
- Service Integration Guide - Adding new services
- Security Hardening - Security implementation details
- Authentik Role Documentation - Technical implementation details