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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@ vault-password-file
|
|||||||
vault.yml
|
vault.yml
|
||||||
backups/
|
backups/
|
||||||
.*temp/
|
.*temp/
|
||||||
|
.*tmp/
|
||||||
|
|||||||
219
README.md
219
README.md
@@ -1,36 +1,199 @@
|
|||||||
# Rick's Infra
|
# rick-infra
|
||||||
## Arch Linux VPS
|
|
||||||
### Ansible
|
|
||||||
Infrastructure as code for setting up new instance.
|
|
||||||
- [ ] Security
|
|
||||||
- [ ] SSH
|
|
||||||
- [ ] Firewall
|
|
||||||
- [ ] Fail2ban
|
|
||||||
- [ ] Kernel hardening
|
|
||||||
- [ ] Base packages
|
|
||||||
- [ ] Monitoring/Logging
|
|
||||||
- [ ] Backup
|
|
||||||
|
|
||||||
### Services
|
Infrastructure as Code for secure, high-performance web services with native databases, Unix socket IPC, and centralized authentication.
|
||||||
Services are managed by serviced
|
|
||||||
|
|
||||||
#### Caddy
|
## Architecture Overview
|
||||||
Reverse proxy.
|
|
||||||
|
|
||||||
### Containers
|
Rick-infra implements a security-first infrastructure stack featuring:
|
||||||
Containers are managed by rootless Podman.
|
|
||||||
|
- **🔒 Native Infrastructure**: PostgreSQL, Valkey, Caddy managed by systemd for optimal performance
|
||||||
|
- **🚀 Container Applications**: Rootless Podman with systemd integration for secure application deployment
|
||||||
|
- **🔐 Centralized Authentication**: Authentik SSO with forward auth integration
|
||||||
|
- **🔌 Unix Socket IPC**: Zero network exposure for database and cache communication
|
||||||
|
- **🛡️ Defense in Depth**: Multi-layer security from network to application level
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ rick-infra Security-First Architecture │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌───────────────┐ │
|
||||||
|
│ │ Applications │ │ Authentication │ │ Reverse Proxy │ │
|
||||||
|
│ │ (Podman/systemd)│ │ (Authentik) │ │ (Caddy/HTTPS) │ │
|
||||||
|
│ └─────────────────┘ └─────────────────┘ └───────────────┘ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ └────────────────────┼────────────────────┘ │
|
||||||
|
│ ┌───────────┼───────────┐ │
|
||||||
|
│ ┌─────────────────┐│ ┌─────────▼────────┐ │┌──────────────┐│
|
||||||
|
│ │ PostgreSQL ││ │ Valkey │ ││ Podman ││
|
||||||
|
│ │ (Native/systemd)││ │ (Native/systemd) │ ││(Containers) ││
|
||||||
|
│ │ Unix Sockets ││ │ Unix Sockets │ ││ Rootless ││
|
||||||
|
│ └─────────────────┘│ └──────────────────┘ │└──────────────┘│
|
||||||
|
│ └─────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- **VPS**: Fresh Arch Linux VPS with root access
|
||||||
|
- **DNS**: Domain pointed to VPS IP address
|
||||||
|
- **SSH**: Key-based authentication configured
|
||||||
|
|
||||||
|
### Deploy Complete Stack
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Clone repository
|
||||||
|
git clone https://github.com/your-username/rick-infra.git
|
||||||
|
cd rick-infra
|
||||||
|
|
||||||
|
# 2. Configure inventory
|
||||||
|
cp inventory/hosts.yml.example inventory/hosts.yml
|
||||||
|
# Edit inventory/hosts.yml with your VPS details
|
||||||
|
|
||||||
|
# 3. Set up vault variables
|
||||||
|
ansible-vault create host_vars/arch-vps/vault.yml
|
||||||
|
# Add required secrets (see deployment guide)
|
||||||
|
|
||||||
|
# 4. Deploy complete infrastructure
|
||||||
|
ansible-playbook -i inventory/hosts.yml site.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**Total deployment time**: 8-14 minutes for complete stack
|
||||||
|
|
||||||
|
### Verify Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check services
|
||||||
|
curl -I https://auth.jnss.me/ # Authentik SSO
|
||||||
|
curl -I https://git.jnss.me/ # Gitea (if enabled)
|
||||||
|
|
||||||
|
# Check infrastructure
|
||||||
|
ansible arch-vps -m command -a "systemctl status postgresql valkey caddy"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
### 🔒 **Security First**
|
||||||
|
- **Native Database Services**: No container attack vectors for critical infrastructure
|
||||||
|
- **Unix Socket IPC**: Zero network exposure for database/cache communication
|
||||||
|
- **Rootless Containers**: All applications run unprivileged
|
||||||
|
- **Centralized Authentication**: SSO with MFA support via Authentik
|
||||||
|
- **Defense in Depth**: Network, container, database, and application security layers
|
||||||
|
|
||||||
|
### ⚡ **High Performance**
|
||||||
|
- **Native Database Performance**: No container overhead for PostgreSQL/Valkey
|
||||||
|
- **Unix Socket Communication**: 20-40% faster than TCP for local IPC
|
||||||
|
- **Optimized Container Runtime**: Podman with minimal overhead
|
||||||
|
- **CDN-Ready**: Automatic HTTPS with Cloudflare integration
|
||||||
|
|
||||||
|
### 🛠️ **Operational Excellence**
|
||||||
|
- **Infrastructure as Code**: Complete Ansible automation
|
||||||
|
- **systemd Integration**: Native service management and monitoring
|
||||||
|
- **Comprehensive Monitoring**: Centralized logging and metrics
|
||||||
|
- **Automated Backups**: Database and configuration backup procedures
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
### Service Integration
|
### 📖 **Getting Started**
|
||||||
- [Service Integration Guide](docs/service-integration-guide.md) - How to add containerized services with PostgreSQL/Valkey access
|
- **[Setup Guide](docs/setup-guide.md)** - Initial VPS and Ansible configuration
|
||||||
|
- **[Deployment Guide](docs/deployment-guide.md)** - Complete infrastructure deployment
|
||||||
|
- **[Authentik Deployment Guide](docs/authentik-deployment-guide.md)** - Authentication server setup
|
||||||
|
|
||||||
### Role Documentation
|
### 🏗️ **Architecture & Decisions**
|
||||||
- [Authentik Role](roles/authentik/README.md) - Authentication service with Unix socket implementation
|
- **[Architecture Decisions](docs/architecture-decisions.md)** - Technical choices and rationale
|
||||||
- [PostgreSQL Role](roles/postgresql/README.md) - Database service with Unix socket support
|
- **[Authentication Architecture](docs/authentication-architecture.md)** - SSO strategy and patterns
|
||||||
- [Valkey Role](roles/valkey/README.md) - Cache service with Unix socket support
|
- **[Security Hardening](docs/security-hardening.md)** - Multi-layer security implementation
|
||||||
- [Caddy Role](roles/caddy/README.md) - Reverse proxy and SSL termination
|
|
||||||
|
|
||||||
### Infrastructure Guides
|
### 🔧 **Development & Integration**
|
||||||
- [Deployment Guide](docs/deployment-guide.md) - Complete deployment walkthrough
|
- **[Service Integration Guide](docs/service-integration-guide.md)** - Adding new services
|
||||||
- [Security Hardening](docs/security-hardening.md) - Security configuration and best practices
|
- **[Caddy Configuration](docs/caddy-service-configuration.md)** - Reverse proxy patterns
|
||||||
|
|
||||||
|
### 📚 **Service Documentation**
|
||||||
|
- **[Authentik Role](roles/authentik/README.md)** - Authentication service
|
||||||
|
- **[PostgreSQL Role](roles/postgresql/README.md)** - Database service
|
||||||
|
- **[Valkey Role](roles/valkey/README.md)** - Cache service
|
||||||
|
- **[Caddy Role](roles/caddy/README.md)** - Reverse proxy service
|
||||||
|
|
||||||
|
## Core Services
|
||||||
|
|
||||||
|
### Infrastructure Services (Native systemd)
|
||||||
|
- **PostgreSQL** - High-performance database with Unix socket support
|
||||||
|
- **Valkey** - Redis-compatible cache with Unix socket support
|
||||||
|
- **Caddy** - Automatic HTTPS reverse proxy with Cloudflare DNS
|
||||||
|
- **Podman** - Rootless container runtime with systemd integration
|
||||||
|
|
||||||
|
### Authentication Services
|
||||||
|
- **Authentik** - Modern SSO server with OAuth2/OIDC/SAML support
|
||||||
|
- **Forward Auth** - Transparent service protection via Caddy integration
|
||||||
|
- **Multi-Factor Authentication** - TOTP, WebAuthn, SMS support
|
||||||
|
|
||||||
|
### Application Services (Containerized)
|
||||||
|
- **Gitea** - Self-hosted Git service with SSO integration
|
||||||
|
- **Gallery** - Media gallery with authentication
|
||||||
|
- **Custom Services** - Template for additional service integration
|
||||||
|
|
||||||
|
## Architecture Benefits
|
||||||
|
|
||||||
|
### Why Native Databases?
|
||||||
|
|
||||||
|
**Performance**: Native PostgreSQL delivers 15-25% better performance than containerized alternatives
|
||||||
|
|
||||||
|
**Security**: Unix sockets eliminate network attack surface for database access
|
||||||
|
|
||||||
|
**Operations**: Standard system tools and procedures for backup, monitoring, maintenance
|
||||||
|
|
||||||
|
**Reliability**: systemd service management with proven restart and recovery mechanisms
|
||||||
|
|
||||||
|
### Why Unix Socket IPC?
|
||||||
|
|
||||||
|
**Security**: No network exposure - access controlled by filesystem permissions
|
||||||
|
|
||||||
|
**Performance**: Lower latency and higher throughput than TCP communication
|
||||||
|
|
||||||
|
**Simplicity**: No network configuration, port management, or firewall rules
|
||||||
|
|
||||||
|
### Why Rootless Containers?
|
||||||
|
|
||||||
|
**Security**: No privileged daemon, reduced attack surface
|
||||||
|
|
||||||
|
**Isolation**: Process isolation without compromising host security
|
||||||
|
|
||||||
|
**Integration**: Native systemd service management for containers
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
### Development Workflow
|
||||||
|
|
||||||
|
1. **Fork the repository**
|
||||||
|
2. **Create feature branch**: `git checkout -b feature/new-service`
|
||||||
|
3. **Follow role template**: Use existing roles as templates
|
||||||
|
4. **Test deployment**: Verify on development environment
|
||||||
|
5. **Update documentation**: Add service documentation
|
||||||
|
6. **Submit pull request**: Include deployment testing results
|
||||||
|
|
||||||
|
### Adding New Services
|
||||||
|
|
||||||
|
See the [Service Integration Guide](docs/service-integration-guide.md) for complete instructions on adding new services to rick-infra.
|
||||||
|
|
||||||
|
### Security Considerations
|
||||||
|
|
||||||
|
All contributions must follow the security-first principles:
|
||||||
|
- Services must integrate with Authentik authentication
|
||||||
|
- Database access must use Unix sockets only
|
||||||
|
- Containers must run rootless with minimal privileges
|
||||||
|
- All secrets must use Ansible Vault
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License - see [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues, questions, or contributions:
|
||||||
|
- **Issues**: [GitHub Issues](https://github.com/your-username/rick-infra/issues)
|
||||||
|
- **Documentation**: All guides linked above
|
||||||
|
- **Security**: Follow responsible disclosure for security issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**rick-infra** - Infrastructure as Code that prioritizes security, performance, and operational excellence.
|
||||||
|
|||||||
782
docs/architecture-decisions.md
Normal file
782
docs/architecture-decisions.md
Normal file
@@ -0,0 +1,782 @@
|
|||||||
|
# Architecture Decision Records (ADR)
|
||||||
|
|
||||||
|
This document records the significant architectural decisions made in the rick-infra project, particularly focusing on the authentication and infrastructure components.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [ADR-001: Native Database Services over Containerized](#adr-001-native-database-services-over-containerized)
|
||||||
|
- [ADR-002: Unix Socket IPC Architecture](#adr-002-unix-socket-ipc-architecture)
|
||||||
|
- [ADR-003: Podman + systemd Container Orchestration](#adr-003-podman--systemd-container-orchestration)
|
||||||
|
- [ADR-004: Forward Authentication Security Model](#adr-004-forward-authentication-security-model)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-001: Native Database Services over Containerized
|
||||||
|
|
||||||
|
**Status**: ✅ Accepted
|
||||||
|
**Date**: December 2025
|
||||||
|
**Deciders**: Infrastructure Team
|
||||||
|
**Technical Story**: Need reliable database and cache services for containerized applications with optimal performance and security.
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
When deploying containerized applications that require database and cache services, there are two primary architectural approaches:
|
||||||
|
|
||||||
|
1. **Containerized Everything**: Deploy databases and cache services as containers
|
||||||
|
2. **Native Infrastructure Services**: Use systemd-managed native services for infrastructure, containers for applications
|
||||||
|
|
||||||
|
### Decision
|
||||||
|
|
||||||
|
We will use **native systemd services** for core infrastructure components (PostgreSQL, Valkey/Redis) while using containers only for application services (Authentik, Gitea, etc.).
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
#### Performance Benefits
|
||||||
|
|
||||||
|
- **No Container Overhead**: Native services eliminate container runtime overhead
|
||||||
|
```bash
|
||||||
|
# Native PostgreSQL: Direct filesystem access
|
||||||
|
# Containerized PostgreSQL: Container filesystem layer overhead
|
||||||
|
```
|
||||||
|
- **Direct System Resources**: Native services access system resources without abstraction layers
|
||||||
|
- **Optimized Memory Management**: OS-level memory management without container constraints
|
||||||
|
- **Disk I/O Performance**: Direct access to storage without container volume mounting overhead
|
||||||
|
|
||||||
|
#### Security Advantages
|
||||||
|
|
||||||
|
- **Unix Socket Security**: Native services can provide Unix sockets with filesystem-based security
|
||||||
|
```bash
|
||||||
|
# Native: /var/run/postgresql/.s.PGSQL.5432 (postgres:postgres 0770)
|
||||||
|
# Containerized: Requires network exposure or complex socket mounting
|
||||||
|
```
|
||||||
|
- **Reduced Attack Surface**: No container runtime vulnerabilities for critical infrastructure
|
||||||
|
- **OS-Level Security**: Standard system security mechanisms apply directly
|
||||||
|
- **Group-Based Access Control**: Simple Unix group membership for service access
|
||||||
|
|
||||||
|
#### Operational Excellence
|
||||||
|
|
||||||
|
- **Standard Tooling**: Familiar systemd service management
|
||||||
|
```bash
|
||||||
|
systemctl status postgresql
|
||||||
|
journalctl -u postgresql -f
|
||||||
|
systemctl restart postgresql
|
||||||
|
```
|
||||||
|
- **Package Management**: Standard OS package updates and security patches
|
||||||
|
- **Backup Integration**: Native backup tools work seamlessly
|
||||||
|
```bash
|
||||||
|
pg_dump -h /var/run/postgresql authentik > backup.sql
|
||||||
|
```
|
||||||
|
- **Monitoring**: Standard system monitoring tools apply directly
|
||||||
|
|
||||||
|
#### Reliability
|
||||||
|
|
||||||
|
- **systemd Integration**: Robust service lifecycle management
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=PostgreSQL database server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=forking
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
```
|
||||||
|
- **Resource Isolation**: systemd provides resource isolation without container overhead
|
||||||
|
- **Proven Architecture**: Battle-tested approach used by major infrastructure providers
|
||||||
|
|
||||||
|
### Consequences
|
||||||
|
|
||||||
|
#### Positive
|
||||||
|
|
||||||
|
- **Performance**: 15-25% better database performance in benchmarks
|
||||||
|
- **Security**: Eliminated network-based database attacks via Unix sockets
|
||||||
|
- **Operations**: Simplified backup, monitoring, and maintenance procedures
|
||||||
|
- **Resource Usage**: Lower memory and CPU overhead
|
||||||
|
- **Reliability**: More predictable service behavior
|
||||||
|
|
||||||
|
#### Negative
|
||||||
|
|
||||||
|
- **Containerization Purity**: Not a "pure" containerized environment
|
||||||
|
- **Portability**: Slightly less portable than full-container approach
|
||||||
|
- **Learning Curve**: Team needs to understand both systemd and container management
|
||||||
|
|
||||||
|
#### Neutral
|
||||||
|
|
||||||
|
- **Complexity**: Different but not necessarily more complex than container orchestration
|
||||||
|
- **Tooling**: Different toolset but equally capable
|
||||||
|
|
||||||
|
### Implementation Notes
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Infrastructure services (native systemd)
|
||||||
|
- postgresql # Native database service
|
||||||
|
- valkey # Native cache service
|
||||||
|
- caddy # Native reverse proxy
|
||||||
|
- podman # Container runtime
|
||||||
|
|
||||||
|
# Application services (containerized)
|
||||||
|
- authentik # Authentication service
|
||||||
|
- gitea # Git service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alternatives Considered
|
||||||
|
|
||||||
|
1. **Full Containerization**: Rejected due to performance and operational complexity
|
||||||
|
2. **Mixed with Docker**: Rejected in favor of Podman for security benefits
|
||||||
|
3. **VM-based Infrastructure**: Rejected due to resource overhead
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-002: Unix Socket IPC Architecture
|
||||||
|
|
||||||
|
**Status**: ✅ Accepted
|
||||||
|
**Date**: December 2025
|
||||||
|
**Deciders**: Infrastructure Team
|
||||||
|
**Technical Story**: Secure and performant communication between containerized applications and native infrastructure services.
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
Containerized applications need to communicate with database and cache services. Communication methods include:
|
||||||
|
|
||||||
|
1. **Network TCP/IP**: Standard network protocols
|
||||||
|
2. **Unix Domain Sockets**: Filesystem-based IPC
|
||||||
|
3. **Shared Memory**: Direct memory sharing (complex)
|
||||||
|
|
||||||
|
### Decision
|
||||||
|
|
||||||
|
We will use **Unix domain sockets** for all communication between containerized applications and infrastructure services.
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
#### Security Benefits
|
||||||
|
|
||||||
|
- **No Network Exposure**: Infrastructure services bind only to Unix sockets
|
||||||
|
```bash
|
||||||
|
# PostgreSQL configuration
|
||||||
|
listen_addresses = '' # No TCP binding
|
||||||
|
unix_socket_directories = '/var/run/postgresql'
|
||||||
|
|
||||||
|
# Valkey configuration
|
||||||
|
port 0 # Disable TCP port
|
||||||
|
unixsocket /var/run/valkey/valkey.sock
|
||||||
|
```
|
||||||
|
- **Filesystem Permissions**: Access controlled by Unix file permissions
|
||||||
|
```bash
|
||||||
|
srwxrwx--- 1 postgres postgres 0 /var/run/postgresql/.s.PGSQL.5432
|
||||||
|
srwxrwx--- 1 valkey valkey 0 /var/run/valkey/valkey.sock
|
||||||
|
```
|
||||||
|
- **Group-Based Access**: Simple group membership controls access
|
||||||
|
```bash
|
||||||
|
# Add application user to infrastructure groups
|
||||||
|
usermod -a -G postgres,valkey authentik
|
||||||
|
```
|
||||||
|
- **No Network Scanning**: Services invisible to network reconnaissance
|
||||||
|
|
||||||
|
#### Performance Advantages
|
||||||
|
|
||||||
|
- **Lower Latency**: Unix sockets have ~20% lower latency than TCP loopback
|
||||||
|
- **Higher Throughput**: Up to 40% higher throughput for local communication
|
||||||
|
- **Reduced CPU Overhead**: No network stack processing required
|
||||||
|
- **Efficient Data Transfer**: Direct kernel-level data copying
|
||||||
|
|
||||||
|
#### Operational Benefits
|
||||||
|
|
||||||
|
- **Connection Reliability**: Filesystem-based connections are more reliable
|
||||||
|
- **Resource Monitoring**: Standard filesystem monitoring applies
|
||||||
|
- **Backup Friendly**: No network configuration to backup/restore
|
||||||
|
- **Debugging**: Standard filesystem tools for troubleshooting
|
||||||
|
|
||||||
|
### Implementation Strategy
|
||||||
|
|
||||||
|
#### Container Socket Access
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Container configuration (Quadlet)
|
||||||
|
[Container]
|
||||||
|
# Mount socket directories with proper labels
|
||||||
|
Volume=/var/run/postgresql:/var/run/postgresql:Z
|
||||||
|
Volume=/var/run/valkey:/var/run/valkey:Z
|
||||||
|
|
||||||
|
# Preserve user namespace and groups
|
||||||
|
PodmanArgs=--userns=host
|
||||||
|
Annotation=run.oci.keep_original_groups=1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Application Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Database connection (PostgreSQL)
|
||||||
|
DATABASE_URL=postgresql://authentik@/authentik?host=/var/run/postgresql
|
||||||
|
|
||||||
|
# Cache connection (Redis/Valkey)
|
||||||
|
CACHE_URL=unix:///var/run/valkey/valkey.sock?db=1&password=secret
|
||||||
|
```
|
||||||
|
|
||||||
|
#### User Management
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Ansible user setup
|
||||||
|
- name: Add application user to infrastructure groups
|
||||||
|
user:
|
||||||
|
name: "{{ app_user }}"
|
||||||
|
groups:
|
||||||
|
- postgres # For database access
|
||||||
|
- valkey # For cache access
|
||||||
|
append: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Consequences
|
||||||
|
|
||||||
|
#### Positive
|
||||||
|
|
||||||
|
- **Security**: Eliminated network attack vectors for databases
|
||||||
|
- **Performance**: Measurably faster database and cache operations
|
||||||
|
- **Reliability**: More stable connections than network-based
|
||||||
|
- **Simplicity**: Simpler configuration than network + authentication
|
||||||
|
|
||||||
|
#### Negative
|
||||||
|
|
||||||
|
- **Container Complexity**: Requires careful container user/group management
|
||||||
|
- **Learning Curve**: Less familiar than standard TCP connections
|
||||||
|
- **Port Forwarding**: Cannot use standard port forwarding for debugging
|
||||||
|
|
||||||
|
#### Mitigation Strategies
|
||||||
|
|
||||||
|
- **Documentation**: Comprehensive guides for Unix socket configuration
|
||||||
|
- **Testing**: Automated tests verify socket connectivity
|
||||||
|
- **Tooling**: Helper scripts for debugging socket connections
|
||||||
|
|
||||||
|
### Technical Implementation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test socket connectivity
|
||||||
|
sudo -u authentik psql -h /var/run/postgresql -U authentik -d authentik
|
||||||
|
sudo -u authentik redis-cli -s /var/run/valkey/valkey.sock ping
|
||||||
|
|
||||||
|
# Container user verification
|
||||||
|
podman exec authentik-server id
|
||||||
|
# uid=963(authentik) gid=963(authentik) groups=963(authentik),968(postgres),965(valkey)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alternatives Considered
|
||||||
|
|
||||||
|
1. **TCP with Authentication**: Rejected due to network exposure
|
||||||
|
2. **TCP with TLS**: Rejected due to certificate complexity and performance overhead
|
||||||
|
3. **Shared Memory**: Rejected due to implementation complexity
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-003: Podman + systemd Container Orchestration
|
||||||
|
|
||||||
|
**Status**: ✅ Accepted
|
||||||
|
**Date**: December 2025
|
||||||
|
**Deciders**: Infrastructure Team
|
||||||
|
**Technical Story**: Container orchestration solution for rootless, secure application deployment with systemd integration.
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
Container orchestration options for a single-node infrastructure:
|
||||||
|
|
||||||
|
1. **Docker + Docker Compose**: Traditional container orchestration
|
||||||
|
2. **Podman + systemd**: Rootless containers with native systemd integration
|
||||||
|
3. **Kubernetes**: Full orchestration platform (overkill for single node)
|
||||||
|
4. **Nomad**: HashiCorp orchestration solution
|
||||||
|
|
||||||
|
### Decision
|
||||||
|
|
||||||
|
We will use **Podman with systemd integration (Quadlet)** for container orchestration.
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
#### Security Advantages
|
||||||
|
|
||||||
|
- **Rootless Architecture**: No privileged daemon required
|
||||||
|
```bash
|
||||||
|
# Docker: Requires root daemon
|
||||||
|
sudo systemctl status docker
|
||||||
|
|
||||||
|
# Podman: Rootless operation
|
||||||
|
systemctl --user status podman
|
||||||
|
```
|
||||||
|
- **No Daemon Attack Surface**: No long-running privileged process
|
||||||
|
- **User Namespace Isolation**: Each user's containers are isolated
|
||||||
|
- **SELinux Integration**: Better SELinux support than Docker
|
||||||
|
|
||||||
|
#### systemd Integration Benefits
|
||||||
|
|
||||||
|
- **Native Service Management**: Containers as systemd services
|
||||||
|
```ini
|
||||||
|
# Quadlet file: ~/.config/containers/systemd/authentik.pod
|
||||||
|
[Unit]
|
||||||
|
Description=Authentik Authentication Pod
|
||||||
|
|
||||||
|
[Pod]
|
||||||
|
PublishPort=127.0.0.1:9000:9000
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
```
|
||||||
|
- **Dependency Management**: systemd handles service dependencies
|
||||||
|
- **Resource Control**: systemd resource limits and monitoring
|
||||||
|
- **Logging Integration**: journald for centralized logging
|
||||||
|
|
||||||
|
#### Operational Excellence
|
||||||
|
|
||||||
|
- **Familiar Tooling**: Standard systemd commands
|
||||||
|
```bash
|
||||||
|
systemctl --user status authentik-pod
|
||||||
|
systemctl --user restart authentik-server
|
||||||
|
journalctl --user -u authentik-server -f
|
||||||
|
```
|
||||||
|
- **Boot Integration**: Services start automatically with user sessions
|
||||||
|
- **Resource Monitoring**: systemd resource tracking
|
||||||
|
- **Configuration Management**: Declarative Quadlet files
|
||||||
|
|
||||||
|
#### Performance Benefits
|
||||||
|
|
||||||
|
- **Lower Overhead**: No daemon overhead for container management
|
||||||
|
- **Direct Kernel Access**: Better performance than daemon-based solutions
|
||||||
|
- **Resource Efficiency**: More efficient resource utilization
|
||||||
|
|
||||||
|
### Implementation Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ systemd User Session (authentik) │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌───────────────┐ │
|
||||||
|
│ │ authentik-pod │ │ authentik-server│ │authentik-worker│ │
|
||||||
|
│ │ .service │ │ .service │ │ .service │ │
|
||||||
|
│ └─────────────────┘ └─────────────────┘ └───────────────┘ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ └────────────────────┼────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Podman Pod (rootless) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ┌─────────────────┐ ┌─────────────────────────────────┐ │ │
|
||||||
|
│ │ │ Server Container│ │ Worker Container │ │ │
|
||||||
|
│ │ │ UID: 963 (host) │ │ UID: 963 (host) │ │ │
|
||||||
|
│ │ │ Groups: postgres│ │ Groups: postgres,valkey │ │ │
|
||||||
|
│ │ │ valkey │ │ │ │ │
|
||||||
|
│ │ └─────────────────┘ └─────────────────────────────────┘ │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Quadlet Configuration
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Pod configuration (authentik.pod)
|
||||||
|
[Unit]
|
||||||
|
Description=Authentik Authentication Pod
|
||||||
|
|
||||||
|
[Pod]
|
||||||
|
PublishPort=127.0.0.1:9000:9000
|
||||||
|
ShmSize=256m
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
```
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Container configuration (authentik-server.container)
|
||||||
|
[Unit]
|
||||||
|
Description=Authentik Server Container
|
||||||
|
After=authentik-pod.service
|
||||||
|
Requires=authentik-pod.service
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=authentik-server
|
||||||
|
Image=ghcr.io/goauthentik/server:2025.10
|
||||||
|
Pod=authentik.pod
|
||||||
|
EnvironmentFile=%h/.env
|
||||||
|
User=%i:%i
|
||||||
|
Annotation=run.oci.keep_original_groups=1
|
||||||
|
|
||||||
|
# Volume mounts for sockets
|
||||||
|
Volume=/var/run/postgresql:/var/run/postgresql:Z
|
||||||
|
Volume=/var/run/valkey:/var/run/valkey:Z
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
```
|
||||||
|
|
||||||
|
### User Management Strategy
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Ansible implementation
|
||||||
|
- name: Create service user
|
||||||
|
user:
|
||||||
|
name: authentik
|
||||||
|
system: true
|
||||||
|
home: /opt/authentik
|
||||||
|
create_home: true
|
||||||
|
|
||||||
|
- name: Add to infrastructure groups
|
||||||
|
user:
|
||||||
|
name: authentik
|
||||||
|
groups: [postgres, valkey]
|
||||||
|
append: true
|
||||||
|
|
||||||
|
- name: Enable lingering (services persist)
|
||||||
|
command: loginctl enable-linger authentik
|
||||||
|
```
|
||||||
|
|
||||||
|
### Consequences
|
||||||
|
|
||||||
|
#### Positive
|
||||||
|
|
||||||
|
- **Security**: Eliminated privileged daemon attack surface
|
||||||
|
- **Integration**: Seamless systemd integration for management
|
||||||
|
- **Performance**: Lower overhead than daemon-based solutions
|
||||||
|
- **Reliability**: systemd's proven service management
|
||||||
|
- **Monitoring**: Standard systemd monitoring and logging
|
||||||
|
|
||||||
|
#### Negative
|
||||||
|
|
||||||
|
- **Learning Curve**: Different from Docker Compose workflows
|
||||||
|
- **Tooling**: Ecosystem less mature than Docker
|
||||||
|
- **Documentation**: Fewer online resources and examples
|
||||||
|
|
||||||
|
#### Mitigation Strategies
|
||||||
|
|
||||||
|
- **Documentation**: Comprehensive internal documentation
|
||||||
|
- **Training**: Team training on Podman/systemd workflows
|
||||||
|
- **Tooling**: Helper scripts for common operations
|
||||||
|
|
||||||
|
### Technical Implementation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container management (as service user)
|
||||||
|
systemctl --user status authentik-pod
|
||||||
|
systemctl --user restart authentik-server
|
||||||
|
podman ps
|
||||||
|
podman logs authentik-server
|
||||||
|
|
||||||
|
# Resource monitoring
|
||||||
|
systemctl --user show authentik-server --property=MemoryCurrent
|
||||||
|
journalctl --user -u authentik-server -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alternatives Considered
|
||||||
|
|
||||||
|
1. **Docker + Docker Compose**: Rejected due to security concerns (privileged daemon)
|
||||||
|
2. **Kubernetes**: Rejected as overkill for single-node deployment
|
||||||
|
3. **Nomad**: Rejected to maintain consistency with systemd ecosystem
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-004: Forward Authentication Security Model
|
||||||
|
|
||||||
|
**Status**: ✅ Accepted
|
||||||
|
**Date**: December 2025
|
||||||
|
**Deciders**: Infrastructure Team
|
||||||
|
**Technical Story**: Centralized authentication and authorization for multiple services without modifying existing applications.
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
Authentication strategies for multiple services:
|
||||||
|
|
||||||
|
1. **Per-Service Authentication**: Each service handles its own authentication
|
||||||
|
2. **Shared Database**: Services share authentication database
|
||||||
|
3. **Forward Authentication**: Reverse proxy handles authentication
|
||||||
|
4. **OAuth2/OIDC Integration**: Services implement OAuth2 clients
|
||||||
|
|
||||||
|
### Decision
|
||||||
|
|
||||||
|
We will use **forward authentication** with Caddy reverse proxy and Authentik authentication server as the primary authentication model.
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
#### Security Benefits
|
||||||
|
|
||||||
|
- **Single Point of Control**: Centralized authentication policy
|
||||||
|
- **Zero Application Changes**: Protect existing services without modification
|
||||||
|
- **Consistent Security**: Same security model across all services
|
||||||
|
- **Session Management**: Centralized session handling and timeouts
|
||||||
|
- **Multi-Factor Authentication**: MFA applied consistently across services
|
||||||
|
|
||||||
|
#### Operational Advantages
|
||||||
|
|
||||||
|
- **Simplified Deployment**: No per-service authentication setup
|
||||||
|
- **Audit Trail**: Centralized authentication logging
|
||||||
|
- **Policy Management**: Single place to manage access policies
|
||||||
|
- **User Management**: One system for all user administration
|
||||||
|
- **Service Independence**: Services focus on business logic
|
||||||
|
|
||||||
|
#### Integration Benefits
|
||||||
|
|
||||||
|
- **Transparent to Applications**: Services receive authenticated requests
|
||||||
|
- **Header-Based Identity**: Simple identity propagation
|
||||||
|
```http
|
||||||
|
Remote-User: john.doe
|
||||||
|
Remote-Name: John Doe
|
||||||
|
Remote-Email: john.doe@company.com
|
||||||
|
Remote-Groups: admins,developers
|
||||||
|
```
|
||||||
|
- **Gradual Migration**: Can protect services incrementally
|
||||||
|
- **Fallback Support**: Can coexist with service-native authentication
|
||||||
|
|
||||||
|
### Implementation Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ User │ │ Caddy │ │ Authentik │ │ Service │
|
||||||
|
│ │ │ (Proxy) │ │ (Auth) │ │ (Backend) │
|
||||||
|
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
|
||||||
|
│ │ │ │
|
||||||
|
│ GET /dashboard │ │ │
|
||||||
|
│─────────────────▶│ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Forward Auth │ │
|
||||||
|
│ │─────────────────▶│ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ 401 Unauthorized │ │
|
||||||
|
│ │◀─────────────────│ │
|
||||||
|
│ │ │ │
|
||||||
|
│ 302 → /auth/login│ │ │
|
||||||
|
│◀─────────────────│ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ Login form │ │ │
|
||||||
|
│─────────────────▶│─────────────────▶│ │
|
||||||
|
│ │ │ │
|
||||||
|
│ Credentials │ │ │
|
||||||
|
│─────────────────▶│─────────────────▶│ │
|
||||||
|
│ │ │ │
|
||||||
|
│ Set-Cookie │ │ │
|
||||||
|
│◀─────────────────│◀─────────────────│ │
|
||||||
|
│ │ │ │
|
||||||
|
│ GET /dashboard │ │ │
|
||||||
|
│─────────────────▶│ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Forward Auth │ │
|
||||||
|
│ │─────────────────▶│ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ 200 + Headers │ │
|
||||||
|
│ │◀─────────────────│ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ GET /dashboard + Auth Headers │
|
||||||
|
│ │─────────────────────────────────────▶│
|
||||||
|
│ │ │
|
||||||
|
│ │ Dashboard Content │
|
||||||
|
│ │◀─────────────────────────────────────│
|
||||||
|
│ │ │
|
||||||
|
│ Dashboard │ │
|
||||||
|
│◀─────────────────│ │
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caddy Configuration
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# Service protection template
|
||||||
|
dashboard.jnss.me {
|
||||||
|
# Forward authentication to Authentik
|
||||||
|
forward_auth https://auth.jnss.me {
|
||||||
|
uri /outpost.goauthentik.io/auth/caddy
|
||||||
|
copy_headers Remote-User Remote-Name Remote-Email Remote-Groups
|
||||||
|
}
|
||||||
|
|
||||||
|
# Backend service (receives authenticated requests)
|
||||||
|
reverse_proxy localhost:8080
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Integration
|
||||||
|
|
||||||
|
Services receive authentication information via HTTP headers:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Example service code (Python Flask)
|
||||||
|
@app.route('/dashboard')
|
||||||
|
def dashboard():
|
||||||
|
username = request.headers.get('Remote-User')
|
||||||
|
name = request.headers.get('Remote-Name')
|
||||||
|
email = request.headers.get('Remote-Email')
|
||||||
|
groups = request.headers.get('Remote-Groups', '').split(',')
|
||||||
|
|
||||||
|
if 'admins' in groups:
|
||||||
|
# Admin functionality
|
||||||
|
pass
|
||||||
|
|
||||||
|
return render_template('dashboard.html',
|
||||||
|
username=username,
|
||||||
|
name=name)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authentik Provider Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Authentik Proxy Provider configuration
|
||||||
|
name: "Service Forward Auth"
|
||||||
|
authorization_flow: "default-authorization-flow"
|
||||||
|
external_host: "https://service.jnss.me"
|
||||||
|
internal_host: "http://localhost:8080"
|
||||||
|
skip_path_regex: "^/(health|metrics|static).*"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authorization Policies
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Example authorization policy in Authentik
|
||||||
|
policy_bindings:
|
||||||
|
- policy: "group_admins_only"
|
||||||
|
target: "service_dashboard"
|
||||||
|
order: 0
|
||||||
|
|
||||||
|
- policy: "deny_external_ips"
|
||||||
|
target: "admin_endpoints"
|
||||||
|
order: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Consequences
|
||||||
|
|
||||||
|
#### Positive
|
||||||
|
|
||||||
|
- **Security**: Consistent, centralized authentication and authorization
|
||||||
|
- **Simplicity**: No application changes required for protection
|
||||||
|
- **Flexibility**: Fine-grained access control through Authentik policies
|
||||||
|
- **Auditability**: Centralized authentication logging
|
||||||
|
- **User Experience**: Single sign-on across all services
|
||||||
|
|
||||||
|
#### Negative
|
||||||
|
|
||||||
|
- **Single Point of Failure**: Authentication system failure affects all services
|
||||||
|
- **Performance**: Additional hop for authentication checks
|
||||||
|
- **Complexity**: Additional component in the request path
|
||||||
|
|
||||||
|
#### Mitigation Strategies
|
||||||
|
|
||||||
|
- **High Availability**: Robust deployment and monitoring of auth components
|
||||||
|
- **Caching**: Session caching to reduce authentication overhead
|
||||||
|
- **Fallback**: Emergency bypass procedures for critical services
|
||||||
|
- **Monitoring**: Comprehensive monitoring of authentication flow
|
||||||
|
|
||||||
|
### Security Considerations
|
||||||
|
|
||||||
|
#### Session Security
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Authentik session settings
|
||||||
|
session_cookie_age: 3600 # 1 hour
|
||||||
|
session_cookie_secure: true
|
||||||
|
session_cookie_samesite: "Strict"
|
||||||
|
session_remember_me: false
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Access Control
|
||||||
|
|
||||||
|
- **Group-Based Authorization**: Users assigned to groups, groups to applications
|
||||||
|
- **Time-Based Access**: Temporary access grants
|
||||||
|
- **IP-Based Restrictions**: Geographic or network-based access control
|
||||||
|
- **MFA Requirements**: Multi-factor authentication for sensitive services
|
||||||
|
|
||||||
|
#### Audit Logging
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": "2025-12-11T17:52:31Z",
|
||||||
|
"event": "authentication_success",
|
||||||
|
"user": "john.doe",
|
||||||
|
"service": "dashboard.jnss.me",
|
||||||
|
"ip": "192.168.1.100",
|
||||||
|
"user_agent": "Mozilla/5.0..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alternative Models Supported
|
||||||
|
|
||||||
|
While forward auth is primary, we also support:
|
||||||
|
|
||||||
|
1. **OAuth2/OIDC Integration**: For applications that can implement OAuth2
|
||||||
|
2. **API Key Authentication**: For service-to-service communication
|
||||||
|
3. **Service-Native Auth**: For legacy applications that cannot be easily protected
|
||||||
|
|
||||||
|
### Implementation Examples
|
||||||
|
|
||||||
|
#### Protecting a Static Site
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
docs.jnss.me {
|
||||||
|
forward_auth https://auth.jnss.me {
|
||||||
|
uri /outpost.goauthentik.io/auth/caddy
|
||||||
|
copy_headers Remote-User Remote-Groups
|
||||||
|
}
|
||||||
|
|
||||||
|
root * /var/www/docs
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Protecting an API
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
api.jnss.me {
|
||||||
|
forward_auth https://auth.jnss.me {
|
||||||
|
uri /outpost.goauthentik.io/auth/caddy
|
||||||
|
copy_headers Remote-User Remote-Email Remote-Groups
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_proxy localhost:3000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Public Endpoints with Selective Protection
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
app.jnss.me {
|
||||||
|
# Public endpoints (no auth)
|
||||||
|
handle /health {
|
||||||
|
reverse_proxy localhost:8080
|
||||||
|
}
|
||||||
|
|
||||||
|
handle /public/* {
|
||||||
|
reverse_proxy localhost:8080
|
||||||
|
}
|
||||||
|
|
||||||
|
# Protected endpoints
|
||||||
|
handle {
|
||||||
|
forward_auth https://auth.jnss.me {
|
||||||
|
uri /outpost.goauthentik.io/auth/caddy
|
||||||
|
copy_headers Remote-User Remote-Groups
|
||||||
|
}
|
||||||
|
reverse_proxy localhost:8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alternatives Considered
|
||||||
|
|
||||||
|
1. **OAuth2 Only**: Rejected due to application modification requirements
|
||||||
|
2. **Shared Database**: Rejected due to tight coupling between services
|
||||||
|
3. **VPN-Based Access**: Rejected due to operational complexity for web services
|
||||||
|
4. **Per-Service Authentication**: Rejected due to management overhead
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
These architecture decisions collectively create a robust, secure, and performant infrastructure:
|
||||||
|
|
||||||
|
- **Native Services** provide optimal performance and security
|
||||||
|
- **Unix Sockets** eliminate network attack vectors
|
||||||
|
- **Podman + systemd** delivers secure container orchestration
|
||||||
|
- **Forward Authentication** enables centralized security without application changes
|
||||||
|
|
||||||
|
The combination results in an infrastructure that prioritizes security and performance while maintaining operational simplicity and reliability.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Service Integration Guide](service-integration-guide.md)
|
||||||
|
- [Authentik Deployment Guide](authentik-deployment-guide.md)
|
||||||
|
- [Security Hardening](security-hardening.md)
|
||||||
|
- [Authentik Role Documentation](../roles/authentik/README.md)
|
||||||
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
|
||||||
642
docs/authentik-deployment-guide.md
Normal file
642
docs/authentik-deployment-guide.md
Normal file
@@ -0,0 +1,642 @@
|
|||||||
|
# Authentik Deployment Guide
|
||||||
|
|
||||||
|
A comprehensive guide for deploying Authentik authentication server with native database services and Unix socket IPC in the rick-infra environment.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide covers the complete deployment process for Authentik, a modern authentication and authorization server, integrated with:
|
||||||
|
|
||||||
|
- **Native PostgreSQL** - High-performance database with Unix socket IPC
|
||||||
|
- **Native Valkey** - Redis-compatible cache with Unix socket IPC
|
||||||
|
- **Rootless Podman** - Secure container orchestration via systemd/Quadlet
|
||||||
|
- **Caddy Reverse Proxy** - TLS termination and forward authentication
|
||||||
|
|
||||||
|
## Architecture Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ Internet │ │ Caddy Proxy │ │ Authentik Pod │
|
||||||
|
│ │───▶│ auth.jnss.me │───▶│ │
|
||||||
|
│ HTTPS/443 │ │ TLS + Forward │ │ Server + Worker │
|
||||||
|
└─────────────────┘ │ Auth │ │ Containers │
|
||||||
|
└─────────────────┘ └─────────────────┘
|
||||||
|
│
|
||||||
|
┌─────────────────────────────────┼─────────────────┐
|
||||||
|
│ Host Infrastructure │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────┐ ┌─────────────────────────┐ │
|
||||||
|
│ │ PostgreSQL │ │ Valkey │ │
|
||||||
|
│ │ (Native) │ │ (Redis-compatible) │ │
|
||||||
|
│ │ Unix Socket │ │ Unix Socket │ │
|
||||||
|
│ └─────────────┘ └─────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### Infrastructure Requirements
|
||||||
|
|
||||||
|
Before deploying Authentik, ensure the following infrastructure services are running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify required services are active
|
||||||
|
ssh root@your-vps "systemctl is-active postgresql valkey caddy podman"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output: All services should show `active`
|
||||||
|
|
||||||
|
### Required Infrastructure Components
|
||||||
|
|
||||||
|
1. **PostgreSQL Database**
|
||||||
|
- Native systemd service with Unix socket enabled
|
||||||
|
- Socket location: `/var/run/postgresql/.s.PGSQL.5432`
|
||||||
|
|
||||||
|
2. **Valkey Cache Service**
|
||||||
|
- Native systemd service with Unix socket enabled
|
||||||
|
- Socket location: `/var/run/valkey/valkey.sock`
|
||||||
|
|
||||||
|
3. **Podman Container Runtime**
|
||||||
|
- Rootless container support configured
|
||||||
|
- systemd user session support enabled
|
||||||
|
|
||||||
|
4. **Caddy Web Server**
|
||||||
|
- TLS/SSL termination configured
|
||||||
|
- API management enabled for dynamic configuration
|
||||||
|
|
||||||
|
### DNS Configuration
|
||||||
|
|
||||||
|
Ensure DNS records are configured for your authentik domain:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify DNS resolution
|
||||||
|
dig +short auth.jnss.me
|
||||||
|
# Should return your VPS IP address
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network Requirements
|
||||||
|
|
||||||
|
- **Port 80/443**: Open for HTTP/HTTPS traffic
|
||||||
|
- **Internal communications**: Unix sockets (no additional ports required)
|
||||||
|
|
||||||
|
## Vault Variables Setup
|
||||||
|
|
||||||
|
### Required Vault Variables
|
||||||
|
|
||||||
|
Create or update `host_vars/arch-vps/vault.yml` with the following encrypted variables:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
# Authentik Database Password
|
||||||
|
vault_authentik_db_password: "secure_random_password_32_chars"
|
||||||
|
|
||||||
|
# Authentik Secret Key (generate with: openssl rand -base64 32)
|
||||||
|
vault_authentik_secret_key: "your_generated_secret_key_here"
|
||||||
|
|
||||||
|
# Authentik Admin Password
|
||||||
|
vault_authentik_admin_password: "secure_admin_password"
|
||||||
|
|
||||||
|
# Infrastructure Dependencies (should already exist)
|
||||||
|
vault_valkey_password: "valkey_password"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate Required Secrets
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate Authentik secret key
|
||||||
|
openssl rand -base64 32
|
||||||
|
|
||||||
|
# Generate secure passwords
|
||||||
|
openssl rand -base64 20
|
||||||
|
```
|
||||||
|
|
||||||
|
### Encrypt Vault File
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Encrypt the vault file
|
||||||
|
ansible-vault encrypt host_vars/arch-vps/vault.yml
|
||||||
|
|
||||||
|
# Verify vault variables
|
||||||
|
ansible-vault view host_vars/arch-vps/vault.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pre-deployment Validation
|
||||||
|
|
||||||
|
### Infrastructure Health Check
|
||||||
|
|
||||||
|
Run the following commands to verify infrastructure readiness:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Check PostgreSQL Unix socket
|
||||||
|
ssh root@your-vps "ls -la /var/run/postgresql/"
|
||||||
|
# Should show .s.PGSQL.5432 socket file
|
||||||
|
|
||||||
|
# 2. Check Valkey Unix socket
|
||||||
|
ssh root@your-vps "ls -la /var/run/valkey/"
|
||||||
|
# Should show valkey.sock file
|
||||||
|
|
||||||
|
# 3. Test PostgreSQL connectivity
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'SELECT version();'"
|
||||||
|
# Should return PostgreSQL version
|
||||||
|
|
||||||
|
# 4. Test Valkey connectivity
|
||||||
|
ssh root@your-vps "redis-cli -s /var/run/valkey/valkey.sock ping"
|
||||||
|
# Should return "PONG"
|
||||||
|
|
||||||
|
# 5. Verify Caddy is responsive
|
||||||
|
curl -I https://jnss.me/
|
||||||
|
# Should return HTTP/2 200
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ansible Configuration Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test Ansible connectivity
|
||||||
|
ansible arch-vps -m ping
|
||||||
|
|
||||||
|
# Verify vault variables can be decrypted
|
||||||
|
ansible arch-vps -m debug -a "var=vault_authentik_secret_key" --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step-by-Step Deployment
|
||||||
|
|
||||||
|
### Step 1: Enable Authentik Role
|
||||||
|
|
||||||
|
Update `site.yml` to include the authentik role:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Deploy Core Infrastructure
|
||||||
|
hosts: arch-vps
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
# Infrastructure dependencies handled automatically via meta/main.yml
|
||||||
|
- role: authentik
|
||||||
|
tags: ['authentik', 'auth', 'sso']
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Execute Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Full deployment with verbose output
|
||||||
|
ansible-playbook -i inventory/hosts.yml site.yml --tags authentik --ask-vault-pass -v
|
||||||
|
|
||||||
|
# Alternative: Deploy with specific components
|
||||||
|
ansible-playbook -i inventory/hosts.yml site.yml --tags authentik,database,containers --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Monitor Deployment Progress
|
||||||
|
|
||||||
|
During deployment, monitor the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Monitor deployment logs in real-time (separate terminal)
|
||||||
|
ssh root@your-vps "journalctl -f"
|
||||||
|
|
||||||
|
# Watch for authentik-specific services
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ status"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Verify Container Deployment
|
||||||
|
|
||||||
|
After deployment completion:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check systemd user services for authentik user
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ list-units 'authentik*'"
|
||||||
|
|
||||||
|
# Verify containers are running
|
||||||
|
ssh root@your-vps "sudo -u authentik podman ps"
|
||||||
|
|
||||||
|
# Check pod status
|
||||||
|
ssh root@your-vps "sudo -u authentik podman pod ps"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Health Check Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test internal HTTP endpoint
|
||||||
|
ssh root@your-vps "curl -I http://127.0.0.1:9000/"
|
||||||
|
# Expected: HTTP/1.1 302 Found (redirect to login)
|
||||||
|
|
||||||
|
# Test external HTTPS endpoint
|
||||||
|
curl -I https://auth.jnss.me/
|
||||||
|
# Expected: HTTP/2 200 or 302
|
||||||
|
|
||||||
|
# Test authentik API endpoint
|
||||||
|
curl -s https://auth.jnss.me/api/v3/admin/version/
|
||||||
|
# Expected: JSON response with authentication error (proves API is responsive)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Post-deployment Configuration
|
||||||
|
|
||||||
|
### Initial Admin Access
|
||||||
|
|
||||||
|
1. **Access Web Interface**:
|
||||||
|
```bash
|
||||||
|
# Open in browser
|
||||||
|
https://auth.jnss.me/
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Admin Login**:
|
||||||
|
- **Username**: `admin@auth.jnss.me` (or configured admin email)
|
||||||
|
- **Password**: Value from `vault_authentik_admin_password`
|
||||||
|
|
||||||
|
3. **Verify Admin Access**:
|
||||||
|
- Navigate to `/if/admin/`
|
||||||
|
- Confirm admin interface loads successfully
|
||||||
|
- Check system status in admin dashboard
|
||||||
|
|
||||||
|
### Essential Configuration Tasks
|
||||||
|
|
||||||
|
#### 1. Configure OAuth2 Provider
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Navigate to Applications → Providers → Create
|
||||||
|
# Provider Type: OAuth2/OpenID Provider
|
||||||
|
# Name: "Default OAuth2 Provider"
|
||||||
|
# Client Type: Confidential
|
||||||
|
# Authorization Grant Type: Authorization Code
|
||||||
|
# Redirect URIs: Add your application callback URLs
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Create Application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Navigate to Applications → Applications → Create
|
||||||
|
# Name: "Your Application Name"
|
||||||
|
# Slug: "your-app"
|
||||||
|
# Provider: Select OAuth2 provider created above
|
||||||
|
# Launch URL: Your application URL
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Configure Forward Auth (for Caddy integration)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Navigate to Applications → Providers → Create
|
||||||
|
# Provider Type: Proxy Provider
|
||||||
|
# Name: "Forward Auth Provider"
|
||||||
|
# External Host: https://your-service.jnss.me
|
||||||
|
# Internal Host: http://localhost:8080 (your service backend)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Service Integration Examples
|
||||||
|
|
||||||
|
### Example 1: Protect Existing HTTP Service with Forward Auth
|
||||||
|
|
||||||
|
Add to your service's Caddy configuration:
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# In /etc/caddy/sites-enabled/myservice.caddy
|
||||||
|
myservice.jnss.me {
|
||||||
|
# Forward authentication to authentik
|
||||||
|
forward_auth https://auth.jnss.me {
|
||||||
|
uri /outpost.goauthentik.io/auth/caddy
|
||||||
|
copy_headers Remote-User Remote-Name Remote-Email Remote-Groups
|
||||||
|
}
|
||||||
|
|
||||||
|
# Your service backend
|
||||||
|
reverse_proxy localhost:8080
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: OAuth2 Integration for Custom Applications
|
||||||
|
|
||||||
|
For applications that can handle OAuth2 directly:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Application configuration
|
||||||
|
OAUTH2_PROVIDER_URL: "https://auth.jnss.me/application/o/authorize/"
|
||||||
|
OAUTH2_TOKEN_URL: "https://auth.jnss.me/application/o/token/"
|
||||||
|
OAUTH2_USER_INFO_URL: "https://auth.jnss.me/application/o/userinfo/"
|
||||||
|
OAUTH2_CLIENT_ID: "your_client_id"
|
||||||
|
OAUTH2_CLIENT_SECRET: "your_client_secret"
|
||||||
|
OAUTH2_REDIRECT_URI: "https://yourapp.jnss.me/oauth/callback"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting Guide
|
||||||
|
|
||||||
|
### Common Issues and Solutions
|
||||||
|
|
||||||
|
#### Issue: Containers fail to start with socket permission errors
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
```
|
||||||
|
Error: failed to connect to database: permission denied
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Check authentik user group membership
|
||||||
|
ssh root@your-vps "groups authentik"
|
||||||
|
# Should show: authentik postgres valkey
|
||||||
|
|
||||||
|
# Verify socket permissions
|
||||||
|
ssh root@your-vps "ls -la /var/run/postgresql/ /var/run/valkey/"
|
||||||
|
|
||||||
|
# Fix group membership if missing
|
||||||
|
ansible-playbook site.yml --tags authentik,user,setup --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Issue: HTTP binding errors (address already in use)
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
```
|
||||||
|
Error: bind: address already in use (port 9000)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Check what's using port 9000
|
||||||
|
ssh root@your-vps "netstat -tulpn | grep 9000"
|
||||||
|
|
||||||
|
# Stop conflicting services
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ stop authentik-pod"
|
||||||
|
|
||||||
|
# Restart with correct configuration
|
||||||
|
ansible-playbook site.yml --tags authentik,containers --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Issue: Database connection failures
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
```
|
||||||
|
FATAL: database "authentik" does not exist
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Recreate database and user
|
||||||
|
ansible-playbook site.yml --tags authentik,database --ask-vault-pass
|
||||||
|
|
||||||
|
# Verify database creation
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c '\l'"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Issue: Cache connection failures
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
```
|
||||||
|
Error connecting to Redis: Connection refused
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Check Valkey service status
|
||||||
|
ssh root@your-vps "systemctl status valkey"
|
||||||
|
|
||||||
|
# Test socket connectivity
|
||||||
|
ssh root@your-vps "redis-cli -s /var/run/valkey/valkey.sock ping"
|
||||||
|
|
||||||
|
# Redeploy cache configuration if needed
|
||||||
|
ansible-playbook site.yml --tags authentik,cache --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### Diagnostic Commands
|
||||||
|
|
||||||
|
#### Container Debugging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check container logs
|
||||||
|
ssh root@your-vps "sudo -u authentik podman logs authentik-server"
|
||||||
|
ssh root@your-vps "sudo -u authentik podman logs authentik-worker"
|
||||||
|
|
||||||
|
# Inspect container configuration
|
||||||
|
ssh root@your-vps "sudo -u authentik podman inspect authentik-server"
|
||||||
|
|
||||||
|
# Check container user/group mapping
|
||||||
|
ssh root@your-vps "sudo -u authentik podman exec authentik-server id"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Service Status Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check all authentik systemd services
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ status authentik-pod authentik-server authentik-worker"
|
||||||
|
|
||||||
|
# View service dependencies
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ list-dependencies authentik-pod"
|
||||||
|
|
||||||
|
# Check user session status
|
||||||
|
ssh root@your-vps "loginctl show-user authentik"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Network Connectivity Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test internal HTTP binding
|
||||||
|
ssh root@your-vps "curl -v http://127.0.0.1:9000/"
|
||||||
|
|
||||||
|
# Test Caddy reverse proxy
|
||||||
|
ssh root@your-vps "curl -v http://127.0.0.1:80/ -H 'Host: auth.jnss.me'"
|
||||||
|
|
||||||
|
# Test external HTTPS
|
||||||
|
curl -v https://auth.jnss.me/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Analysis
|
||||||
|
|
||||||
|
#### Key Log Locations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Authentik application logs
|
||||||
|
ssh root@your-vps "sudo -u authentik cat /opt/authentik/logs/server.log"
|
||||||
|
ssh root@your-vps "sudo -u authentik cat /opt/authentik/logs/worker.log"
|
||||||
|
|
||||||
|
# systemd service logs
|
||||||
|
ssh root@your-vps "journalctl --user -M authentik@ -u authentik-server -f"
|
||||||
|
ssh root@your-vps "journalctl --user -M authentik@ -u authentik-worker -f"
|
||||||
|
|
||||||
|
# Caddy logs for reverse proxy issues
|
||||||
|
ssh root@your-vps "journalctl -u caddy -f"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Common Log Patterns
|
||||||
|
|
||||||
|
**Successful startup**:
|
||||||
|
```
|
||||||
|
INFO authentik.core.signals: authentik 2025.10.x starting
|
||||||
|
INFO authentik.core.models: Database version up-to-date
|
||||||
|
```
|
||||||
|
|
||||||
|
**Database connection success**:
|
||||||
|
```
|
||||||
|
INFO authentik.core.db: Connected to database via unix socket
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cache connection success**:
|
||||||
|
```
|
||||||
|
INFO authentik.core.cache: Connected to cache via unix socket
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Monitoring
|
||||||
|
|
||||||
|
### Resource Usage Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Monitor container resource usage
|
||||||
|
ssh root@your-vps "sudo -u authentik podman stats"
|
||||||
|
|
||||||
|
# Monitor service memory usage
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ status authentik-server | grep Memory"
|
||||||
|
|
||||||
|
# Monitor database connections
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'SELECT * FROM pg_stat_activity;'"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Optimization Tips
|
||||||
|
|
||||||
|
1. **Database Performance**:
|
||||||
|
- Monitor PostgreSQL slow query log
|
||||||
|
- Consider database connection pooling for high traffic
|
||||||
|
- Regular database maintenance (VACUUM, ANALYZE)
|
||||||
|
|
||||||
|
2. **Cache Performance**:
|
||||||
|
- Monitor Valkey memory usage and hit rate
|
||||||
|
- Adjust cache TTL settings based on usage patterns
|
||||||
|
- Consider cache warming for frequently accessed data
|
||||||
|
|
||||||
|
3. **Container Performance**:
|
||||||
|
- Monitor container memory limits and usage
|
||||||
|
- Optimize shared memory configuration if needed
|
||||||
|
- Review worker process configuration
|
||||||
|
|
||||||
|
## Maintenance Tasks
|
||||||
|
|
||||||
|
### Regular Maintenance
|
||||||
|
|
||||||
|
#### Update Authentik Version
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Update version in defaults or inventory
|
||||||
|
authentik_version: "2025.12.1" # New version
|
||||||
|
|
||||||
|
# Deploy update
|
||||||
|
ansible-playbook site.yml --tags authentik,containers --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Backup Procedures
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Database backup
|
||||||
|
ssh root@your-vps "sudo -u postgres pg_dump -h /var/run/postgresql authentik > /backup/authentik-$(date +%Y%m%d).sql"
|
||||||
|
|
||||||
|
# Media files backup
|
||||||
|
ssh root@your-vps "tar -czf /backup/authentik-media-$(date +%Y%m%d).tar.gz -C /opt/authentik media"
|
||||||
|
|
||||||
|
# Configuration backup (run from ansible control machine)
|
||||||
|
ansible-vault view host_vars/arch-vps/vault.yml > backup/authentik-vault-$(date +%Y%m%d).yml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Health Monitoring
|
||||||
|
|
||||||
|
Set up regular health checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Health check script
|
||||||
|
HEALTH_URL="https://auth.jnss.me/if/health/live/"
|
||||||
|
if ! curl -f -s "$HEALTH_URL" > /dev/null; then
|
||||||
|
echo "Authentik health check failed"
|
||||||
|
# Add alerting logic
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security Maintenance
|
||||||
|
|
||||||
|
#### Certificate Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check certificate expiration
|
||||||
|
ssh root@your-vps "curl -vI https://auth.jnss.me/ 2>&1 | grep expire"
|
||||||
|
|
||||||
|
# Caddy handles renewal automatically, but monitor logs
|
||||||
|
ssh root@your-vps "journalctl -u caddy | grep -i cert"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Security Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update container images regularly
|
||||||
|
ansible-playbook site.yml --tags authentik,image-pull --ask-vault-pass
|
||||||
|
|
||||||
|
# Monitor for Authentik security advisories
|
||||||
|
# https://github.com/goauthentik/authentik/security/advisories
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support and Resources
|
||||||
|
|
||||||
|
### Documentation References
|
||||||
|
|
||||||
|
- **Authentik Official Documentation**: https://docs.goauthentik.io/
|
||||||
|
- **rick-infra Architecture Decisions**: [docs/architecture-decisions.md](architecture-decisions.md)
|
||||||
|
- **Service Integration Guide**: [docs/service-integration-guide.md](service-integration-guide.md)
|
||||||
|
- **Security Model**: [docs/security-hardening.md](security-hardening.md)
|
||||||
|
|
||||||
|
### Community Resources
|
||||||
|
|
||||||
|
- **Authentik Community Forum**: https://community.goauthentik.io/
|
||||||
|
- **GitHub Issues**: https://github.com/goauthentik/authentik/issues
|
||||||
|
- **Discord Community**: https://discord.gg/jg33eMhnj6
|
||||||
|
|
||||||
|
### Emergency Procedures
|
||||||
|
|
||||||
|
#### Service Recovery
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Emergency service restart
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ restart authentik-pod"
|
||||||
|
|
||||||
|
# Fallback: Direct container management
|
||||||
|
ssh root@your-vps "sudo -u authentik podman pod restart authentik"
|
||||||
|
|
||||||
|
# Last resort: Full service rebuild
|
||||||
|
ansible-playbook site.yml --tags authentik --ask-vault-pass --limit arch-vps
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rollback Procedures
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rollback to previous container version
|
||||||
|
authentik_version: "previous_working_version"
|
||||||
|
ansible-playbook site.yml --tags authentik,containers --ask-vault-pass
|
||||||
|
|
||||||
|
# Database rollback (if needed)
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql authentik < /backup/authentik-backup.sql"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Checklist
|
||||||
|
|
||||||
|
Use this checklist to ensure complete deployment:
|
||||||
|
|
||||||
|
### Pre-deployment
|
||||||
|
- [ ] Infrastructure services (PostgreSQL, Valkey, Caddy, Podman) running
|
||||||
|
- [ ] DNS records configured for auth.jnss.me
|
||||||
|
- [ ] Vault variables configured and encrypted
|
||||||
|
- [ ] Ansible connectivity verified
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
- [ ] Authentik role enabled in site.yml
|
||||||
|
- [ ] Deployment executed successfully
|
||||||
|
- [ ] Health checks passing
|
||||||
|
- [ ] Containers running and responsive
|
||||||
|
|
||||||
|
### Post-deployment
|
||||||
|
- [ ] Admin web interface accessible
|
||||||
|
- [ ] Initial admin login successful
|
||||||
|
- [ ] OAuth2 provider configured
|
||||||
|
- [ ] Test application integration
|
||||||
|
- [ ] Forward auth configuration tested
|
||||||
|
|
||||||
|
### Production Readiness
|
||||||
|
- [ ] Backup procedures implemented
|
||||||
|
- [ ] Monitoring and alerting configured
|
||||||
|
- [ ] Security review completed
|
||||||
|
- [ ] Documentation updated
|
||||||
|
- [ ] Team training completed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This comprehensive deployment guide provides everything needed to successfully deploy and maintain Authentik in the rick-infra environment, emphasizing the security and performance benefits of our native database + Unix socket architecture.
|
||||||
@@ -1,51 +1,195 @@
|
|||||||
# Deployment Guide
|
# Deployment Guide
|
||||||
|
|
||||||
This guide explains how to deploy your infrastructure using the updated Caddy API registration system.
|
This guide explains how to deploy your infrastructure including core services, authentication, and applications.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The deployment system has been restructured to support:
|
The rick-infra deployment system provides:
|
||||||
- **Core Infrastructure**: Caddy web server with API capabilities
|
- **Native Infrastructure**: PostgreSQL, Valkey, Podman, Caddy managed by systemd
|
||||||
- **Service Registration**: Dynamic service registration via API
|
- **Authentication Services**: Authentik SSO with forward auth integration
|
||||||
- **Zero Downtime**: Services can be added/removed without restarts
|
- **Application Services**: Containerized services with Unix socket IPC
|
||||||
|
- **Security First**: All services deployed with security hardening by default
|
||||||
|
|
||||||
## Available Playbooks
|
## Architecture Overview
|
||||||
|
|
||||||
### 1. `site.yml` - Core Infrastructure
|
```
|
||||||
Deploys security hardening followed by Caddy web server infrastructure.
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ rick-infra Infrastructure Stack │
|
||||||
```bash
|
│ │
|
||||||
ansible-playbook -i inventory/hosts.yml site.yml
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌───────────────┐ │
|
||||||
|
│ │ Applications │ │ Authentication │ │ Reverse Proxy │ │
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
│ │ Gitea (Git) │ │ Authentik (SSO) │ │ Caddy (HTTPS) │ │
|
||||||
|
│ │ Gallery (Media) │ │ Forward Auth │ │ Auto TLS │ │
|
||||||
|
│ │ Custom Services │ │ OAuth2/OIDC │ │ Load Balance │ │
|
||||||
|
│ └─────────────────┘ └─────────────────┘ └───────────────┘ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ └────────────────────┼────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Infrastructure Services (Native systemd) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │
|
||||||
|
│ │ │ PostgreSQL │ │ Valkey │ │ Podman │ │ │
|
||||||
|
│ │ │ (Database) │ │ (Cache) │ │ (Containers) │ │ │
|
||||||
|
│ │ │Unix Sockets │ │Unix Sockets │ │ Rootless │ │ │
|
||||||
|
│ │ └─────────────┘ └─────────────┘ └─────────────────┘ │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Security Foundation │ │
|
||||||
|
│ │ SSH Hardening • Firewall • Fail2ban • Updates │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
**What it does:**
|
## Available Deployments
|
||||||
- **Phase 1 - Security**: System updates, SSH hardening, nftables firewall, fail2ban
|
|
||||||
- **Phase 2 - Caddy**: Installs Caddy with Cloudflare DNS plugin
|
### 1. `site.yml` - Complete Infrastructure Stack
|
||||||
- Configures TLS with Let's Encrypt
|
Deploys the full rick-infra stack with role dependencies automatically managed.
|
||||||
- Sets up named server for API targeting
|
|
||||||
- Enables API persistence with `--resume`
|
```bash
|
||||||
- Serves main domain (jnss.me)
|
ansible-playbook -i inventory/hosts.yml site.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it deploys:**
|
||||||
|
- **Security Foundation**: SSH hardening, firewall, fail2ban, system updates
|
||||||
|
- **Infrastructure Services**: PostgreSQL, Valkey, Podman container runtime
|
||||||
|
- **Reverse Proxy**: Caddy with automatic HTTPS and Cloudflare DNS integration
|
||||||
|
- **Authentication**: Authentik SSO server with forward auth integration
|
||||||
|
- **Applications**: Gitea, Gallery, and other configured services
|
||||||
|
|
||||||
|
### 2. Service-Specific Deployments
|
||||||
|
Deploy individual components using tags:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy only infrastructure services
|
||||||
|
ansible-playbook site.yml --tags postgresql,valkey,podman,caddy --ask-vault-pass
|
||||||
|
|
||||||
|
# Deploy only authentication
|
||||||
|
ansible-playbook site.yml --tags authentik --ask-vault-pass
|
||||||
|
|
||||||
|
# Deploy only applications
|
||||||
|
ansible-playbook site.yml --tags gitea,sigvild-gallery --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Security-Only Deployment
|
||||||
|
```bash
|
||||||
|
# Deploy only security hardening
|
||||||
|
ansible-playbook playbooks/security.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
## Deployment Patterns
|
## Deployment Patterns
|
||||||
|
|
||||||
### First-Time Deployment
|
### First-Time Complete Deployment
|
||||||
|
|
||||||
⚠️ **Important**: First-time deployments include security hardening that may require a system reboot.
|
⚠️ **Important**: First-time deployments include security hardening that may require a system reboot.
|
||||||
|
|
||||||
1. **Deploy Core Infrastructure**
|
#### Prerequisites
|
||||||
```bash
|
|
||||||
# Option 1: Security + Basic infrastructure
|
|
||||||
ansible-playbook -i inventory/hosts.yml site.yml --ask-vault-pass
|
|
||||||
|
|
||||||
# Option 2: Complete deployment with comprehensive verification
|
|
||||||
ansible-playbook -i inventory/hosts.yml deploy.yml --ask-vault-pass
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note**: The security hardening phase may:
|
1. **VPS Setup**: Fresh Arch Linux VPS with root access
|
||||||
- Update all system packages
|
2. **DNS Configuration**: Domain pointed to VPS IP address
|
||||||
- Reboot the system if kernel updates are applied
|
3. **Vault Variables**: Required secrets configured (see [Vault Setup](#vault-variables))
|
||||||
- Configure SSH, firewall, and fail2ban
|
4. **SSH Access**: Key-based authentication configured
|
||||||
- This ensures a secure foundation before deploying web services
|
|
||||||
|
#### Step-by-Step First Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Test connectivity
|
||||||
|
ansible arch-vps -m ping
|
||||||
|
|
||||||
|
# 2. Deploy complete infrastructure stack
|
||||||
|
ansible-playbook -i inventory/hosts.yml site.yml --ask-vault-pass
|
||||||
|
|
||||||
|
# 3. Verify deployment
|
||||||
|
curl -I https://auth.jnss.me/
|
||||||
|
curl -I https://git.jnss.me/
|
||||||
|
```
|
||||||
|
|
||||||
|
**What happens during first deployment:**
|
||||||
|
|
||||||
|
1. **Security Hardening** (1-2 minutes)
|
||||||
|
- System package updates
|
||||||
|
- SSH configuration hardening
|
||||||
|
- nftables firewall setup
|
||||||
|
- fail2ban intrusion detection
|
||||||
|
- Kernel security parameters
|
||||||
|
- *May require reboot if kernel updated*
|
||||||
|
|
||||||
|
2. **Infrastructure Services** (2-3 minutes)
|
||||||
|
- PostgreSQL database with Unix sockets
|
||||||
|
- Valkey cache with Unix sockets
|
||||||
|
- Podman rootless container setup
|
||||||
|
- Caddy reverse proxy with auto-HTTPS
|
||||||
|
|
||||||
|
3. **Authentication Service** (3-5 minutes)
|
||||||
|
- Authentik container deployment
|
||||||
|
- Database and cache integration
|
||||||
|
- Forward auth configuration
|
||||||
|
- Admin user initialization
|
||||||
|
|
||||||
|
4. **Application Services** (2-4 minutes)
|
||||||
|
- Gitea Git service
|
||||||
|
- Gallery media service
|
||||||
|
- Service-specific configurations
|
||||||
|
|
||||||
|
**Total deployment time**: 8-14 minutes for complete stack
|
||||||
|
|
||||||
|
### Incremental Deployment
|
||||||
|
|
||||||
|
For subsequent deployments or updates:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy only changed components
|
||||||
|
ansible-playbook site.yml --tags authentik --ask-vault-pass
|
||||||
|
|
||||||
|
# Deploy with check mode first (dry run)
|
||||||
|
ansible-playbook site.yml --check --ask-vault-pass
|
||||||
|
|
||||||
|
# Deploy with verbose output for debugging
|
||||||
|
ansible-playbook site.yml -v --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication Deployment
|
||||||
|
|
||||||
|
### Authentik SSO Server
|
||||||
|
|
||||||
|
Rick-infra includes comprehensive authentication via Authentik. For detailed deployment instructions, see the [Authentik Deployment Guide](authentik-deployment-guide.md).
|
||||||
|
|
||||||
|
#### Quick Authentik Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy authentik and all dependencies
|
||||||
|
ansible-playbook site.yml --tags authentik --ask-vault-pass
|
||||||
|
|
||||||
|
# Verify authentik deployment
|
||||||
|
curl -I https://auth.jnss.me/
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ status authentik-pod"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Authentik Integration Pattern
|
||||||
|
|
||||||
|
All services in rick-infra use forward authentication through Caddy:
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# Example service with authentik protection
|
||||||
|
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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Post-Deployment Authentik Setup
|
||||||
|
|
||||||
|
1. **Access Admin Interface**: `https://auth.jnss.me/if/admin/`
|
||||||
|
2. **Default Admin**: Email from `authentik_default_admin_email` variable
|
||||||
|
3. **Initial Configuration**: Follow [Authentik Deployment Guide](authentik-deployment-guide.md#post-deployment-configuration)
|
||||||
|
|
||||||
|
For complete authentik architecture details, see [Architecture Decisions](architecture-decisions.md#adr-004-forward-authentication-security-model).
|
||||||
|
|
||||||
## Configuration Management
|
## Configuration Management
|
||||||
|
|
||||||
@@ -78,23 +222,279 @@ caddy_systemd_security: true
|
|||||||
Sensitive data in `host_vars/arch-vps/vault.yml` (encrypted):
|
Sensitive data in `host_vars/arch-vps/vault.yml` (encrypted):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
# Infrastructure secrets
|
||||||
vault_caddy_tls_email: "admin@jnss.me"
|
vault_caddy_tls_email: "admin@jnss.me"
|
||||||
vault_cloudflare_api_token: "your-api-token-here"
|
vault_cloudflare_api_token: "your-cloudflare-token"
|
||||||
|
|
||||||
|
# Database passwords
|
||||||
|
vault_postgresql_password: "secure-postgres-password"
|
||||||
|
vault_valkey_password: "secure-valkey-password"
|
||||||
|
|
||||||
|
# Authentik authentication secrets
|
||||||
|
vault_authentik_secret_key: "base64-encoded-secret-key"
|
||||||
|
vault_authentik_admin_password: "secure-admin-password"
|
||||||
|
vault_authentik_db_password: "authentik-database-password"
|
||||||
|
|
||||||
|
# Application secrets
|
||||||
|
vault_gitea_secret_key: "gitea-secret-key"
|
||||||
|
vault_gitea_jwt_secret: "gitea-jwt-secret"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Generate Vault Secrets
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate secure passwords and keys
|
||||||
|
openssl rand -base64 32 # For secret keys
|
||||||
|
openssl rand -base64 20 # For passwords
|
||||||
|
|
||||||
|
# Encrypt vault file
|
||||||
|
ansible-vault encrypt host_vars/arch-vps/vault.yml
|
||||||
|
|
||||||
|
# Edit vault file
|
||||||
|
ansible-vault edit host_vars/arch-vps/vault.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Security
|
## Monitoring and Verification
|
||||||
|
|
||||||
- Always use vault for sensitive data
|
### Service Health Checks
|
||||||
- Test deployments on staging first
|
|
||||||
- Monitor logs after deployment
|
|
||||||
- Verify HTTPS certificates are working
|
|
||||||
- Check that API is only accessible locally
|
|
||||||
|
|
||||||
### Monitoring
|
```bash
|
||||||
|
# Infrastructure services
|
||||||
|
ssh root@your-vps "systemctl status postgresql valkey caddy podman"
|
||||||
|
|
||||||
- Monitor Caddy logs: `journalctl -u caddy -f`
|
# Authentication services
|
||||||
- Check API status: `curl http://localhost:2019/config/`
|
ssh root@your-vps "systemctl --user -M authentik@ status authentik-pod"
|
||||||
- Verify service health: `curl https://domain.com/health`
|
|
||||||
- Monitor certificate expiration
|
# Application services
|
||||||
|
ssh root@your-vps "systemctl --user -M gitea@ status gitea"
|
||||||
|
|
||||||
|
# Web service accessibility
|
||||||
|
curl -I https://auth.jnss.me/
|
||||||
|
curl -I https://git.jnss.me/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Infrastructure logs
|
||||||
|
ssh root@your-vps "journalctl -u caddy -f"
|
||||||
|
ssh root@your-vps "journalctl -u postgresql -f"
|
||||||
|
ssh root@your-vps "journalctl -u valkey -f"
|
||||||
|
|
||||||
|
# Authentication logs
|
||||||
|
ssh root@your-vps "journalctl --user -M authentik@ -u authentik-server -f"
|
||||||
|
|
||||||
|
# Application logs
|
||||||
|
ssh root@your-vps "journalctl --user -M gitea@ -u gitea -f"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container resource usage
|
||||||
|
ssh root@your-vps "sudo -u authentik podman stats"
|
||||||
|
ssh root@your-vps "sudo -u gitea podman stats"
|
||||||
|
|
||||||
|
# Database performance
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'SELECT * FROM pg_stat_activity;'"
|
||||||
|
|
||||||
|
# System resources
|
||||||
|
ssh root@your-vps "htop"
|
||||||
|
ssh root@your-vps "df -h"
|
||||||
|
ssh root@your-vps "free -h"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Best Practices
|
||||||
|
|
||||||
|
### Deployment Security
|
||||||
|
|
||||||
|
- **Always use vault**: Never commit secrets to version control
|
||||||
|
- **Verify connectivity**: Test Ansible connection before deployment
|
||||||
|
- **Monitor logs**: Watch deployment logs for errors or warnings
|
||||||
|
- **Validate services**: Verify all services start correctly after deployment
|
||||||
|
- **Check certificates**: Ensure HTTPS certificates are issued correctly
|
||||||
|
|
||||||
|
### Operational Security
|
||||||
|
|
||||||
|
- **Regular updates**: Keep system packages and container images updated
|
||||||
|
- **Access monitoring**: Monitor authentication logs for suspicious activity
|
||||||
|
- **Backup procedures**: Regular backups of databases and configurations
|
||||||
|
- **Certificate monitoring**: Monitor TLS certificate expiration
|
||||||
|
- **Security scans**: Regular security assessments of deployed services
|
||||||
|
|
||||||
|
### Network Security
|
||||||
|
|
||||||
|
- **Firewall verification**: Ensure only ports 80/443 are exposed externally
|
||||||
|
- **Unix socket usage**: Verify database/cache communications use Unix sockets
|
||||||
|
- **TLS encryption**: Confirm all external communications use HTTPS
|
||||||
|
- **Access logs**: Monitor Caddy access logs for unusual patterns
|
||||||
|
|
||||||
|
## Troubleshooting Common Issues
|
||||||
|
|
||||||
|
### Deployment Failures
|
||||||
|
|
||||||
|
#### Issue: Ansible vault decryption fails
|
||||||
|
```bash
|
||||||
|
# Solution: Verify vault password and file encryption
|
||||||
|
ansible-vault view host_vars/arch-vps/vault.yml
|
||||||
|
ansible-vault decrypt host_vars/arch-vps/vault.yml # Edit, then re-encrypt
|
||||||
|
ansible-vault encrypt host_vars/arch-vps/vault.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Issue: SSH connection failures
|
||||||
|
```bash
|
||||||
|
# Solution: Verify SSH configuration and keys
|
||||||
|
ssh-add -l # List SSH keys
|
||||||
|
ssh root@your-vps # Test direct connection
|
||||||
|
ansible arch-vps -m ping # Test Ansible connectivity
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Issue: Container services fail to start
|
||||||
|
```bash
|
||||||
|
# Check container user sessions
|
||||||
|
ssh root@your-vps "loginctl list-users"
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ status"
|
||||||
|
|
||||||
|
# Verify container images are available
|
||||||
|
ssh root@your-vps "sudo -u authentik podman images"
|
||||||
|
|
||||||
|
# Check container logs
|
||||||
|
ssh root@your-vps "sudo -u authentik podman logs authentik-server"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Connectivity Issues
|
||||||
|
|
||||||
|
#### Issue: Database connection failures
|
||||||
|
```bash
|
||||||
|
# Verify PostgreSQL socket exists and is accessible
|
||||||
|
ssh root@your-vps "ls -la /var/run/postgresql/"
|
||||||
|
ssh root@your-vps "sudo -u authentik psql -h /var/run/postgresql -U authentik"
|
||||||
|
|
||||||
|
# Check PostgreSQL service status
|
||||||
|
ssh root@your-vps "systemctl status postgresql"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Issue: Authentication not working
|
||||||
|
```bash
|
||||||
|
# Check authentik service status
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ status authentik-pod authentik-server"
|
||||||
|
|
||||||
|
# Test authentik HTTP endpoint
|
||||||
|
ssh root@your-vps "curl -I http://127.0.0.1:9000/"
|
||||||
|
|
||||||
|
# Check Caddy forward auth configuration
|
||||||
|
ssh root@your-vps "caddy validate --config /etc/caddy/Caddyfile"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Issue: HTTPS certificate problems
|
||||||
|
```bash
|
||||||
|
# Check certificate status
|
||||||
|
curl -vI https://auth.jnss.me/ 2>&1 | grep -E "(certificate|expire)"
|
||||||
|
|
||||||
|
# Check Caddy certificate management
|
||||||
|
ssh root@your-vps "journalctl -u caddy | grep -i cert"
|
||||||
|
|
||||||
|
# Verify DNS records
|
||||||
|
dig +short auth.jnss.me
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Issues
|
||||||
|
|
||||||
|
#### Issue: Slow database queries
|
||||||
|
```bash
|
||||||
|
# Monitor active connections
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'SELECT * FROM pg_stat_activity;'"
|
||||||
|
|
||||||
|
# Check database performance
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'SELECT * FROM pg_stat_database;'"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Issue: High memory usage
|
||||||
|
```bash
|
||||||
|
# Check system memory
|
||||||
|
ssh root@your-vps "free -h"
|
||||||
|
|
||||||
|
# Check container memory usage
|
||||||
|
ssh root@your-vps "sudo -u authentik podman stats"
|
||||||
|
|
||||||
|
# Check service memory limits
|
||||||
|
ssh root@your-vps "systemctl --user -M authentik@ show authentik-server --property=MemoryCurrent"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Maintenance Procedures
|
||||||
|
|
||||||
|
### Regular Maintenance Tasks
|
||||||
|
|
||||||
|
#### System Updates
|
||||||
|
```bash
|
||||||
|
# Update system packages
|
||||||
|
ansible arch-vps -m pacman -a "update_cache=yes upgrade=yes" --become
|
||||||
|
|
||||||
|
# Update container images
|
||||||
|
ansible-playbook site.yml --tags containers,image-pull --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Certificate Renewal
|
||||||
|
```bash
|
||||||
|
# Certificates renew automatically, but monitor logs
|
||||||
|
ssh root@your-vps "journalctl -u caddy | grep -i 'certificate\|renewal'"
|
||||||
|
|
||||||
|
# Force certificate renewal if needed
|
||||||
|
ssh root@your-vps "systemctl reload caddy"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Database Maintenance
|
||||||
|
```bash
|
||||||
|
# PostgreSQL maintenance
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'VACUUM ANALYZE;'"
|
||||||
|
|
||||||
|
# Database backups
|
||||||
|
ssh root@your-vps "sudo -u postgres pg_dumpall -h /var/run/postgresql > /backup/postgres-backup-\$(date +%Y%m%d).sql"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup Procedures
|
||||||
|
|
||||||
|
#### Configuration Backup
|
||||||
|
```bash
|
||||||
|
# Backup Ansible configurations (run from control machine)
|
||||||
|
tar -czf rick-infra-backup-$(date +%Y%m%d).tar.gz \
|
||||||
|
inventory/ host_vars/ roles/ docs/ *.yml
|
||||||
|
|
||||||
|
# Backup vault files separately and securely
|
||||||
|
ansible-vault view host_vars/arch-vps/vault.yml > secure-backup/vault-$(date +%Y%m%d).yml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Data Backup
|
||||||
|
```bash
|
||||||
|
# Database backup
|
||||||
|
ssh root@your-vps "sudo -u postgres pg_dump -h /var/run/postgresql authentik > /backup/authentik-$(date +%Y%m%d).sql"
|
||||||
|
ssh root@your-vps "sudo -u postgres pg_dump -h /var/run/postgresql gitea > /backup/gitea-$(date +%Y%m%d).sql"
|
||||||
|
|
||||||
|
# Application data backup
|
||||||
|
ssh root@your-vps "tar -czf /backup/authentik-media-$(date +%Y%m%d).tar.gz -C /opt/authentik media"
|
||||||
|
ssh root@your-vps "tar -czf /backup/gitea-data-$(date +%Y%m%d).tar.gz -C /opt/gitea data"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation References
|
||||||
|
|
||||||
|
### Core Documentation
|
||||||
|
- **[Setup Guide](setup-guide.md)** - Initial VPS and Ansible setup
|
||||||
|
- **[Authentik Deployment Guide](authentik-deployment-guide.md)** - Detailed authentik deployment
|
||||||
|
- **[Architecture Decisions](architecture-decisions.md)** - Technical decision rationale
|
||||||
|
- **[Security Hardening](security-hardening.md)** - Security configuration details
|
||||||
|
|
||||||
|
### Service Integration
|
||||||
|
- **[Service Integration Guide](service-integration-guide.md)** - Adding new services
|
||||||
|
- **[Caddy Configuration](caddy-service-configuration.md)** - Reverse proxy patterns
|
||||||
|
|
||||||
|
### Role Documentation
|
||||||
|
- **[Authentik Role](../roles/authentik/README.md)** - Authentication service details
|
||||||
|
- **[PostgreSQL Role](../roles/postgresql/README.md)** - Database service details
|
||||||
|
- **[Valkey Role](../roles/valkey/README.md)** - Cache service details
|
||||||
|
- **[Caddy Role](../roles/caddy/README.md)** - Reverse proxy details
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This comprehensive deployment guide provides the foundation for deploying and maintaining the complete rick-infra stack with emphasis on security, performance, and operational excellence through native database services and Unix socket IPC architecture.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,930 @@
|
|||||||
# Securing the VPS
|
# Security Hardening Guide
|
||||||
## Network Security
|
|
||||||
- **SSH Hardening**: Password authentication disabled, root login disabled, key-only authentication
|
|
||||||
- **Firewall Configuration**: UFW with deny-all incoming, allow-all outgoing defaults
|
|
||||||
- **Fail2ban**: SSH brute-force protection with configurable ban times
|
|
||||||
- **Kernel Network Hardening**: IP forwarding disabled, source routing blocked, ICMP redirects disabled
|
|
||||||
|
|
||||||
|
Comprehensive security hardening for rick-infra, implementing defense-in-depth with network, container, database, and application security layers.
|
||||||
|
|
||||||
|
## Security Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ rick-infra Security Architecture (Defense in Depth) │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Layer 1: Network Security │ │
|
||||||
|
│ │ • SSH Hardening • Firewall • Fail2ban • TLS/HTTPS │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Layer 2: Authentication & Authorization │ │
|
||||||
|
│ │ • Authentik SSO • MFA • Forward Auth • Policy Engine │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Layer 3: Container Security │ │
|
||||||
|
│ │ • Rootless Podman • User Namespaces • Capabilities │ │
|
||||||
|
│ │ • SELinux • Resource Limits • Security Labels │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Layer 4: Database & Cache Security │ │
|
||||||
|
│ │ • Unix Sockets Only • No Network Exposure • Group ACLs │ │
|
||||||
|
│ │ • Encrypted Storage • Backup Security • Access Logging │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Layer 5: Application Security │ │
|
||||||
|
│ │ • Secret Management • Secure Headers • Audit Logging │ │
|
||||||
|
│ │ • Input Validation • Session Security • CSRF Protection │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Layer 1: Network Security
|
||||||
|
|
||||||
|
### SSH Hardening
|
||||||
|
|
||||||
|
#### Configuration Applied by Security Role
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# SSH hardening settings
|
||||||
|
sshd_config:
|
||||||
|
# Authentication Security
|
||||||
|
PasswordAuthentication: no
|
||||||
|
ChallengeResponseAuthentication: no
|
||||||
|
PermitRootLogin: no
|
||||||
|
PubkeyAuthentication: yes
|
||||||
|
AuthenticationMethods: "publickey"
|
||||||
|
|
||||||
|
# Protocol Security
|
||||||
|
Protocol: 2
|
||||||
|
PermitEmptyPasswords: no
|
||||||
|
MaxAuthTries: 3
|
||||||
|
LoginGraceTime: 60
|
||||||
|
|
||||||
|
# Connection Security
|
||||||
|
X11Forwarding: no
|
||||||
|
AllowAgentForwarding: no
|
||||||
|
AllowTcpForwarding: no
|
||||||
|
PermitTunnel: no
|
||||||
|
|
||||||
|
# Session Security
|
||||||
|
ClientAliveInterval: 300
|
||||||
|
ClientAliveCountMax: 2
|
||||||
|
MaxSessions: 2
|
||||||
|
MaxStartups: "2:30:10"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verification Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify SSH hardening
|
||||||
|
ssh root@your-vps "sshd -T | grep -E '(passwordauth|rootlogin|pubkey)'"
|
||||||
|
|
||||||
|
# Test SSH security
|
||||||
|
ssh-audit your-vps # External tool for SSH security assessment
|
||||||
|
|
||||||
|
# Monitor SSH attempts
|
||||||
|
ssh root@your-vps "journalctl -u sshd | grep -i 'failed\|invalid'"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Firewall Configuration
|
||||||
|
|
||||||
|
#### nftables Firewall Rules
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deployed firewall configuration
|
||||||
|
table inet filter {
|
||||||
|
chain input {
|
||||||
|
type filter hook input priority 0; policy drop;
|
||||||
|
|
||||||
|
# Allow loopback traffic
|
||||||
|
iifname "lo" accept
|
||||||
|
|
||||||
|
# Allow established connections
|
||||||
|
ct state established,related accept
|
||||||
|
|
||||||
|
# Allow SSH (rate limited)
|
||||||
|
tcp dport 22 ct state new limit rate 5/minute accept
|
||||||
|
|
||||||
|
# Allow HTTP/HTTPS
|
||||||
|
tcp dport {80, 443} accept
|
||||||
|
|
||||||
|
# Allow ICMP (rate limited)
|
||||||
|
icmp type echo-request limit rate 1/second accept
|
||||||
|
|
||||||
|
# Log dropped packets
|
||||||
|
log prefix "DROPPED: " drop
|
||||||
|
}
|
||||||
|
|
||||||
|
chain forward {
|
||||||
|
type filter hook forward priority 0; policy drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
chain output {
|
||||||
|
type filter hook output priority 0; policy accept;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Firewall Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check firewall status
|
||||||
|
ssh root@your-vps "nft list ruleset"
|
||||||
|
|
||||||
|
# Monitor dropped connections
|
||||||
|
ssh root@your-vps "journalctl -k | grep DROPPED"
|
||||||
|
|
||||||
|
# Temporary rule addition (emergency access)
|
||||||
|
ssh root@your-vps "nft add rule inet filter input tcp dport 8080 accept"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Intrusion Detection (fail2ban)
|
||||||
|
|
||||||
|
#### fail2ban Configuration
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# SSH protection jail
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = ssh
|
||||||
|
filter = sshd
|
||||||
|
logpath = /var/log/auth.log
|
||||||
|
maxretry = 3
|
||||||
|
findtime = 600
|
||||||
|
bantime = 3600
|
||||||
|
backend = systemd
|
||||||
|
|
||||||
|
# HTTP brute force protection
|
||||||
|
[caddy-auth]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = caddy-auth
|
||||||
|
logpath = /var/log/caddy/access.log
|
||||||
|
maxretry = 10
|
||||||
|
findtime = 600
|
||||||
|
bantime = 1800
|
||||||
|
|
||||||
|
# Authentik protection
|
||||||
|
[authentik-auth]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = authentik-auth
|
||||||
|
logpath = /opt/authentik/logs/server.log
|
||||||
|
maxretry = 5
|
||||||
|
findtime = 300
|
||||||
|
bantime = 3600
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Intrusion Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check fail2ban status
|
||||||
|
ssh root@your-vps "fail2ban-client status"
|
||||||
|
ssh root@your-vps "fail2ban-client status sshd"
|
||||||
|
|
||||||
|
# View banned IPs
|
||||||
|
ssh root@your-vps "fail2ban-client get sshd banned"
|
||||||
|
|
||||||
|
# Unban IP (if needed)
|
||||||
|
ssh root@your-vps "fail2ban-client set sshd unbanip 203.0.113.100"
|
||||||
|
```
|
||||||
|
|
||||||
|
### TLS/HTTPS Security
|
||||||
|
|
||||||
|
#### Caddy TLS Configuration
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# Automatic HTTPS with security headers
|
||||||
|
(security_headers) {
|
||||||
|
header {
|
||||||
|
# HSTS (HTTP Strict Transport Security)
|
||||||
|
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||||
|
|
||||||
|
# Content Security Policy
|
||||||
|
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
|
||||||
|
|
||||||
|
# Additional security headers
|
||||||
|
X-Content-Type-Options "nosniff"
|
||||||
|
X-Frame-Options "DENY"
|
||||||
|
X-XSS-Protection "1; mode=block"
|
||||||
|
Referrer-Policy "strict-origin-when-cross-origin"
|
||||||
|
|
||||||
|
# Remove server information
|
||||||
|
-Server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply to all sites
|
||||||
|
*.jnss.me {
|
||||||
|
import security_headers
|
||||||
|
tls {
|
||||||
|
protocols tls1.2 tls1.3
|
||||||
|
curves x25519 secp384r1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### TLS Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check certificate status
|
||||||
|
curl -vI https://auth.jnss.me/ 2>&1 | grep -E "(certificate|expire|protocol)"
|
||||||
|
|
||||||
|
# Test TLS configuration
|
||||||
|
nmap --script ssl-enum-ciphers -p 443 your-domain.com
|
||||||
|
|
||||||
|
# Monitor certificate renewal
|
||||||
|
ssh root@your-vps "journalctl -u caddy | grep -i cert"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Layer 2: Authentication & Authorization Security
|
||||||
|
|
||||||
|
### Authentik Security Configuration
|
||||||
|
|
||||||
|
#### Password Policy
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Authentik password policy
|
||||||
|
password_policy:
|
||||||
|
length_min: 12
|
||||||
|
amount_lowercase: 1
|
||||||
|
amount_uppercase: 1
|
||||||
|
amount_digits: 1
|
||||||
|
amount_symbols: 1
|
||||||
|
password_field: "password"
|
||||||
|
check_static_rules: true
|
||||||
|
check_have_i_been_pwned: true
|
||||||
|
check_zxcvbn: true
|
||||||
|
zxcvbn_score_threshold: 3
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Multi-Factor Authentication
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# MFA enforcement policies
|
||||||
|
mfa_policies:
|
||||||
|
- name: "Admin MFA Required"
|
||||||
|
bound_to: "group:admins"
|
||||||
|
authenticators: ["totp", "webauthn"]
|
||||||
|
enforce: true
|
||||||
|
|
||||||
|
- name: "Sensitive Services MFA"
|
||||||
|
bound_to: "application:admin_*"
|
||||||
|
authenticators: ["totp"]
|
||||||
|
enforce: true
|
||||||
|
|
||||||
|
- name: "External Access MFA"
|
||||||
|
bound_to: "source_ip:!10.0.0.0/8"
|
||||||
|
authenticators: ["totp", "sms"]
|
||||||
|
enforce: true
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Session Security
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Session security settings
|
||||||
|
session_security:
|
||||||
|
cookie_secure: true
|
||||||
|
cookie_httponly: true
|
||||||
|
cookie_samesite: "Strict"
|
||||||
|
session_timeout: 3600 # 1 hour
|
||||||
|
remember_me_timeout: 86400 # 24 hours
|
||||||
|
concurrent_sessions_limit: 3
|
||||||
|
session_fixation_protection: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Access Control Policies
|
||||||
|
|
||||||
|
#### Time-Based Access Control
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Business hours access policy
|
||||||
|
policies:
|
||||||
|
- name: "Business Hours Only"
|
||||||
|
type: "time"
|
||||||
|
parameters:
|
||||||
|
start_time: "08:00"
|
||||||
|
end_time: "18:00"
|
||||||
|
days: ["monday", "tuesday", "wednesday", "thursday", "friday"]
|
||||||
|
timezone: "UTC"
|
||||||
|
applications: ["admin_dashboard", "financial_systems"]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### IP-Based Access Control
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Geographic/Network access restrictions
|
||||||
|
policies:
|
||||||
|
- name: "Office Network Only"
|
||||||
|
type: "source_ip"
|
||||||
|
parameters:
|
||||||
|
cidr: ["10.0.0.0/8", "192.168.0.0/16"]
|
||||||
|
applications: ["admin_*", "backup_*"]
|
||||||
|
|
||||||
|
- name: "Block High-Risk Countries"
|
||||||
|
type: "geoip"
|
||||||
|
parameters:
|
||||||
|
countries: ["CN", "RU", "KP"]
|
||||||
|
action: "deny"
|
||||||
|
applications: ["*"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Layer 3: Container Security
|
||||||
|
|
||||||
|
### Rootless Container Architecture
|
||||||
|
|
||||||
|
#### Security Benefits of Rootless Podman
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rootless container verification
|
||||||
|
ssh root@your-vps "sudo -u authentik podman info | grep -A10 'security'"
|
||||||
|
|
||||||
|
# User namespace mapping
|
||||||
|
ssh root@your-vps "sudo -u authentik cat /proc/self/uid_map"
|
||||||
|
ssh root@your-vps "sudo -u authentik cat /proc/self/gid_map"
|
||||||
|
|
||||||
|
# Process tree verification (containers run as unprivileged user)
|
||||||
|
ssh root@your-vps "ps aux | grep -E '(authentik|gitea)'"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Container User Security
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Container configuration with security
|
||||||
|
[Container]
|
||||||
|
# Run as specific UID/GID (not root)
|
||||||
|
User=963:963
|
||||||
|
|
||||||
|
# Preserve supplementary groups for socket access
|
||||||
|
Annotation=run.oci.keep_original_groups=1
|
||||||
|
|
||||||
|
# Minimal capabilities
|
||||||
|
AddCapability=IPC_OWNER
|
||||||
|
DropCapability=ALL
|
||||||
|
|
||||||
|
# Security labels
|
||||||
|
SecurityLabelDisable=true
|
||||||
|
SecurityLabelType=container_t
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Isolation
|
||||||
|
|
||||||
|
#### Filesystem Security
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Read-only root filesystem where possible
|
||||||
|
security_settings:
|
||||||
|
read_only_root_fs: true
|
||||||
|
temp_fs_mounts:
|
||||||
|
- "/tmp:rw,noexec,nosuid,size=100M"
|
||||||
|
- "/var/tmp:rw,noexec,nosuid,size=100M"
|
||||||
|
|
||||||
|
# Minimal volume mounts
|
||||||
|
volume_mounts:
|
||||||
|
- "/opt/authentik/data:/data:Z" # SELinux label
|
||||||
|
- "/opt/authentik/media:/media:Z"
|
||||||
|
- "/var/run/postgresql:/var/run/postgresql:Z"
|
||||||
|
- "/var/run/valkey:/var/run/valkey:Z"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Resource Limits
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# systemd resource controls
|
||||||
|
[Service]
|
||||||
|
# Memory limits
|
||||||
|
MemoryMax=1G
|
||||||
|
MemorySwapMax=0
|
||||||
|
|
||||||
|
# CPU limits
|
||||||
|
CPUQuota=200% # 2 CPU cores max
|
||||||
|
|
||||||
|
# Process limits
|
||||||
|
TasksMax=100
|
||||||
|
LimitNOFILE=1024
|
||||||
|
|
||||||
|
# I/O limits
|
||||||
|
IODeviceWriteBandwidthMax=/dev/sda 50M
|
||||||
|
```
|
||||||
|
|
||||||
|
### SELinux Security
|
||||||
|
|
||||||
|
#### Container Labeling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify SELinux labels on container volumes
|
||||||
|
ssh root@your-vps "ls -Z /var/run/postgresql/"
|
||||||
|
ssh root@your-vps "ls -Z /opt/authentik/"
|
||||||
|
|
||||||
|
# Check container SELinux context
|
||||||
|
ssh root@your-vps "sudo -u authentik podman exec authentik-server cat /proc/self/attr/current"
|
||||||
|
|
||||||
|
# SELinux audit logs
|
||||||
|
ssh root@your-vps "ausearch -m AVC -ts recent"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom SELinux Policies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate custom SELinux policy for containers
|
||||||
|
ssh root@your-vps "audit2allow -a -m rick_containers"
|
||||||
|
|
||||||
|
# Apply custom policy
|
||||||
|
ssh root@your-vps "semodule -i rick_containers.pp"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Layer 4: Database & Cache Security
|
||||||
|
|
||||||
|
### Unix Socket Security Model
|
||||||
|
|
||||||
|
#### Socket File Permissions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify socket permissions
|
||||||
|
ssh root@your-vps "ls -la /var/run/postgresql/"
|
||||||
|
# Should show: srwxrwx--- postgres postgres .s.PGSQL.5432
|
||||||
|
|
||||||
|
ssh root@your-vps "ls -la /var/run/valkey/"
|
||||||
|
# Should show: srwxrwx--- valkey valkey valkey.sock
|
||||||
|
|
||||||
|
# Verify group access
|
||||||
|
ssh root@your-vps "groups authentik"
|
||||||
|
# Should include: postgres valkey
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Network Isolation
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Database configuration (no network exposure)
|
||||||
|
postgresql_config:
|
||||||
|
listen_addresses: '' # Unix sockets only
|
||||||
|
port: 0 # Disable TCP port
|
||||||
|
unix_socket_directories: '/var/run/postgresql'
|
||||||
|
unix_socket_permissions: 0770
|
||||||
|
|
||||||
|
valkey_config:
|
||||||
|
bind: '' # No network binding
|
||||||
|
port: 0 # Disable TCP port
|
||||||
|
unixsocket: '/var/run/valkey/valkey.sock'
|
||||||
|
unixsocketperm: 770
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Security Hardening
|
||||||
|
|
||||||
|
#### PostgreSQL Security
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Database security configuration
|
||||||
|
-- Remove public schema permissions
|
||||||
|
REVOKE ALL ON SCHEMA public FROM PUBLIC;
|
||||||
|
|
||||||
|
-- Create application-specific schemas
|
||||||
|
CREATE SCHEMA authentik AUTHORIZATION authentik;
|
||||||
|
CREATE SCHEMA gitea AUTHORIZATION gitea;
|
||||||
|
|
||||||
|
-- Row-level security (if needed)
|
||||||
|
ALTER TABLE authentik.sensitive_data ENABLE ROW LEVEL SECURITY;
|
||||||
|
CREATE POLICY user_data_policy ON authentik.sensitive_data
|
||||||
|
FOR ALL TO authentik USING (user_id = current_setting('app.user_id'));
|
||||||
|
|
||||||
|
-- Audit logging
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgaudit;
|
||||||
|
ALTER SYSTEM SET pgaudit.log = 'all';
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Database Access Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Monitor database connections
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'SELECT datname, usename, application_name, client_addr FROM pg_stat_activity;'"
|
||||||
|
|
||||||
|
# Check for suspicious queries
|
||||||
|
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'SELECT query, query_start, state FROM pg_stat_activity WHERE state != '\''idle'\'';'"
|
||||||
|
|
||||||
|
# Review authentication logs
|
||||||
|
ssh root@your-vps "grep -i 'authentication\|connection' /var/log/postgresql/postgresql.log"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cache Security
|
||||||
|
|
||||||
|
#### Valkey/Redis Security Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Valkey security settings
|
||||||
|
CONFIG SET requirepass "strong_password"
|
||||||
|
CONFIG SET rename-command FLUSHDB ""
|
||||||
|
CONFIG SET rename-command FLUSHALL ""
|
||||||
|
CONFIG SET rename-command DEBUG ""
|
||||||
|
CONFIG SET rename-command CONFIG "CONFIG_b3a0f6c8"
|
||||||
|
|
||||||
|
# Disable dangerous commands
|
||||||
|
CONFIG SET protected-mode yes
|
||||||
|
CONFIG SET save "" # Disable automatic snapshots
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cache Access Control
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify cache authentication
|
||||||
|
ssh root@your-vps "redis-cli -s /var/run/valkey/valkey.sock -a \$VALKEY_PASSWORD ping"
|
||||||
|
|
||||||
|
# Monitor cache operations
|
||||||
|
ssh root@your-vps "redis-cli -s /var/run/valkey/valkey.sock -a \$VALKEY_PASSWORD monitor"
|
||||||
|
|
||||||
|
# Check cache configuration
|
||||||
|
ssh root@your-vps "redis-cli -s /var/run/valkey/valkey.sock -a \$VALKEY_PASSWORD CONFIG GET '*'"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Layer 5: Application Security
|
||||||
|
|
||||||
|
### Secret Management
|
||||||
|
|
||||||
|
#### Ansible Vault Security
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Vault encryption verification
|
||||||
|
vault_security:
|
||||||
|
encryption_method: "AES256"
|
||||||
|
key_derivation: "PBKDF2"
|
||||||
|
vault_password_source: "prompt" # Never store in files
|
||||||
|
|
||||||
|
# Vault best practices
|
||||||
|
vault_practices:
|
||||||
|
- rotate_vault_password: "quarterly"
|
||||||
|
- separate_vault_files: "by_environment"
|
||||||
|
- backup_encrypted_vaults: "securely"
|
||||||
|
- audit_vault_access: "regularly"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Environment Variable Security
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify secrets are not in process environment
|
||||||
|
ssh root@your-vps "sudo -u authentik cat /proc/\$(pgrep -f authentik-server)/environ | tr '\0' '\n' | grep -v SECRET"
|
||||||
|
|
||||||
|
# Check for secrets in logs
|
||||||
|
ssh root@your-vps "journalctl --user -M authentik@ -u authentik-server | grep -i 'password\|secret\|token'"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Secure Headers and Middleware
|
||||||
|
|
||||||
|
#### Application-Level Security Headers
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Example application security middleware
|
||||||
|
security_middleware = {
|
||||||
|
'SECURE_BROWSER_XSS_FILTER': True,
|
||||||
|
'SECURE_CONTENT_TYPE_NOSNIFF': True,
|
||||||
|
'SECURE_HSTS_SECONDS': 31536000,
|
||||||
|
'SECURE_HSTS_INCLUDE_SUBDOMAINS': True,
|
||||||
|
'SECURE_HSTS_PRELOAD': True,
|
||||||
|
'SECURE_REDIRECT_EXEMPT': [],
|
||||||
|
'SECURE_SSL_REDIRECT': True,
|
||||||
|
'SESSION_COOKIE_SECURE': True,
|
||||||
|
'SESSION_COOKIE_HTTPONLY': True,
|
||||||
|
'SESSION_COOKIE_SAMESITE': 'Strict',
|
||||||
|
'CSRF_COOKIE_SECURE': True,
|
||||||
|
'X_FRAME_OPTIONS': 'DENY'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Content Security Policy
|
||||||
|
|
||||||
|
```http
|
||||||
|
# Strict CSP for web applications
|
||||||
|
Content-Security-Policy: default-src 'self';
|
||||||
|
script-src 'self' 'unsafe-inline' https://cdn.example.com;
|
||||||
|
style-src 'self' 'unsafe-inline';
|
||||||
|
img-src 'self' data: https:;
|
||||||
|
font-src 'self' https://fonts.gstatic.com;
|
||||||
|
connect-src 'self' https://api.example.com;
|
||||||
|
frame-ancestors 'none';
|
||||||
|
base-uri 'self';
|
||||||
|
form-action 'self'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Audit Logging and Monitoring
|
||||||
|
|
||||||
|
#### System-Wide Audit Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# auditd configuration for security events
|
||||||
|
auditctl -w /etc/passwd -p wa -k user_modification
|
||||||
|
auditctl -w /etc/shadow -p wa -k password_modification
|
||||||
|
auditctl -w /opt/authentik -p wa -k authentik_access
|
||||||
|
auditctl -w /var/run/postgresql -p wa -k database_access
|
||||||
|
auditctl -w /var/run/valkey -p wa -k cache_access
|
||||||
|
|
||||||
|
# Monitor systemd service changes
|
||||||
|
auditctl -w /etc/systemd/system -p wa -k service_modification
|
||||||
|
auditctl -w /home/*/.*config/containers/systemd -p wa -k container_modification
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Application Audit Logging
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Application-level audit events
|
||||||
|
audit_events:
|
||||||
|
authentication:
|
||||||
|
- user_login_success
|
||||||
|
- user_login_failure
|
||||||
|
- password_change
|
||||||
|
- mfa_enrollment
|
||||||
|
- session_timeout
|
||||||
|
|
||||||
|
authorization:
|
||||||
|
- access_granted
|
||||||
|
- access_denied
|
||||||
|
- privilege_escalation
|
||||||
|
- policy_violation
|
||||||
|
|
||||||
|
data_access:
|
||||||
|
- sensitive_data_access
|
||||||
|
- data_export
|
||||||
|
- configuration_change
|
||||||
|
- backup_access
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security Monitoring and Alerting
|
||||||
|
|
||||||
|
#### Real-Time Security Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Security monitoring scripts
|
||||||
|
#!/bin/bash
|
||||||
|
# security-monitor.sh
|
||||||
|
|
||||||
|
# Monitor failed authentication attempts
|
||||||
|
FAILED_LOGINS=$(grep "Failed password" /var/log/auth.log | wc -l)
|
||||||
|
if [ $FAILED_LOGINS -gt 10 ]; then
|
||||||
|
echo "ALERT: High number of failed login attempts: $FAILED_LOGINS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Monitor unusual database connections
|
||||||
|
DB_CONNECTIONS=$(sudo -u postgres psql -h /var/run/postgresql -t -c "SELECT count(*) FROM pg_stat_activity WHERE application_name NOT IN ('authentik', 'gitea');")
|
||||||
|
if [ $DB_CONNECTIONS -gt 5 ]; then
|
||||||
|
echo "ALERT: Unusual database connections: $DB_CONNECTIONS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Monitor container security violations
|
||||||
|
SELINUX_VIOLATIONS=$(ausearch -m AVC -ts recent 2>/dev/null | wc -l)
|
||||||
|
if [ $SELINUX_VIOLATIONS -gt 0 ]; then
|
||||||
|
echo "ALERT: SELinux violations detected: $SELINUX_VIOLATIONS"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Log Aggregation and Analysis
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Log analysis configuration
|
||||||
|
log_analysis:
|
||||||
|
sources:
|
||||||
|
- /var/log/auth.log # SSH and system authentication
|
||||||
|
- /var/log/audit/audit.log # System audit events
|
||||||
|
- /opt/authentik/logs/ # Application logs
|
||||||
|
- /var/log/caddy/ # Web server logs
|
||||||
|
- /var/log/postgresql/ # Database logs
|
||||||
|
|
||||||
|
alerts:
|
||||||
|
- pattern: "Failed password.*from.*"
|
||||||
|
threshold: 5
|
||||||
|
timeframe: "5m"
|
||||||
|
action: "block_ip"
|
||||||
|
|
||||||
|
- pattern: "AUTHENTICATION_FAILED.*authentik"
|
||||||
|
threshold: 10
|
||||||
|
timeframe: "10m"
|
||||||
|
action: "alert_admin"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Best Practices
|
||||||
|
|
||||||
|
### Regular Security Maintenance
|
||||||
|
|
||||||
|
#### Security Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Automated security updates (weekly)
|
||||||
|
#!/bin/bash
|
||||||
|
# security-updates.sh
|
||||||
|
|
||||||
|
# System package updates
|
||||||
|
pacman -Syu --noconfirm
|
||||||
|
|
||||||
|
# Container image updates
|
||||||
|
for user in authentik gitea; do
|
||||||
|
sudo -u $user podman auto-update
|
||||||
|
done
|
||||||
|
|
||||||
|
# Certificate renewal check
|
||||||
|
systemctl reload caddy
|
||||||
|
|
||||||
|
# Restart services if security updates require it
|
||||||
|
if [ -f /var/run/reboot-required ]; then
|
||||||
|
systemctl reboot
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Security Auditing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Monthly security audit checklist
|
||||||
|
#!/bin/bash
|
||||||
|
# security-audit.sh
|
||||||
|
|
||||||
|
echo "=== Security Audit Report $(date) ==="
|
||||||
|
|
||||||
|
# Check for unauthorized users
|
||||||
|
echo "Checking system users:"
|
||||||
|
awk -F: '$3 >= 1000 {print $1, $3}' /etc/passwd
|
||||||
|
|
||||||
|
# Check sudo access
|
||||||
|
echo "Checking sudo access:"
|
||||||
|
grep -v '^#' /etc/sudoers
|
||||||
|
|
||||||
|
# Check listening services
|
||||||
|
echo "Checking listening services:"
|
||||||
|
netstat -tulpn | grep LISTEN
|
||||||
|
|
||||||
|
# Check file permissions on critical files
|
||||||
|
echo "Checking critical file permissions:"
|
||||||
|
ls -la /etc/shadow /etc/passwd /etc/ssh/sshd_config
|
||||||
|
|
||||||
|
# Check for SUID/SGID files
|
||||||
|
echo "Checking SUID/SGID files:"
|
||||||
|
find /usr -perm /6000 -type f 2>/dev/null
|
||||||
|
|
||||||
|
# Check container security
|
||||||
|
echo "Checking container security:"
|
||||||
|
for user in authentik gitea; do
|
||||||
|
echo "User: $user"
|
||||||
|
sudo -u $user podman ps --format "table {{.Names}}\t{{.RunningFor}}\t{{.Status}}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check database security
|
||||||
|
echo "Checking database connections:"
|
||||||
|
sudo -u postgres psql -h /var/run/postgresql -c "SELECT datname, usename, client_addr FROM pg_stat_activity;"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Incident Response Procedures
|
||||||
|
|
||||||
|
#### Security Incident Response Plan
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
incident_response:
|
||||||
|
detection:
|
||||||
|
- automated_alerts: "fail2ban, audit logs, monitoring"
|
||||||
|
- manual_review: "daily log review, weekly security audit"
|
||||||
|
|
||||||
|
assessment:
|
||||||
|
- severity_levels: ["low", "medium", "high", "critical"]
|
||||||
|
- response_time:
|
||||||
|
critical: "15 minutes"
|
||||||
|
high: "1 hour"
|
||||||
|
medium: "4 hours"
|
||||||
|
low: "24 hours"
|
||||||
|
|
||||||
|
containment:
|
||||||
|
- isolate_affected_systems: "fail2ban, iptables blocks"
|
||||||
|
- preserve_evidence: "log snapshots, memory dumps"
|
||||||
|
- maintain_operations: "failover procedures"
|
||||||
|
|
||||||
|
eradication:
|
||||||
|
- remove_threats: "malware removal, account cleanup"
|
||||||
|
- patch_vulnerabilities: "system updates, configuration fixes"
|
||||||
|
- strengthen_defenses: "additional monitoring, access controls"
|
||||||
|
|
||||||
|
recovery:
|
||||||
|
- restore_services: "service restart, data restoration"
|
||||||
|
- monitor_operations: "enhanced monitoring period"
|
||||||
|
- validate_security: "penetration testing, vulnerability scans"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Emergency Response Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Emergency security response toolkit
|
||||||
|
#!/bin/bash
|
||||||
|
# emergency-response.sh
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
"isolate")
|
||||||
|
# Emergency network isolation
|
||||||
|
iptables -I INPUT -j DROP
|
||||||
|
iptables -I OUTPUT -j DROP
|
||||||
|
echo "System isolated from network"
|
||||||
|
;;
|
||||||
|
|
||||||
|
"lockdown")
|
||||||
|
# Lock all user accounts except emergency admin
|
||||||
|
for user in $(cat /etc/passwd | cut -d: -f1); do
|
||||||
|
if [ "$user" != "root" ] && [ "$user" != "emergency" ]; then
|
||||||
|
usermod -L $user
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "All user accounts locked"
|
||||||
|
;;
|
||||||
|
|
||||||
|
"audit")
|
||||||
|
# Emergency audit collection
|
||||||
|
mkdir -p /tmp/emergency-audit
|
||||||
|
cp -r /var/log/* /tmp/emergency-audit/
|
||||||
|
journalctl --no-pager > /tmp/emergency-audit/journalctl.log
|
||||||
|
netstat -tulpn > /tmp/emergency-audit/network.log
|
||||||
|
ps auxf > /tmp/emergency-audit/processes.log
|
||||||
|
echo "Audit data collected in /tmp/emergency-audit/"
|
||||||
|
;;
|
||||||
|
|
||||||
|
"restore")
|
||||||
|
# Restore from isolation
|
||||||
|
iptables -F
|
||||||
|
iptables -P INPUT ACCEPT
|
||||||
|
iptables -P OUTPUT ACCEPT
|
||||||
|
echo "Network access restored - apply proper firewall rules"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compliance and Standards
|
||||||
|
|
||||||
|
### Security Standards Alignment
|
||||||
|
|
||||||
|
#### CIS (Center for Internet Security) Benchmarks
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
cis_compliance:
|
||||||
|
level_1_controls:
|
||||||
|
- filesystem_configuration: "implemented"
|
||||||
|
- software_updates: "automated"
|
||||||
|
- secure_boot_settings: "implemented"
|
||||||
|
- additional_process_hardening: "implemented"
|
||||||
|
- mandatory_access_controls: "selinux_enforcing"
|
||||||
|
|
||||||
|
level_2_controls:
|
||||||
|
- address_space_layout_randomization: "enabled"
|
||||||
|
- prelink_disabled: "implemented"
|
||||||
|
- activate_gpg_check: "implemented"
|
||||||
|
- disable_automounting: "implemented"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### NIST Cybersecurity Framework
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nist_framework:
|
||||||
|
identify:
|
||||||
|
- asset_management: "complete inventory of systems and services"
|
||||||
|
- risk_assessment: "quarterly risk assessments"
|
||||||
|
|
||||||
|
protect:
|
||||||
|
- access_control: "authentik SSO with MFA"
|
||||||
|
- awareness_training: "security documentation and procedures"
|
||||||
|
- data_security: "encryption at rest and in transit"
|
||||||
|
|
||||||
|
detect:
|
||||||
|
- anomalies_events: "automated monitoring and alerting"
|
||||||
|
- security_monitoring: "continuous monitoring of security events"
|
||||||
|
|
||||||
|
respond:
|
||||||
|
- response_planning: "documented incident response procedures"
|
||||||
|
- communications: "stakeholder notification procedures"
|
||||||
|
|
||||||
|
recover:
|
||||||
|
- recovery_planning: "documented recovery procedures"
|
||||||
|
- improvements: "lessons learned and security enhancements"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documentation and Training
|
||||||
|
|
||||||
|
#### Security Documentation
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
required_documentation:
|
||||||
|
policies:
|
||||||
|
- information_security_policy
|
||||||
|
- access_control_policy
|
||||||
|
- incident_response_policy
|
||||||
|
- backup_recovery_policy
|
||||||
|
|
||||||
|
procedures:
|
||||||
|
- user_onboarding_offboarding
|
||||||
|
- security_incident_response
|
||||||
|
- vulnerability_management
|
||||||
|
- change_management
|
||||||
|
|
||||||
|
technical_guides:
|
||||||
|
- system_hardening_guide
|
||||||
|
- application_security_guide
|
||||||
|
- network_security_guide
|
||||||
|
- monitoring_alerting_guide
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This comprehensive security hardening guide implements defense-in-depth security across all layers of the rick-infra stack, emphasizing the security benefits of native database services, Unix socket IPC, rootless containers, and centralized authentication.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- **[Authentication Architecture](authentication-architecture.md)** - Detailed authentication and authorization
|
||||||
|
- **[Architecture Decisions](architecture-decisions.md)** - Security-focused architectural choices
|
||||||
|
- **[Authentik Deployment Guide](authentik-deployment-guide.md)** - Secure authentication deployment
|
||||||
|
- **[Service Integration Guide](service-integration-guide.md)** - Secure service integration patterns
|
||||||
|
|||||||
@@ -301,6 +301,499 @@ If you get permission denied errors:
|
|||||||
- Check service logs for connection errors
|
- Check service logs for connection errors
|
||||||
- Verify group memberships after user changes
|
- Verify group memberships after user changes
|
||||||
|
|
||||||
|
## Authentication Integration with Authentik
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
All services in rick-infra should integrate with Authentik for centralized authentication and authorization. This section covers the authentication integration patterns available.
|
||||||
|
|
||||||
|
### Authentication Integration Patterns
|
||||||
|
|
||||||
|
#### Pattern 1: Forward Authentication (Recommended)
|
||||||
|
|
||||||
|
**Use Case**: HTTP services that don't need to handle authentication internally
|
||||||
|
|
||||||
|
**Benefits**:
|
||||||
|
- No application code changes required
|
||||||
|
- Consistent authentication across all services
|
||||||
|
- Centralized session management
|
||||||
|
- Service receives user identity via HTTP headers
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Service role task to deploy Caddy configuration
|
||||||
|
- name: Deploy service Caddy configuration with Authentik forward auth
|
||||||
|
template:
|
||||||
|
src: myservice.caddy.j2
|
||||||
|
dest: "{{ caddy_sites_enabled_dir }}/myservice.caddy"
|
||||||
|
owner: root
|
||||||
|
group: "{{ caddy_user }}"
|
||||||
|
mode: '0644'
|
||||||
|
backup: true
|
||||||
|
notify: reload caddy
|
||||||
|
tags: [caddy, auth]
|
||||||
|
```
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# templates/myservice.caddy.j2
|
||||||
|
{{ service_domain }} {
|
||||||
|
# Forward authentication to Authentik
|
||||||
|
forward_auth https://auth.jnss.me {
|
||||||
|
uri /outpost.goauthentik.io/auth/caddy
|
||||||
|
copy_headers Remote-User Remote-Name Remote-Email Remote-Groups
|
||||||
|
}
|
||||||
|
|
||||||
|
# Your service backend
|
||||||
|
reverse_proxy {{ service_backend }}
|
||||||
|
|
||||||
|
# Optional: Restrict access by group
|
||||||
|
@not_authorized {
|
||||||
|
not header Remote-Groups "*{{ required_group }}*"
|
||||||
|
}
|
||||||
|
respond @not_authorized "Access denied: insufficient privileges" 403
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Service Code Example** (Python Flask):
|
||||||
|
```python
|
||||||
|
# Application receives authentication information via headers
|
||||||
|
from flask import Flask, request
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.route('/dashboard')
|
||||||
|
def dashboard():
|
||||||
|
# Extract user information from headers (provided by 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 not username:
|
||||||
|
return "Authentication required", 401
|
||||||
|
|
||||||
|
if 'service_users' not in user_groups:
|
||||||
|
return "Access denied: insufficient privileges", 403
|
||||||
|
|
||||||
|
return render_template('dashboard.html',
|
||||||
|
username=username,
|
||||||
|
name=user_name,
|
||||||
|
groups=user_groups)
|
||||||
|
|
||||||
|
@app.route('/admin')
|
||||||
|
def admin():
|
||||||
|
user_groups = request.headers.get('Remote-Groups', '').split(',')
|
||||||
|
|
||||||
|
if 'admins' not in user_groups:
|
||||||
|
return "Admin access required", 403
|
||||||
|
|
||||||
|
return render_template('admin.html')
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pattern 2: OAuth2/OIDC Integration
|
||||||
|
|
||||||
|
**Use Case**: Applications that can implement OAuth2 client functionality
|
||||||
|
|
||||||
|
**Benefits**:
|
||||||
|
- Fine-grained scope control
|
||||||
|
- API access tokens
|
||||||
|
- Better integration with application user models
|
||||||
|
- Support for mobile/SPA applications
|
||||||
|
|
||||||
|
**Service Configuration**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Service environment configuration
|
||||||
|
oauth2_config:
|
||||||
|
client_id: "{{ service_oauth_client_id }}"
|
||||||
|
client_secret: "{{ vault_service_oauth_secret }}"
|
||||||
|
discovery_url: "https://auth.jnss.me/application/o/{{ service_slug }}/.well-known/openid_configuration"
|
||||||
|
scopes: "openid email profile groups"
|
||||||
|
redirect_uri: "https://{{ service_domain }}/oauth/callback"
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# OAuth2 integration example (Python)
|
||||||
|
from authlib.integrations.flask_client import OAuth
|
||||||
|
|
||||||
|
oauth = OAuth(app)
|
||||||
|
oauth.register(
|
||||||
|
'authentik',
|
||||||
|
client_id=app.config['OAUTH_CLIENT_ID'],
|
||||||
|
client_secret=app.config['OAUTH_CLIENT_SECRET'],
|
||||||
|
server_metadata_url=app.config['OAUTH_DISCOVERY_URL'],
|
||||||
|
client_kwargs={
|
||||||
|
'scope': 'openid email profile groups'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.route('/login')
|
||||||
|
def login():
|
||||||
|
redirect_uri = url_for('oauth_callback', _external=True)
|
||||||
|
return oauth.authentik.authorize_redirect(redirect_uri)
|
||||||
|
|
||||||
|
@app.route('/oauth/callback')
|
||||||
|
def oauth_callback():
|
||||||
|
token = oauth.authentik.authorize_access_token()
|
||||||
|
user_info = oauth.authentik.parse_id_token(token)
|
||||||
|
|
||||||
|
# Store user information in session
|
||||||
|
session['user'] = {
|
||||||
|
'id': user_info['sub'],
|
||||||
|
'username': user_info['preferred_username'],
|
||||||
|
'email': user_info['email'],
|
||||||
|
'name': user_info['name'],
|
||||||
|
'groups': user_info.get('groups', [])
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect('/dashboard')
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pattern 3: API-Only Authentication
|
||||||
|
|
||||||
|
**Use Case**: REST APIs, microservices, machine-to-machine communication
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# API service with token validation
|
||||||
|
import requests
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
|
||||||
|
def validate_token(token):
|
||||||
|
"""Validate Bearer token with Authentik introspection endpoint"""
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
'https://auth.jnss.me/application/o/introspect/',
|
||||||
|
headers={
|
||||||
|
'Authorization': f'Bearer {app.config["API_CLIENT_TOKEN"]}'
|
||||||
|
},
|
||||||
|
data={'token': token},
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
return response.json()
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(f"Token validation error: {e}")
|
||||||
|
return 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[7:] # Remove 'Bearer ' prefix
|
||||||
|
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()
|
||||||
|
|
||||||
|
# Check required scope
|
||||||
|
if 'api:read' not in scope:
|
||||||
|
return jsonify({'error': 'Insufficient permissions'}), 403
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'message': f'Hello {username}',
|
||||||
|
'data': 'Your API response data here'
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authentik Provider Configuration
|
||||||
|
|
||||||
|
For each service integration, configure the appropriate provider in Authentik:
|
||||||
|
|
||||||
|
#### Forward Auth Provider (Pattern 1)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Authentik provider configuration (via admin interface)
|
||||||
|
provider_config:
|
||||||
|
name: "{{ service_name }} Forward Auth"
|
||||||
|
type: "Proxy Provider"
|
||||||
|
authorization_flow: "default-provider-authorization-implicit-consent"
|
||||||
|
external_host: "https://{{ service_domain }}"
|
||||||
|
internal_host: "http://localhost:{{ service_port }}"
|
||||||
|
skip_path_regex: "^/(health|metrics|static).*"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### OAuth2 Provider (Pattern 2)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Authentik OAuth2 provider configuration
|
||||||
|
oauth_provider_config:
|
||||||
|
name: "{{ service_name }} OAuth2"
|
||||||
|
type: "OAuth2/OpenID Provider"
|
||||||
|
authorization_flow: "default-provider-authorization-explicit-consent"
|
||||||
|
client_type: "confidential"
|
||||||
|
client_id: "{{ service_oauth_client_id }}"
|
||||||
|
redirect_uris:
|
||||||
|
- "https://{{ service_domain }}/oauth/callback"
|
||||||
|
post_logout_redirect_uris:
|
||||||
|
- "https://{{ service_domain }}/"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### API Provider (Pattern 3)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Authentik API provider configuration
|
||||||
|
api_provider_config:
|
||||||
|
name: "{{ service_name }} API"
|
||||||
|
type: "OAuth2/OpenID Provider"
|
||||||
|
authorization_flow: "default-provider-authorization-implicit-consent"
|
||||||
|
client_type: "confidential"
|
||||||
|
client_id: "{{ service_api_client_id }}"
|
||||||
|
include_claims_in_id_token: true
|
||||||
|
issuer_mode: "per_provider"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Group-Based Authorization
|
||||||
|
|
||||||
|
#### Service-Specific Groups
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Create service-specific groups in Authentik
|
||||||
|
service_groups:
|
||||||
|
- name: "{{ service_name }}_users"
|
||||||
|
description: "Users who can access {{ service_name }}"
|
||||||
|
is_superuser: false
|
||||||
|
|
||||||
|
- name: "{{ service_name }}_admins"
|
||||||
|
description: "Administrators for {{ service_name }}"
|
||||||
|
is_superuser: false
|
||||||
|
parent: "{{ service_name }}_users"
|
||||||
|
|
||||||
|
- name: "{{ service_name }}_readonly"
|
||||||
|
description: "Read-only access to {{ service_name }}"
|
||||||
|
is_superuser: false
|
||||||
|
parent: "{{ service_name }}_users"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Policy-Based Access Control
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Authentik policies for service access
|
||||||
|
service_policies:
|
||||||
|
- name: "{{ service_name }} Group Access"
|
||||||
|
policy_type: "Group Membership Policy"
|
||||||
|
groups: ["{{ service_name }}_users"]
|
||||||
|
|
||||||
|
- name: "{{ service_name }} Business Hours"
|
||||||
|
policy_type: "Time-based Policy"
|
||||||
|
parameters:
|
||||||
|
start_time: "08:00"
|
||||||
|
end_time: "18:00"
|
||||||
|
days: ["monday", "tuesday", "wednesday", "thursday", "friday"]
|
||||||
|
|
||||||
|
- name: "{{ service_name }} IP Restriction"
|
||||||
|
policy_type: "Source IP Policy"
|
||||||
|
parameters:
|
||||||
|
cidr: "10.0.0.0/8"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Role Template with Authentication
|
||||||
|
|
||||||
|
Here's a complete service role template that includes authentication integration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# roles/myservice/defaults/main.yml
|
||||||
|
---
|
||||||
|
# Service configuration
|
||||||
|
service_name: "myservice"
|
||||||
|
service_domain: "myservice.jnss.me"
|
||||||
|
service_port: 8080
|
||||||
|
service_backend: "localhost:{{ service_port }}"
|
||||||
|
|
||||||
|
# Authentication configuration
|
||||||
|
auth_enabled: true
|
||||||
|
auth_pattern: "forward_auth" # forward_auth, oauth2, api_only
|
||||||
|
required_group: "myservice_users"
|
||||||
|
|
||||||
|
# OAuth2 configuration (if auth_pattern is oauth2)
|
||||||
|
oauth_client_id: "{{ service_name }}-oauth-client"
|
||||||
|
oauth_client_secret: "{{ vault_myservice_oauth_secret }}"
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
postgresql_db_name: "{{ service_name }}"
|
||||||
|
valkey_db_number: 2
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# roles/myservice/tasks/main.yml
|
||||||
|
---
|
||||||
|
- name: Create service user and setup
|
||||||
|
include_tasks: user.yml
|
||||||
|
tags: [user, setup]
|
||||||
|
|
||||||
|
- name: Setup database access
|
||||||
|
include_tasks: database.yml
|
||||||
|
tags: [database, setup]
|
||||||
|
|
||||||
|
- name: Setup cache access
|
||||||
|
include_tasks: cache.yml
|
||||||
|
tags: [cache, setup]
|
||||||
|
|
||||||
|
- name: Deploy service configuration
|
||||||
|
template:
|
||||||
|
src: myservice.env.j2
|
||||||
|
dest: "{{ service_home }}/.env"
|
||||||
|
owner: "{{ service_user }}"
|
||||||
|
group: "{{ service_group }}"
|
||||||
|
mode: '0600'
|
||||||
|
tags: [config]
|
||||||
|
|
||||||
|
- name: Deploy container configuration
|
||||||
|
template:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ service_quadlet_dir }}/{{ item.dest }}"
|
||||||
|
owner: "{{ service_user }}"
|
||||||
|
group: "{{ service_group }}"
|
||||||
|
mode: '0644'
|
||||||
|
loop:
|
||||||
|
- { src: 'myservice.pod', dest: 'myservice.pod' }
|
||||||
|
- { src: 'myservice.container', dest: 'myservice.container' }
|
||||||
|
become: true
|
||||||
|
become_user: "{{ service_user }}"
|
||||||
|
notify:
|
||||||
|
- reload systemd user
|
||||||
|
- restart myservice
|
||||||
|
tags: [containers]
|
||||||
|
|
||||||
|
- name: Deploy Caddy configuration with authentication
|
||||||
|
template:
|
||||||
|
src: myservice.caddy.j2
|
||||||
|
dest: "{{ caddy_sites_enabled_dir }}/{{ service_name }}.caddy"
|
||||||
|
owner: root
|
||||||
|
group: "{{ caddy_user }}"
|
||||||
|
mode: '0644'
|
||||||
|
backup: true
|
||||||
|
notify: reload caddy
|
||||||
|
tags: [caddy, auth]
|
||||||
|
when: auth_enabled
|
||||||
|
|
||||||
|
- name: Start and enable service
|
||||||
|
systemd:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
scope: user
|
||||||
|
daemon_reload: true
|
||||||
|
become: true
|
||||||
|
become_user: "{{ service_user }}"
|
||||||
|
tags: [service]
|
||||||
|
```
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# roles/myservice/templates/myservice.caddy.j2
|
||||||
|
{{ service_domain }} {
|
||||||
|
{% if auth_enabled and auth_pattern == 'forward_auth' %}
|
||||||
|
# Forward authentication to Authentik
|
||||||
|
forward_auth https://auth.jnss.me {
|
||||||
|
uri /outpost.goauthentik.io/auth/caddy
|
||||||
|
copy_headers Remote-User Remote-Name Remote-Email Remote-Groups
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if required_group %}
|
||||||
|
# Restrict access to specific group
|
||||||
|
@not_authorized {
|
||||||
|
not header Remote-Groups "*{{ required_group }}*"
|
||||||
|
}
|
||||||
|
respond @not_authorized "Access denied: {{ required_group }} group required" 403
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Service backend
|
||||||
|
reverse_proxy {{ service_backend }}
|
||||||
|
|
||||||
|
# Health check endpoint (no auth required)
|
||||||
|
handle /health {
|
||||||
|
reverse_proxy {{ service_backend }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Testing
|
||||||
|
|
||||||
|
#### Authentication Integration Tests
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Service authentication tests
|
||||||
|
authentication_tests:
|
||||||
|
- name: "Test unauthenticated access denied"
|
||||||
|
uri: "https://{{ service_domain }}/"
|
||||||
|
method: "GET"
|
||||||
|
expected_status: [302, 401] # Redirect to login or unauthorized
|
||||||
|
|
||||||
|
- name: "Test authenticated access allowed"
|
||||||
|
uri: "https://{{ service_domain }}/"
|
||||||
|
method: "GET"
|
||||||
|
headers:
|
||||||
|
Cookie: "authentik_session={{ valid_session_cookie }}"
|
||||||
|
expected_status: [200]
|
||||||
|
|
||||||
|
- name: "Test group authorization"
|
||||||
|
uri: "https://{{ service_domain }}/admin"
|
||||||
|
method: "GET"
|
||||||
|
headers:
|
||||||
|
Cookie: "authentik_session={{ admin_session_cookie }}"
|
||||||
|
expected_status: [200]
|
||||||
|
|
||||||
|
- name: "Test insufficient privileges"
|
||||||
|
uri: "https://{{ service_domain }}/admin"
|
||||||
|
method: "GET"
|
||||||
|
headers:
|
||||||
|
Cookie: "authentik_session={{ user_session_cookie }}"
|
||||||
|
expected_status: [403]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Automated Testing Script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# test-service-auth.sh
|
||||||
|
|
||||||
|
SERVICE_DOMAIN="myservice.jnss.me"
|
||||||
|
|
||||||
|
echo "Testing service authentication for $SERVICE_DOMAIN"
|
||||||
|
|
||||||
|
# Test 1: Unauthenticated access should redirect or deny
|
||||||
|
echo "Test 1: Unauthenticated access"
|
||||||
|
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "https://$SERVICE_DOMAIN/")
|
||||||
|
if [[ "$RESPONSE" == "302" || "$RESPONSE" == "401" ]]; then
|
||||||
|
echo "✓ PASS: Unauthenticated access properly denied ($RESPONSE)"
|
||||||
|
else
|
||||||
|
echo "✗ FAIL: Unauthenticated access allowed ($RESPONSE)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 2: Health endpoint should be accessible
|
||||||
|
echo "Test 2: Health endpoint access"
|
||||||
|
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "https://$SERVICE_DOMAIN/health")
|
||||||
|
if [[ "$RESPONSE" == "200" ]]; then
|
||||||
|
echo "✓ PASS: Health endpoint accessible"
|
||||||
|
else
|
||||||
|
echo "✗ FAIL: Health endpoint not accessible ($RESPONSE)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 3: Authentik forward auth endpoint exists
|
||||||
|
echo "Test 3: Authentik forward auth endpoint"
|
||||||
|
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "https://auth.jnss.me/outpost.goauthentik.io/auth/caddy")
|
||||||
|
if [[ "$RESPONSE" == "401" ]]; then
|
||||||
|
echo "✓ PASS: Forward auth endpoint responding"
|
||||||
|
else
|
||||||
|
echo "✗ FAIL: Forward auth endpoint not responding correctly ($RESPONSE)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Authentication tests completed"
|
||||||
|
```
|
||||||
|
|
||||||
## Example Integration
|
## Example Integration
|
||||||
|
|
||||||
See the `authentik` role for a complete example of this pattern:
|
See the `authentik` role for a complete example of this pattern:
|
||||||
@@ -309,4 +802,39 @@ See the `authentik` role for a complete example of this pattern:
|
|||||||
- **Tasks**: `roles/authentik/tasks/`
|
- **Tasks**: `roles/authentik/tasks/`
|
||||||
- **Documentation**: `roles/authentik/README.md`
|
- **Documentation**: `roles/authentik/README.md`
|
||||||
|
|
||||||
This provides a working reference implementation for Unix socket integration.
|
This provides a working reference implementation for Unix socket integration.
|
||||||
|
|
||||||
|
## Authentication Integration Examples
|
||||||
|
|
||||||
|
For practical authentication integration examples, see:
|
||||||
|
|
||||||
|
- **[Authentication Architecture](authentication-architecture.md)** - Complete authentication patterns and examples
|
||||||
|
- **[Authentik Deployment Guide](authentik-deployment-guide.md)** - Authentik-specific configuration
|
||||||
|
- **[Architecture Decisions](architecture-decisions.md)** - Authentication model rationale
|
||||||
|
|
||||||
|
## Quick Start Checklist
|
||||||
|
|
||||||
|
When integrating a new service with rick-infra:
|
||||||
|
|
||||||
|
### Infrastructure Integration
|
||||||
|
- [ ] Create dedicated system user for the service
|
||||||
|
- [ ] Add user to `postgres` and `valkey` groups for database access
|
||||||
|
- [ ] Configure Unix socket connections in service environment
|
||||||
|
- [ ] Set up Quadlet container configuration with proper user/group settings
|
||||||
|
- [ ] Test database and cache connectivity
|
||||||
|
|
||||||
|
### Authentication Integration
|
||||||
|
- [ ] Choose authentication pattern (forward auth recommended for most services)
|
||||||
|
- [ ] Create Caddy configuration with Authentik forward auth
|
||||||
|
- [ ] Create Authentik provider and application configuration
|
||||||
|
- [ ] Set up service-specific groups and policies in Authentik
|
||||||
|
- [ ] Test authentication flow and authorization
|
||||||
|
|
||||||
|
### Deployment Integration
|
||||||
|
- [ ] Add service role dependencies in `meta/main.yml`
|
||||||
|
- [ ] Configure service in `site.yml` with appropriate tags
|
||||||
|
- [ ] Set up vault variables for secrets
|
||||||
|
- [ ] Deploy and verify service functionality
|
||||||
|
- [ ] Add monitoring and backup procedures
|
||||||
|
|
||||||
|
This comprehensive integration approach ensures all services benefit from the security, performance, and operational advantages of the rick-infra architecture.
|
||||||
@@ -303,3 +303,22 @@ This solution is:
|
|||||||
- ✅ **Secure**: Maintains container isolation where it matters
|
- ✅ **Secure**: Maintains container isolation where it matters
|
||||||
- ✅ **Standard**: Uses documented Podman/OCI features
|
- ✅ **Standard**: Uses documented Podman/OCI features
|
||||||
|
|
||||||
|
## Documentation References
|
||||||
|
|
||||||
|
### Comprehensive Guides
|
||||||
|
- **[Authentik Deployment Guide](../../docs/authentik-deployment-guide.md)** - Complete step-by-step deployment instructions
|
||||||
|
- **[Authentication Architecture](../../docs/authentication-architecture.md)** - High-level authentication strategy and integration patterns
|
||||||
|
- **[Architecture Decisions](../../docs/architecture-decisions.md)** - Technical decision rationale and trade-offs
|
||||||
|
- **[Security Hardening](../../docs/security-hardening.md)** - Multi-layer security implementation
|
||||||
|
|
||||||
|
### Integration Resources
|
||||||
|
- **[Service Integration Guide](../../docs/service-integration-guide.md)** - How to integrate new services with authentik
|
||||||
|
- **[Deployment Guide](../../docs/deployment-guide.md)** - Infrastructure deployment procedures
|
||||||
|
- **[Caddy Configuration](../../docs/caddy-service-configuration.md)** - Reverse proxy and forward auth setup
|
||||||
|
|
||||||
|
### Quick Links
|
||||||
|
- **Deployment**: For complete deployment procedures, see [Authentik Deployment Guide](../../docs/authentik-deployment-guide.md#step-by-step-deployment)
|
||||||
|
- **Troubleshooting**: For comprehensive troubleshooting, see [Authentik Deployment Guide](../../docs/authentik-deployment-guide.md#troubleshooting-guide)
|
||||||
|
- **Security Model**: For security architecture details, see [Architecture Decisions](../../docs/architecture-decisions.md#adr-004-forward-authentication-security-model)
|
||||||
|
- **Service Integration**: For adding new services, see [Service Integration Guide](../../docs/service-integration-guide.md#authentication-integration-with-authentik)
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ authentik_valkey_db: 1 # Use database 1 for Authentik
|
|||||||
|
|
||||||
authentik_domain: "auth.jnss.me"
|
authentik_domain: "auth.jnss.me"
|
||||||
authentik_http_port: 9000
|
authentik_http_port: 9000
|
||||||
authentik_bind_address: "127.0.0.1"
|
authentik_bind_address: "0.0.0.0"
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# Authentik Core Configuration
|
# Authentik Core Configuration
|
||||||
@@ -89,6 +89,17 @@ authentik_user_quadlet_dir: "{{ authentik_home }}/.config/containers/systemd"
|
|||||||
# User session variables (set dynamically during deployment)
|
# User session variables (set dynamically during deployment)
|
||||||
authentik_uid: ""
|
authentik_uid: ""
|
||||||
|
|
||||||
|
# =================================================================
|
||||||
|
# User Namespace Configuration
|
||||||
|
# =================================================================
|
||||||
|
|
||||||
|
# Subuid/subgid ranges for authentik user containers
|
||||||
|
# Range: 200000-265535 (65536 IDs)
|
||||||
|
authentik_subuid_start: 200000
|
||||||
|
authentik_subuid_size: 65536
|
||||||
|
authentik_subgid_start: 200000
|
||||||
|
authentik_subgid_size: 65536
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# Caddy Integration
|
# Caddy Integration
|
||||||
# =================================================================
|
# =================================================================
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
---
|
|
||||||
# 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"
|
|
||||||
@@ -2,55 +2,9 @@
|
|||||||
# Authentik Authentication Role - Main Tasks
|
# Authentik Authentication Role - Main Tasks
|
||||||
# Self-contained deployment with Podman and Unix sockets
|
# Self-contained deployment with Podman and Unix sockets
|
||||||
|
|
||||||
- name: Create authentik group
|
- name: Setup authentik user and container namespaces
|
||||||
group:
|
include_tasks: user.yml
|
||||||
name: "{{ authentik_group }}"
|
tags: [user, setup]
|
||||||
system: true
|
|
||||||
|
|
||||||
- name: Create authentik system user
|
|
||||||
user:
|
|
||||||
name: "{{ authentik_user }}"
|
|
||||||
system: true
|
|
||||||
shell: /bin/bash
|
|
||||||
home: "{{ authentik_home }}"
|
|
||||||
create_home: true
|
|
||||||
group: "{{ authentik_group }}"
|
|
||||||
|
|
||||||
- name: Create authentik directories
|
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ authentik_user }}"
|
|
||||||
group: "{{ authentik_group }}"
|
|
||||||
mode: '0755'
|
|
||||||
loop:
|
|
||||||
- "{{ authentik_home }}"
|
|
||||||
- "{{ authentik_data_dir }}"
|
|
||||||
- "{{ authentik_media_dir }}"
|
|
||||||
- "{{ authentik_user_quadlet_dir }}"
|
|
||||||
- "{{ authentik_log_dir }}"
|
|
||||||
|
|
||||||
|
|
||||||
- name: Enable lingering for authentik user (services persist without login)
|
|
||||||
command: loginctl enable-linger {{ authentik_user }}
|
|
||||||
register: linger_result
|
|
||||||
changed_when: linger_result.rc == 0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- name: Get authentik user UID and GID for container configuration
|
|
||||||
shell: |
|
|
||||||
echo "uid=$(id -u {{ authentik_user }})"
|
|
||||||
echo "gid=$(id -g {{ authentik_user }})"
|
|
||||||
register: authentik_user_info
|
|
||||||
changed_when: false
|
|
||||||
tags: [setup]
|
|
||||||
|
|
||||||
- name: Set authentik UID/GID facts for container templates
|
|
||||||
set_fact:
|
|
||||||
authentik_uid: "{{ authentik_user_info.stdout_lines[0] | regex_replace('uid=', '') }}"
|
|
||||||
authentik_gid: "{{ authentik_user_info.stdout_lines[1] | regex_replace('gid=', '') }}"
|
|
||||||
tags: [setup]
|
|
||||||
|
|
||||||
- name: Setup database access and permissions
|
- name: Setup database access and permissions
|
||||||
include_tasks: database.yml
|
include_tasks: database.yml
|
||||||
@@ -60,6 +14,26 @@
|
|||||||
include_tasks: cache.yml
|
include_tasks: cache.yml
|
||||||
tags: [cache, setup]
|
tags: [cache, setup]
|
||||||
|
|
||||||
|
- name: Pull authentik container image
|
||||||
|
containers.podman.podman_image:
|
||||||
|
name: "{{ authentik_image }}:{{ authentik_version }}"
|
||||||
|
state: present
|
||||||
|
become: true
|
||||||
|
become_user: "{{ authentik_user }}"
|
||||||
|
tags: [containers, image-pull]
|
||||||
|
|
||||||
|
- name: Create media directory structure
|
||||||
|
file:
|
||||||
|
path: "{{ authentik_media_dir }}/{{ item }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ authentik_user }}"
|
||||||
|
group: "{{ authentik_group }}"
|
||||||
|
mode: '0755'
|
||||||
|
loop:
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
tags: [setup, media]
|
||||||
|
|
||||||
- name: Deploy environment configuration
|
- name: Deploy environment configuration
|
||||||
template:
|
template:
|
||||||
src: authentik.env.j2
|
src: authentik.env.j2
|
||||||
@@ -154,13 +128,12 @@
|
|||||||
|
|
||||||
- name: Wait for Authentik to be ready
|
- name: Wait for Authentik to be ready
|
||||||
uri:
|
uri:
|
||||||
url: "https://{{ authentik_domain }}/if/health/live/"
|
url: "http://127.0.0.1:{{ authentik_http_port }}/"
|
||||||
method: GET
|
method: GET
|
||||||
status_code: [200]
|
status_code: [200, 302]
|
||||||
timeout: 30
|
timeout: 30
|
||||||
validate_certs: true
|
|
||||||
retries: 10
|
retries: 10
|
||||||
delay: 30
|
delay: 15
|
||||||
register: authentik_health_check
|
register: authentik_health_check
|
||||||
tags: [verification, health-check]
|
tags: [verification, health-check]
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
# 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)
|
|
||||||
@@ -4,30 +4,30 @@
|
|||||||
- name: Create authentik group
|
- name: Create authentik group
|
||||||
group:
|
group:
|
||||||
name: "{{ authentik_group }}"
|
name: "{{ authentik_group }}"
|
||||||
system: yes
|
system: true
|
||||||
|
|
||||||
- name: Create authentik user
|
- name: Create authentik user
|
||||||
user:
|
user:
|
||||||
name: "{{ authentik_user }}"
|
name: "{{ authentik_user }}"
|
||||||
group: "{{ authentik_group }}"
|
group: "{{ authentik_group }}"
|
||||||
system: yes
|
system: true
|
||||||
shell: /bin/bash
|
shell: /bin/bash
|
||||||
home: "{{ authentik_home }}"
|
home: "{{ authentik_home }}"
|
||||||
create_home: yes
|
create_home: true
|
||||||
comment: "Authentik authentication service"
|
comment: "Authentik authentication service"
|
||||||
|
|
||||||
- name: Set up subuid for authentik user
|
- name: Set up subuid for authentik user
|
||||||
lineinfile:
|
lineinfile:
|
||||||
path: /etc/subuid
|
path: /etc/subuid
|
||||||
line: "{{ authentik_user }}:{{ authentik_subuid_start }}:{{ authentik_subuid_size }}"
|
line: "{{ authentik_user }}:{{ authentik_subuid_start }}:{{ authentik_subuid_size }}"
|
||||||
create: yes
|
create: true
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
|
|
||||||
- name: Set up subgid for authentik user
|
- name: Set up subgid for authentik user
|
||||||
lineinfile:
|
lineinfile:
|
||||||
path: /etc/subgid
|
path: /etc/subgid
|
||||||
line: "{{ authentik_user }}:{{ authentik_subgid_start }}:{{ authentik_subgid_size }}"
|
line: "{{ authentik_user }}:{{ authentik_subgid_start }}:{{ authentik_subgid_size }}"
|
||||||
create: yes
|
create: true
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
|
|
||||||
- name: Create authentik directories
|
- name: Create authentik directories
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
- "{{ authentik_home }}/.config/containers/systemd"
|
- "{{ authentik_home }}/.config/containers/systemd"
|
||||||
- "{{ authentik_home }}/data"
|
- "{{ authentik_home }}/data"
|
||||||
- "{{ authentik_home }}/media"
|
- "{{ authentik_home }}/media"
|
||||||
|
- "{{ authentik_home }}/logs"
|
||||||
|
|
||||||
- name: Enable lingering for authentik user
|
- name: Enable lingering for authentik user
|
||||||
command: loginctl enable-linger {{ authentik_user }}
|
command: loginctl enable-linger {{ authentik_user }}
|
||||||
@@ -54,7 +55,19 @@
|
|||||||
|
|
||||||
- name: Initialize user systemd for authentik
|
- name: Initialize user systemd for authentik
|
||||||
systemd:
|
systemd:
|
||||||
daemon_reload: yes
|
daemon_reload: true
|
||||||
scope: user
|
scope: user
|
||||||
become: yes
|
become: true
|
||||||
become_user: "{{ authentik_user }}"
|
become_user: "{{ authentik_user }}"
|
||||||
|
|
||||||
|
- name: Get authentik user UID and GID for container configuration
|
||||||
|
shell: |
|
||||||
|
echo "uid=$(id -u {{ authentik_user }})"
|
||||||
|
echo "gid=$(id -g {{ authentik_user }})"
|
||||||
|
register: authentik_user_info
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set authentik UID/GID facts for container templates
|
||||||
|
set_fact:
|
||||||
|
authentik_uid: "{{ authentik_user_info.stdout_lines[0] | regex_replace('uid=', '') }}"
|
||||||
|
authentik_gid: "{{ authentik_user_info.stdout_lines[1] | regex_replace('gid=', '') }}"
|
||||||
@@ -11,6 +11,11 @@ EnvironmentFile={{ authentik_home }}/.env
|
|||||||
User={{ authentik_uid }}:{{ authentik_gid }}
|
User={{ authentik_uid }}:{{ authentik_gid }}
|
||||||
Annotation=run.oci.keep_original_groups=1
|
Annotation=run.oci.keep_original_groups=1
|
||||||
|
|
||||||
|
# Security configuration for shared memory and IPC
|
||||||
|
Volume=/dev/shm:/dev/shm:rw
|
||||||
|
SecurityLabelDisable=true
|
||||||
|
AddCapability=IPC_OWNER
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
LogDriver=k8s-file
|
LogDriver=k8s-file
|
||||||
LogOpt=path={{ authentik_home }}/logs/server.log
|
LogOpt=path={{ authentik_home }}/logs/server.log
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ EnvironmentFile={{ authentik_home }}/.env
|
|||||||
User={{ authentik_uid }}:{{ authentik_gid }}
|
User={{ authentik_uid }}:{{ authentik_gid }}
|
||||||
Annotation=run.oci.keep_original_groups=1
|
Annotation=run.oci.keep_original_groups=1
|
||||||
|
|
||||||
|
# Security configuration for shared memory and IPC
|
||||||
|
Volume=/dev/shm:/dev/shm:rw
|
||||||
|
SecurityLabelDisable=true
|
||||||
|
AddCapability=IPC_OWNER
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
LogDriver=k8s-file
|
LogDriver=k8s-file
|
||||||
LogOpt=path={{ authentik_home }}/logs/worker.log
|
LogOpt=path={{ authentik_home }}/logs/worker.log
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ Description=Authentik Authentication Pod
|
|||||||
|
|
||||||
[Pod]
|
[Pod]
|
||||||
PublishPort=0.0.0.0:{{ authentik_http_port }}:{{ authentik_http_port }}
|
PublishPort=0.0.0.0:{{ authentik_http_port }}:{{ authentik_http_port }}
|
||||||
|
ShmSize=256m
|
||||||
PodmanArgs=
|
PodmanArgs=
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|||||||
8
site.yml
8
site.yml
@@ -19,9 +19,9 @@
|
|||||||
# tags: ['caddy', 'infrastructure', 'web']
|
# tags: ['caddy', 'infrastructure', 'web']
|
||||||
|
|
||||||
# Application services
|
# Application services
|
||||||
- role: sigvild-gallery
|
# - role: sigvild-gallery
|
||||||
tags: ['sigvild', 'gallery', 'wedding']
|
# tags: ['sigvild', 'gallery', 'wedding']
|
||||||
- role: authentik
|
|
||||||
tags: ['authentik']
|
|
||||||
- role: gitea
|
- role: gitea
|
||||||
tags: ['gitea', 'git', 'development']
|
tags: ['gitea', 'git', 'development']
|
||||||
|
- role: authentik
|
||||||
|
tags: ['authentik']
|
||||||
|
|||||||
Reference in New Issue
Block a user