Fix: Complete authentik Quadlet implementation with networking solution
Resolves authentik deployment issues by implementing proper Podman Quadlet configuration and fixing networking for external access through Caddy. Core Fixes: • Add missing [Install] sections to container Quadlet files for systemd service generation • Fix pod references from 'systemd-authentik' to 'authentik.pod' for proper Quadlet linking • Remove problematic --userns=host to use proper rootless user namespaces • Configure subuid/subgid ranges for authentik user (200000:65536) • Update networking to bind 0.0.0.0:9000 only (remove unnecessary HTTPS port 9443) • Add AUTHENTIK_LISTEN__HTTP=0.0.0.0:9000 environment configuration • Fix Caddy reverse proxy to use HTTP backend instead of HTTPS Infrastructure Updates: • Enhance PostgreSQL role with Unix socket configuration and user management • Improve Valkey role with proper systemd integration and socket permissions • Add comprehensive service integration documentation • Update deployment playbooks with backup and restore capabilities Security Improvements: • Secure network isolation with Caddy SSL termination • Reduced attack surface by removing direct HTTPS container exposure • Proper rootless container configuration with user namespace mapping Result: authentik now fully operational with external HTTPS access via auth.jnss.me All systemd services (authentik-pod, authentik-server, authentik-worker) running correctly.
This commit is contained in:
312
docs/service-integration-guide.md
Normal file
312
docs/service-integration-guide.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# Service Integration Guide
|
||||
|
||||
This guide explains how to add new containerized services to rick-infra with PostgreSQL and Valkey/Redis access via Unix sockets.
|
||||
|
||||
## Overview
|
||||
|
||||
Rick-infra provides a standardized approach for containerized services to access infrastructure services through Unix sockets, maintaining security while providing optimal performance.
|
||||
|
||||
## Architecture Pattern
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Application Service (Podman Container) │
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ Your Container │ │
|
||||
│ │ UID: service │ (host user namespace) │
|
||||
│ │ Groups: service,│ │
|
||||
│ │ postgres, │ (supplementary groups preserved) │
|
||||
│ │ valkey │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │ │
|
||||
│ └─────────────────────┐ │
|
||||
└─────────────────────────────────│───────────────────────────┘
|
||||
│
|
||||
┌───────────────▼──────────────┐
|
||||
│ Host Infrastructure Services │
|
||||
│ │
|
||||
│ PostgreSQL Unix Socket │
|
||||
│ /var/run/postgresql/ │
|
||||
│ │
|
||||
│ Valkey Unix Socket │
|
||||
│ /var/run/valkey/ │
|
||||
└──────────────────────────────┘
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Your service must be deployed as:
|
||||
1. **Systemd user service** (via Quadlet)
|
||||
2. **Dedicated system user**
|
||||
3. **Podman container** (rootless)
|
||||
|
||||
## Step 1: User Setup
|
||||
|
||||
Create a dedicated system user for your service and add it to infrastructure groups:
|
||||
|
||||
```yaml
|
||||
- name: Create service user
|
||||
user:
|
||||
name: myservice
|
||||
system: true
|
||||
shell: /bin/false
|
||||
home: /opt/myservice
|
||||
create_home: true
|
||||
|
||||
- name: Add service user to infrastructure groups
|
||||
user:
|
||||
name: myservice
|
||||
groups:
|
||||
- postgres # For PostgreSQL access
|
||||
- valkey # For Valkey/Redis access
|
||||
append: true
|
||||
```
|
||||
|
||||
## Step 2: Container Configuration
|
||||
|
||||
### Pod Configuration (`myservice.pod`)
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=My Service Pod
|
||||
|
||||
[Pod]
|
||||
PublishPort=127.0.0.1:8080:8080
|
||||
PodmanArgs=--userns=host
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
**Key Points**:
|
||||
- `--userns=host` preserves host user namespace
|
||||
- Standard port publishing for network access
|
||||
|
||||
### Container Configuration (`myservice.container`)
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=My Service Container
|
||||
|
||||
[Container]
|
||||
Image=my-service:latest
|
||||
Pod=myservice.pod
|
||||
EnvironmentFile=/opt/myservice/.env
|
||||
User={{ service_uid }}:{{ service_gid }}
|
||||
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
|
||||
|
||||
# Application volumes
|
||||
Volume=/opt/myservice/data:/data
|
||||
Volume=/opt/myservice/logs:/logs
|
||||
|
||||
Exec=my-service
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
**Key Points**:
|
||||
- `Annotation=run.oci.keep_original_groups=1` preserves supplementary groups
|
||||
- Mount socket directories with `:Z` for SELinux relabeling
|
||||
- Use host UID/GID for the service user
|
||||
|
||||
## Step 3: Service Configuration
|
||||
|
||||
### PostgreSQL Connection
|
||||
|
||||
Use Unix socket connection strings:
|
||||
|
||||
```bash
|
||||
# Environment variable
|
||||
DATABASE_URL=postgresql://myservice@/myservice_db?host=/var/run/postgresql
|
||||
|
||||
# Or separate variables
|
||||
DB_HOST=/var/run/postgresql
|
||||
DB_USER=myservice
|
||||
DB_NAME=myservice_db
|
||||
# No DB_PORT needed for Unix sockets
|
||||
```
|
||||
|
||||
### Valkey/Redis Connection
|
||||
|
||||
**Correct Format** (avoids URL parsing issues):
|
||||
|
||||
```bash
|
||||
# Single URL format (recommended)
|
||||
CACHE_URL=unix:///var/run/valkey/valkey.sock?db=2&password=your_password
|
||||
|
||||
# Alternative format
|
||||
REDIS_URL=redis://localhost/2?unix_socket_path=/var/run/valkey/valkey.sock
|
||||
```
|
||||
|
||||
**Avoid** separate HOST/DB variables which can cause port parsing issues:
|
||||
```bash
|
||||
# DON'T USE - causes parsing problems
|
||||
REDIS_HOST=unix:///var/run/valkey/valkey.sock
|
||||
REDIS_DB=2
|
||||
```
|
||||
|
||||
## Step 4: Database Setup
|
||||
|
||||
Add database setup tasks to your role:
|
||||
|
||||
```yaml
|
||||
- name: Create application database
|
||||
postgresql_db:
|
||||
name: "{{ service_db_name }}"
|
||||
owner: "{{ service_db_user }}"
|
||||
encoding: UTF-8
|
||||
lc_collate: en_US.UTF-8
|
||||
lc_ctype: en_US.UTF-8
|
||||
become_user: postgres
|
||||
|
||||
- name: Create application database user
|
||||
postgresql_user:
|
||||
name: "{{ service_db_user }}"
|
||||
password: "{{ service_db_password }}"
|
||||
db: "{{ service_db_name }}"
|
||||
priv: ALL
|
||||
become_user: postgres
|
||||
|
||||
- name: Grant connect privileges
|
||||
postgresql_privs:
|
||||
db: "{{ service_db_name }}"
|
||||
role: "{{ service_db_user }}"
|
||||
objs: ALL_IN_SCHEMA
|
||||
privs: ALL
|
||||
become_user: postgres
|
||||
```
|
||||
|
||||
## Step 5: Service Role Template
|
||||
|
||||
Create an Ansible role using this pattern:
|
||||
|
||||
```
|
||||
myservice/
|
||||
├── defaults/main.yml
|
||||
├── handlers/main.yml
|
||||
├── tasks/
|
||||
│ ├── main.yml
|
||||
│ ├── database.yml
|
||||
│ └── cache.yml
|
||||
├── templates/
|
||||
│ ├── myservice.env.j2
|
||||
│ ├── myservice.pod
|
||||
│ ├── myservice.container
|
||||
│ └── myservice.caddy.j2
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### Example Environment Template
|
||||
|
||||
```bash
|
||||
# My Service Configuration
|
||||
# Generated by Ansible - DO NOT EDIT
|
||||
|
||||
# Database Configuration (Unix Socket)
|
||||
DATABASE_URL=postgresql://{{ service_db_user }}@/{{ service_db_name }}?host={{ postgresql_unix_socket_directories }}
|
||||
DB_PASSWORD={{ service_db_password }}
|
||||
|
||||
# Cache Configuration (Unix Socket)
|
||||
CACHE_URL=unix://{{ valkey_unix_socket_path }}?db={{ service_valkey_db }}&password={{ valkey_password }}
|
||||
|
||||
# Application Configuration
|
||||
SECRET_KEY={{ service_secret_key }}
|
||||
LOG_LEVEL={{ service_log_level }}
|
||||
BIND_ADDRESS={{ service_bind_address }}:{{ service_port }}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Socket Permission Issues
|
||||
|
||||
If you get permission denied errors:
|
||||
|
||||
1. **Check group membership**:
|
||||
```bash
|
||||
groups myservice
|
||||
# Should show: myservice postgres valkey
|
||||
```
|
||||
|
||||
2. **Verify container annotations**:
|
||||
```bash
|
||||
podman inspect myservice --format='{{.Config.Annotations}}'
|
||||
# Should include: run.oci.keep_original_groups=1
|
||||
```
|
||||
|
||||
3. **Check socket permissions**:
|
||||
```bash
|
||||
ls -la /var/run/postgresql/
|
||||
ls -la /var/run/valkey/
|
||||
```
|
||||
|
||||
### Connection Issues
|
||||
|
||||
1. **Test socket access from host**:
|
||||
```bash
|
||||
sudo -u myservice psql -h /var/run/postgresql -U myservice myservice_db
|
||||
sudo -u myservice redis-cli -s /var/run/valkey/valkey.sock ping
|
||||
```
|
||||
|
||||
2. **Check URL format**:
|
||||
- Use single `CACHE_URL` instead of separate variables
|
||||
- Include password in URL if required
|
||||
- Verify database number is correct
|
||||
|
||||
### Container Issues
|
||||
|
||||
1. **Check container user**:
|
||||
```bash
|
||||
podman exec myservice id
|
||||
# Should show correct UID and supplementary groups
|
||||
```
|
||||
|
||||
2. **Verify socket mounts**:
|
||||
```bash
|
||||
podman exec myservice ls -la /var/run/postgresql/
|
||||
podman exec myservice ls -la /var/run/valkey/
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Security**:
|
||||
- Use dedicated system users for each service
|
||||
- Limit group memberships to required infrastructure
|
||||
- Use vault variables for secrets
|
||||
|
||||
2. **Configuration**:
|
||||
- Use single URL format for Redis connections
|
||||
- Mount socket directories with appropriate SELinux labels
|
||||
- Include `run.oci.keep_original_groups=1` annotation
|
||||
|
||||
3. **Deployment**:
|
||||
- Test socket access before container deployment
|
||||
- Use proper dependency ordering in playbooks
|
||||
- Include database and cache setup tasks
|
||||
|
||||
4. **Monitoring**:
|
||||
- Monitor socket file permissions
|
||||
- Check service logs for connection errors
|
||||
- Verify group memberships after user changes
|
||||
|
||||
## Example Integration
|
||||
|
||||
See the `authentik` role for a complete example of this pattern:
|
||||
|
||||
- **Templates**: `roles/authentik/templates/`
|
||||
- **Tasks**: `roles/authentik/tasks/`
|
||||
- **Documentation**: `roles/authentik/README.md`
|
||||
|
||||
This provides a working reference implementation for Unix socket integration.
|
||||
Reference in New Issue
Block a user