Add comprehensive authentik documentation and improve role configuration
- 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
This commit is contained in:
949
docs/authentication-architecture.md
Normal file
949
docs/authentication-architecture.md
Normal file
@@ -0,0 +1,949 @@
|
||||
# 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
|
||||
|
||||
```mermaid
|
||||
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
|
||||
|
||||
```mermaid
|
||||
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**:
|
||||
|
||||
```caddyfile
|
||||
# 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):
|
||||
```python
|
||||
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**:
|
||||
|
||||
```python
|
||||
# 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**:
|
||||
|
||||
```yaml
|
||||
# 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"]
|
||||
```
|
||||
|
||||
```python
|
||||
# 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**:
|
||||
|
||||
```yaml
|
||||
# Authentik Service Account Configuration
|
||||
name: "Backend Service Account"
|
||||
username: "backend-service"
|
||||
groups: ["services", "backend-access"]
|
||||
token_validity: "8760h" # 1 year
|
||||
```
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
1. **TOTP (Time-based One-Time Passwords)**
|
||||
- Google Authenticator, Authy, 1Password
|
||||
- RFC 6238 compliant
|
||||
- Configurable validity period
|
||||
|
||||
2. **WebAuthn/FIDO2**
|
||||
- Hardware security keys (YubiKey, etc.)
|
||||
- Biometric authentication
|
||||
- Passwordless authentication support
|
||||
|
||||
3. **SMS Authentication**
|
||||
- SMS-based verification codes
|
||||
- International phone number support
|
||||
- Rate limiting and abuse protection
|
||||
|
||||
4. **Email Authentication**
|
||||
- Email-based verification codes
|
||||
- Fallback authentication method
|
||||
- Configurable code validity
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Session Management
|
||||
|
||||
#### Session Configuration
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```http
|
||||
# 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
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```caddyfile
|
||||
# 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**:
|
||||
```ini
|
||||
# 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**:
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```caddyfile
|
||||
# Caddy configuration for API
|
||||
api.jnss.me {
|
||||
# No forward auth - API handles OAuth2 directly
|
||||
reverse_proxy localhost:8080
|
||||
}
|
||||
```
|
||||
|
||||
**API Service Configuration**:
|
||||
```python
|
||||
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**:
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```caddyfile
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```sql
|
||||
-- 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**:
|
||||
```bash
|
||||
# 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**:
|
||||
```bash
|
||||
# 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**:
|
||||
```bash
|
||||
# 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**:
|
||||
```bash
|
||||
# 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**:
|
||||
```sql
|
||||
-- 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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](authentik-deployment-guide.md)** - Detailed deployment instructions
|
||||
- **[Architecture Decisions](architecture-decisions.md)** - Technical decision rationale
|
||||
- **[Service Integration Guide](service-integration-guide.md)** - Adding new services
|
||||
- **[Security Hardening](security-hardening.md)** - Security implementation details
|
||||
- **[Authentik Role Documentation](../roles/authentik/README.md)** - Technical implementation details
|
||||
Reference in New Issue
Block a user