diff --git a/roles/authentik/README.md b/roles/authentik/README.md new file mode 100644 index 0000000..ece7783 --- /dev/null +++ b/roles/authentik/README.md @@ -0,0 +1,119 @@ +# Authentik Authentication Service Role + +Containerized Authentik authentication service for rick-infra using Podman quadlets and following established architectural patterns. + +## Features + +- ✅ **Containerized deployment**: Uses Podman with systemd quadlets +- ✅ **Self-contained**: Manages its own database and configuration +- ✅ **Service-specific user**: Runs as dedicated `authentik` user with subuid/subgid +- ✅ **Infrastructure integration**: PostgreSQL database, Valkey cache, Caddy proxy +- ✅ **Bridge networking**: Container-to-host communication via host.containers.internal +- ✅ **Security hardened**: Rootless containers with proper isolation +- ✅ **Production ready**: HTTPS, proper health checks, restart policies + +## Architecture + +- **Dependencies**: PostgreSQL, Valkey, Podman, Caddy infrastructure roles +- **Database**: Self-managed authentik database and user in PostgreSQL +- **Cache**: Uses Valkey database 1 for session/cache storage +- **Containers**: 2-container pod (server + worker) deployed via quadlets +- **Network**: Custom bridge network with host gateway access +- **Web access**: https://auth.domain.com (via Caddy reverse proxy) + +## Configuration + +Key variables (defaults in `defaults/main.yml`): + +```yaml +# Service +authentik_service_enabled: true +authentik_http_port: 9000 +authentik_https_port: 9443 + +# Domain +authentik_subdomain: "auth" +authentik_domain: "{{ caddy_domain }}" + +# Container +authentik_image_server: "ghcr.io/goauthentik/server" +authentik_image_tag: "2024.8.3" +authentik_user: "authentik" + +# Database (self-managed) +authentik_db_name: "authentik" +authentik_db_user: "authentik" +authentik_db_password: "{{ vault_authentik_db_password }}" + +# Cache (Valkey) +authentik_redis_host: "host.containers.internal" +authentik_redis_db: 1 + +# Security +authentik_secret_key: "{{ vault_authentik_secret_key }}" +``` + +## Container Architecture + +### Pod Structure +``` +authentik-pod +├── authentik-server (web interface, API) +└── authentik-worker (background tasks) +``` + +### Networking +- **Bridge network**: `authentik-net` with DNS resolution +- **Host gateway**: `host.containers.internal` → host system +- **Port mapping**: 9000:9000 (HTTP), 9443:9443 (HTTPS) + +### Storage +- **Config**: `~authentik/.config/containers/` (quadlets, env) +- **Data**: `~authentik/data/` (persistent application data) +- **Media**: `~authentik/media/` (uploaded files) + +## Usage + +1. **Add vault secrets**: Set required vault variables in host_vars: + ```yaml + vault_authentik_db_password: "secure_db_password" + vault_authentik_secret_key: "long_random_secret_key" + ``` + +2. **Deploy**: `ansible-playbook site.yml --tags authentik` + +3. **Access**: Visit https://auth.yourdomain.com/if/flow/initial-setup/ + +## Dependencies + +- PostgreSQL infrastructure role (database) +- Valkey infrastructure role (cache) +- Podman infrastructure role (containers) +- Caddy web server (HTTPS reverse proxy) +- Vault secrets: `vault_authentik_db_password`, `vault_authentik_secret_key` + +## Containerized Service Pattern + +This role establishes rick-infra's containerized service pattern: + +### User Management +- Service-specific user (`authentik`) with dedicated home directory +- Subuid/subgid ranges (100000-165535) for rootless containers +- Systemd user session with lingering enabled + +### Container Integration +- Podman quadlets for native systemd integration +- Custom bridge networks with host gateway access +- Container-to-host connectivity via `host.containers.internal` +- Persistent storage mounted from user home directory + +### Infrastructure Integration +- Self-managed database creation via PostgreSQL role +- Cache integration with Valkey infrastructure +- Reverse proxy deployment via Caddy sites-enabled +- Follows rick-infra self-contained service patterns + +--- + +**Rick-Infra Authentik Service** +SSO authentication and authorization platform for modern applications. \ No newline at end of file diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml new file mode 100644 index 0000000..9c371ea --- /dev/null +++ b/roles/authentik/defaults/main.yml @@ -0,0 +1,107 @@ +--- +# ================================================================= +# Authentik Authentication Service Role - Container Configuration +# ================================================================= +# Containerized Authentik deployment using Podman quadlets +# Follows rick-infra patterns for pragmatic service deployment + +# ================================================================= +# Service Configuration +# ================================================================= + +# Service Management +authentik_service_enabled: true +authentik_service_state: "started" + +# User and Container Configuration +authentik_user: "authentik" +authentik_group: "authentik" +authentik_home: "/var/lib/authentik" +authentik_subuid_start: 100000 +authentik_subgid_start: 100000 +authentik_subuid_size: 65536 +authentik_subgid_size: 65536 + +# Container Configuration +authentik_image_server: "ghcr.io/goauthentik/server" +authentik_image_tag: "2024.8.3" +authentik_pod_name: "authentik" +authentik_network_name: "authentik-net" + +# ================================================================= +# Domain and Caddy Integration +# ================================================================= + +# Domain setup (follows rick-infra pattern) +authentik_subdomain: "auth" +authentik_domain: "{{ caddy_domain | default('localhost') }}" +authentik_full_domain: "{{ authentik_subdomain }}.{{ authentik_domain }}" + +# Caddy integration +caddy_sites_enabled_dir: "/etc/caddy/sites-enabled" + +# ================================================================= +# Database Configuration (Self-Contained) +# ================================================================= + +# Authentik manages its own database +authentik_db_engine: "postgresql" +authentik_db_host: "host.containers.internal" +authentik_db_port: 5432 +authentik_db_name: "authentik" +authentik_db_user: "authentik" +authentik_db_password: "{{ vault_authentik_db_password }}" + +# ================================================================= +# Cache Configuration (Valkey/Redis) +# ================================================================= + +# Valkey/Redis cache configuration +authentik_redis_host: "host.containers.internal" +authentik_redis_port: 6379 +authentik_redis_db: 1 +authentik_redis_password: "" # Valkey has no auth by default + +# ================================================================= +# Application Settings +# ================================================================= + +# Authentik core configuration +authentik_secret_key: "{{ vault_authentik_secret_key }}" +authentik_error_reporting_enabled: false +authentik_disable_update_check: true +authentik_disable_startup_analytics: true + +# Email configuration (disabled by default) +authentik_email_host: "" +authentik_email_port: 587 +authentik_email_username: "" +authentik_email_password: "" +authentik_email_use_tls: true +authentik_email_from: "authentik@{{ authentik_domain }}" + +# Worker configuration +authentik_worker_concurrency: 2 + +# ================================================================= +# Container Networking +# ================================================================= + +# Port mappings +authentik_http_port: 9000 +authentik_https_port: 9443 + +# Network configuration +authentik_bridge_network: true +authentik_enable_host_gateway: true + +# ================================================================= +# Rick-Infra Integration Notes +# ================================================================= +# This role: +# - Depends on PostgreSQL, Valkey, Podman, and Caddy infrastructure roles +# - Creates its own database and user in PostgreSQL +# - Uses Valkey database 1 for caching +# - Deploys Caddy configuration to sites-enabled +# - Uses Podman quadlets for systemd integration +# - Follows containerized service pattern with service-specific user \ No newline at end of file diff --git a/roles/authentik/handlers/main.yml b/roles/authentik/handlers/main.yml new file mode 100644 index 0000000..bae5fb1 --- /dev/null +++ b/roles/authentik/handlers/main.yml @@ -0,0 +1,28 @@ +--- +# Authentik Role Handlers + +- name: reload systemd + systemd: + daemon_reload: yes + +- name: reload user systemd for authentik + systemd: + daemon_reload: yes + scope: user + become: yes + become_user: "{{ authentik_user }}" + +- name: restart authentik pod + systemd: + name: "{{ authentik_pod_name }}" + state: restarted + scope: user + become: yes + become_user: "{{ authentik_user }}" + when: authentik_service_state == "started" + +- name: reload caddy + systemd: + name: caddy + state: reloaded + when: caddy_service_enabled | default(false) \ No newline at end of file diff --git a/roles/authentik/meta/main.yml b/roles/authentik/meta/main.yml new file mode 100644 index 0000000..09b7196 --- /dev/null +++ b/roles/authentik/meta/main.yml @@ -0,0 +1,37 @@ +--- +galaxy_info: + author: Rick's Infrastructure Team + description: Containerized Authentik authentication service for rick-infra + company: Personal Infrastructure + + license: MIT + + min_ansible_version: "2.9" + + platforms: + - name: ArchLinux + versions: + - all + + galaxy_tags: + - authentication + - authentik + - containers + - podman + - archlinux + +dependencies: + - role: postgresql + tags: ['postgresql', 'infrastructure'] + - role: valkey + tags: ['valkey', 'infrastructure'] + - role: podman + tags: ['podman', 'infrastructure'] + - role: caddy + tags: ['caddy'] + +# Containerized Authentik authentication service +# - Creates its own database using PostgreSQL infrastructure +# - Uses Valkey for caching (database 1) +# - Deployed via Podman quadlets with service-specific user +# - Integrated with Caddy reverse proxy \ No newline at end of file diff --git a/roles/authentik/tasks/containers.yml b/roles/authentik/tasks/containers.yml new file mode 100644 index 0000000..42c16f6 --- /dev/null +++ b/roles/authentik/tasks/containers.yml @@ -0,0 +1,69 @@ +--- +# Authentik Container Deployment - Podman Quadlets + +- name: Deploy authentik environment file + template: + src: authentik.env.j2 + dest: "{{ authentik_home }}/.config/containers/authentik.env" + owner: "{{ authentik_user }}" + group: "{{ authentik_group }}" + mode: '0600' + notify: restart authentik pod + +- name: Deploy authentik pod quadlet + template: + src: authentik.pod.j2 + dest: "{{ authentik_home }}/.config/containers/systemd/{{ authentik_pod_name }}.pod" + owner: "{{ authentik_user }}" + group: "{{ authentik_group }}" + mode: '0644' + notify: + - reload user systemd for authentik + - restart authentik pod + +- name: Deploy authentik server container quadlet + template: + src: authentik-server.container.j2 + dest: "{{ authentik_home }}/.config/containers/systemd/authentik-server.container" + owner: "{{ authentik_user }}" + group: "{{ authentik_group }}" + mode: '0644' + notify: + - reload user systemd for authentik + - restart authentik pod + +- name: Deploy authentik worker container quadlet + template: + src: authentik-worker.container.j2 + dest: "{{ authentik_home }}/.config/containers/systemd/authentik-worker.container" + owner: "{{ authentik_user }}" + group: "{{ authentik_group }}" + mode: '0644' + notify: + - reload user systemd for authentik + - restart authentik pod + +- name: Reload user systemd to recognize quadlets + systemd: + daemon_reload: yes + scope: user + become: yes + become_user: "{{ authentik_user }}" + +- name: Enable and start authentik pod + systemd: + name: "{{ authentik_pod_name }}-pod" + enabled: "{{ authentik_service_enabled }}" + state: "{{ authentik_service_state }}" + scope: user + become: yes + become_user: "{{ authentik_user }}" + +- name: Wait for Authentik to be ready + uri: + url: "http://127.0.0.1:{{ authentik_http_port }}/if/flow/initial-setup/" + method: GET + status_code: [200, 302] + retries: 30 + delay: 2 + when: authentik_service_state == "started" diff --git a/roles/authentik/tasks/database.yml b/roles/authentik/tasks/database.yml new file mode 100644 index 0000000..f59344a --- /dev/null +++ b/roles/authentik/tasks/database.yml @@ -0,0 +1,28 @@ +--- +# Authentik Database Management - Self-Contained Database Setup + +- name: Create Authentik database user + postgresql_user: + name: "{{ authentik_db_user }}" + password: "{{ authentik_db_password }}" + encrypted: yes + become: yes + become_user: postgres + +- name: Create Authentik database + postgresql_db: + name: "{{ authentik_db_name }}" + owner: "{{ authentik_db_user }}" + encoding: UTF8 + template: template0 + become: yes + become_user: postgres + +- name: Grant all privileges on Authentik database to user + postgresql_privs: + db: "{{ authentik_db_name }}" + privs: ALL + type: database + role: "{{ authentik_db_user }}" + become: yes + become_user: postgres \ No newline at end of file diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml new file mode 100644 index 0000000..3468971 --- /dev/null +++ b/roles/authentik/tasks/main.yml @@ -0,0 +1,36 @@ +--- +# Authentik Authentication Service Role - Containerized Implementation +# Manages Authentik using Podman with self-contained database + +- name: Create authentik user and configure subuid/subgid + include_tasks: user.yml + +- name: Set up authentik database + include_tasks: database.yml + +- name: Configure container networking + include_tasks: networking.yml + +- name: Deploy authentik containers via quadlets + include_tasks: containers.yml + +- name: Deploy Caddy configuration for Authentik + template: + src: authentik.caddy.j2 + dest: "{{ caddy_sites_enabled_dir }}/authentik.caddy" + mode: '0644' + notify: reload caddy + when: caddy_sites_enabled_dir is defined + +- name: Display Authentik service status + debug: + msg: | + ✅ Authentik authentication service deployed successfully! + + 🌐 Web Interface: https://{{ authentik_full_domain }} + 🔐 Admin Interface: https://{{ authentik_full_domain }}/if/admin/ + 📦 Local HTTP: http://127.0.0.1:{{ authentik_http_port }} + 🗄️ Database: {{ authentik_db_name }} (self-managed) + 🚀 Cache: Valkey database {{ authentik_redis_db }} + + 🏗️ Authentication service ready for SSO integration! \ No newline at end of file diff --git a/roles/authentik/tasks/networking.yml b/roles/authentik/tasks/networking.yml new file mode 100644 index 0000000..c2bf74c --- /dev/null +++ b/roles/authentik/tasks/networking.yml @@ -0,0 +1,23 @@ +--- +# Authentik Container Networking - Bridge Network Setup + +- name: Create authentik bridge network + containers.podman.podman_network: + name: "{{ authentik_network_name }}" + driver: bridge + internal: false + state: present + become: yes + become_user: "{{ authentik_user }}" + +- name: Check if authentik network exists + command: podman network ls --format json + become: yes + become_user: "{{ authentik_user }}" + register: network_list + changed_when: false + +- name: Ensure host gateway is available in network + debug: + msg: "Network {{ authentik_network_name }} configured for host.containers.internal access" + when: authentik_enable_host_gateway | default(true) diff --git a/roles/authentik/tasks/user.yml b/roles/authentik/tasks/user.yml new file mode 100644 index 0000000..90298e9 --- /dev/null +++ b/roles/authentik/tasks/user.yml @@ -0,0 +1,60 @@ +--- +# Authentik User Management - Service-Specific User Setup + +- name: Create authentik group + group: + name: "{{ authentik_group }}" + system: yes + +- name: Create authentik user + user: + name: "{{ authentik_user }}" + group: "{{ authentik_group }}" + system: yes + shell: /bin/bash + home: "{{ authentik_home }}" + create_home: yes + comment: "Authentik authentication service" + +- name: Set up subuid for authentik user + lineinfile: + path: /etc/subuid + line: "{{ authentik_user }}:{{ authentik_subuid_start }}:{{ authentik_subuid_size }}" + create: yes + mode: '0644' + +- name: Set up subgid for authentik user + lineinfile: + path: /etc/subgid + line: "{{ authentik_user }}:{{ authentik_subgid_start }}:{{ authentik_subgid_size }}" + create: yes + mode: '0644' + +- name: Create authentik directories + file: + path: "{{ item }}" + state: directory + owner: "{{ authentik_user }}" + group: "{{ authentik_group }}" + mode: '0755' + loop: + - "{{ authentik_home }}" + - "{{ authentik_home }}/.config" + - "{{ authentik_home }}/.config/systemd" + - "{{ authentik_home }}/.config/systemd/user" + - "{{ authentik_home }}/.config/containers" + - "{{ authentik_home }}/.config/containers/systemd" + - "{{ authentik_home }}/data" + - "{{ authentik_home }}/media" + +- name: Enable lingering for authentik user + command: loginctl enable-linger {{ authentik_user }} + args: + creates: "/var/lib/systemd/linger/{{ authentik_user }}" + +- name: Initialize user systemd for authentik + systemd: + daemon_reload: yes + scope: user + become: yes + become_user: "{{ authentik_user }}" \ No newline at end of file diff --git a/roles/authentik/templates/authentik-server.container.j2 b/roles/authentik/templates/authentik-server.container.j2 new file mode 100644 index 0000000..845ed5e --- /dev/null +++ b/roles/authentik/templates/authentik-server.container.j2 @@ -0,0 +1,37 @@ +# Authentik Server Container Quadlet +# Generated by rick-infra Ansible role + +[Unit] +Description=Authentik Server Container +Requires={{ authentik_pod_name }}-pod.service +After={{ authentik_pod_name }}-pod.service + +[Container] +ContainerName=authentik-server +Image={{ authentik_image_server }}:{{ authentik_image_tag }} +Pod={{ authentik_pod_name }}.pod + +# Environment configuration +EnvironmentFile={{ authentik_home }}/.config/containers/authentik.env +Environment=AUTHENTIK_LISTEN__HTTP=0.0.0.0:9000 +Environment=AUTHENTIK_LISTEN__HTTPS=0.0.0.0:9443 + +# Server command +Exec=server + +# Volumes for persistent data +Volume={{ authentik_home }}/data:/data:Z +Volume={{ authentik_home }}/media:/media:Z + +# Health check +HealthCmd=ak healthcheck +HealthInterval=30s +HealthTimeout=10s +HealthRetries=3 + +[Service] +Restart=always +RestartSec=10 + +[Install] +WantedBy=default.target diff --git a/roles/authentik/templates/authentik-worker.container.j2 b/roles/authentik/templates/authentik-worker.container.j2 new file mode 100644 index 0000000..b37bd34 --- /dev/null +++ b/roles/authentik/templates/authentik-worker.container.j2 @@ -0,0 +1,35 @@ +# Authentik Worker Container Quadlet +# Generated by rick-infra Ansible role + +[Unit] +Description=Authentik Worker Container +Requires={{ authentik_pod_name }}-pod.service authentik-server.service +After={{ authentik_pod_name }}-pod.service authentik-server.service + +[Container] +ContainerName=authentik-worker +Image={{ authentik_image_server }}:{{ authentik_image_tag }} +Pod={{ authentik_pod_name }}.pod + +# Environment configuration +EnvironmentFile={{ authentik_home }}/.config/containers/authentik.env + +# Worker command +Exec=worker + +# Volumes for persistent data +Volume={{ authentik_home }}/data:/data:Z +Volume={{ authentik_home }}/media:/media:Z + +# Health check +HealthCmd=ak healthcheck +HealthInterval=30s +HealthTimeout=10s +HealthRetries=3 + +[Service] +Restart=always +RestartSec=10 + +[Install] +WantedBy=default.target diff --git a/roles/authentik/templates/authentik.caddy.j2 b/roles/authentik/templates/authentik.caddy.j2 new file mode 100644 index 0000000..153e66e --- /dev/null +++ b/roles/authentik/templates/authentik.caddy.j2 @@ -0,0 +1,34 @@ +# Authentik Caddy Configuration - Rick-Infra +# Generated by Ansible Authentik role +# Deployed to {{ caddy_sites_enabled_dir }}/authentik.caddy + +{{ authentik_full_domain }} { + # Reverse proxy to Authentik + reverse_proxy 127.0.0.1:{{ authentik_http_port }} + + # Security headers for authentication service + header { + # Enable HSTS + Strict-Transport-Security max-age=31536000; + # Prevent embedding in frames + X-Frame-Options DENY + # Prevent content type sniffing + X-Content-Type-Options nosniff + # XSS protection + X-XSS-Protection "1; mode=block" + # Referrer policy for privacy + Referrer-Policy strict-origin-when-cross-origin + } + + # Logging + log { + output file /var/log/caddy/authentik_access.log + } + + # Optional: Custom error pages + handle_errors { + respond "Authentication service temporarily unavailable" 503 + } +} + +# Rick-Infra: Containerized Authentik authentication service with Caddy reverse proxy \ No newline at end of file diff --git a/roles/authentik/templates/authentik.env.j2 b/roles/authentik/templates/authentik.env.j2 new file mode 100644 index 0000000..84f3958 --- /dev/null +++ b/roles/authentik/templates/authentik.env.j2 @@ -0,0 +1,39 @@ +# Authentik Environment Configuration +# Generated by rick-infra Ansible role + +# Database Configuration +AUTHENTIK_POSTGRESQL__HOST={{ authentik_db_host }} +AUTHENTIK_POSTGRESQL__PORT={{ authentik_db_port }} +AUTHENTIK_POSTGRESQL__NAME={{ authentik_db_name }} +AUTHENTIK_POSTGRESQL__USER={{ authentik_db_user }} +AUTHENTIK_POSTGRESQL__PASSWORD={{ authentik_db_password }} + +# Cache Configuration (Valkey/Redis) +AUTHENTIK_REDIS__HOST={{ authentik_redis_host }} +AUTHENTIK_REDIS__PORT={{ authentik_redis_port }} +AUTHENTIK_REDIS__DB={{ authentik_redis_db }} +{% if authentik_redis_password %} +AUTHENTIK_REDIS__PASSWORD={{ authentik_redis_password }} +{% endif %} + +# Core Configuration +AUTHENTIK_SECRET_KEY={{ authentik_secret_key }} +AUTHENTIK_ERROR_REPORTING__ENABLED={{ authentik_error_reporting_enabled | lower }} +AUTHENTIK_DISABLE_UPDATE_CHECK={{ authentik_disable_update_check | lower }} +AUTHENTIK_DISABLE_STARTUP_ANALYTICS={{ authentik_disable_startup_analytics | lower }} + +# Worker Configuration +AUTHENTIK_WORKER__CONCURRENCY={{ authentik_worker_concurrency }} + +# Email Configuration +{% if authentik_email_host %} +AUTHENTIK_EMAIL__HOST={{ authentik_email_host }} +AUTHENTIK_EMAIL__PORT={{ authentik_email_port }} +AUTHENTIK_EMAIL__USERNAME={{ authentik_email_username }} +AUTHENTIK_EMAIL__PASSWORD={{ authentik_email_password }} +AUTHENTIK_EMAIL__USE_TLS={{ authentik_email_use_tls | lower }} +AUTHENTIK_EMAIL__FROM={{ authentik_email_from }} +{% endif %} + +# Trust reverse proxy headers +AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS=127.0.0.1/32,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 \ No newline at end of file diff --git a/roles/authentik/templates/authentik.pod.j2 b/roles/authentik/templates/authentik.pod.j2 new file mode 100644 index 0000000..837f956 --- /dev/null +++ b/roles/authentik/templates/authentik.pod.j2 @@ -0,0 +1,21 @@ +# Authentik Pod Quadlet +# Generated by rick-infra Ansible role + +[Unit] +Description=Authentik Authentication Service Pod +Wants=network-online.target +After=network-online.target + +[Pod] +PodName={{ authentik_pod_name }} +Network={{ authentik_network_name }} +{% if authentik_enable_host_gateway | default(true) %} +AddHost=host.containers.internal:host-gateway +{% endif %} + +# Published ports for web access +PublishPort={{ authentik_http_port }}:9000 +PublishPort={{ authentik_https_port }}:9443 + +[Install] +WantedBy=default.target \ No newline at end of file diff --git a/roles/gitea/templates/gitea.service.j2 b/roles/gitea/templates/gitea.service.j2 index ad75ff6..59b9ef4 100644 --- a/roles/gitea/templates/gitea.service.j2 +++ b/roles/gitea/templates/gitea.service.j2 @@ -20,8 +20,7 @@ NoNewPrivileges=true PrivateTmp=true ProtectHome=true ProtectSystem=strict -ReadWritePaths={{ gitea_home }} -ReadWritePaths=/etc/gitea +ReadWritePaths={{ gitea_home }} /etc/gitea RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 # Process limits @@ -30,4 +29,4 @@ LimitNOFILE=65536 [Install] WantedBy=multi-user.target -# Rick-Infra: Self-contained Gitea service with security hardening \ No newline at end of file +# Rick-Infra: Self-contained Gitea service with security hardening diff --git a/roles/valkey/defaults/main.yml b/roles/valkey/defaults/main.yml index 648b439..97ba03c 100644 --- a/roles/valkey/defaults/main.yml +++ b/roles/valkey/defaults/main.yml @@ -19,7 +19,7 @@ valkey_port: 6379 valkey_protected_mode: true # Authentication -valkey_requirepass: "{{ vault_valkey_password }}" +valkey_password: "{{ vault_valkey_password }}" # ================================================================= # Performance Settings (Conservative Defaults) @@ -45,8 +45,7 @@ valkey_appendonly: false # RDB only for simplicity # Security Configuration # ================================================================= -# Systemd security hardening -valkey_systemd_security: true +# Security hardening is now built into the custom service file # Valkey security settings valkey_timeout: 300 @@ -85,4 +84,4 @@ valkey_syslog_ident: "valkey" # - VALKEY_DB: "1" (or 2, 3, etc. - unique per application) # # Note: Applications can also use REDIS_* environment variables -# for compatibility since Valkey is fully Redis-compatible \ No newline at end of file +# for compatibility since Valkey is fully Redis-compatible diff --git a/roles/valkey/tasks/main.yml b/roles/valkey/tasks/main.yml index 9cf1d28..c63b910 100644 --- a/roles/valkey/tasks/main.yml +++ b/roles/valkey/tasks/main.yml @@ -38,19 +38,12 @@ backup: yes notify: restart valkey -- name: Create systemd override directory for Valkey security - file: - path: /etc/systemd/system/valkey.service.d - state: directory - mode: '0755' - when: valkey_systemd_security - -- name: Deploy Valkey systemd security override +- name: Deploy custom Valkey service file template: - src: systemd-override.conf.j2 - dest: /etc/systemd/system/valkey.service.d/override.conf + src: valkey.service.j2 + dest: /etc/systemd/system/valkey.service mode: '0644' - when: valkey_systemd_security + backup: yes notify: - reload systemd - restart valkey @@ -70,7 +63,7 @@ when: valkey_service_state == "started" - name: Test Valkey connectivity - command: redis-cli -h {{ valkey_bind }} -p {{ valkey_port }} -a {{ valkey_requirepass }} ping + command: valkey-cli -h {{ valkey_bind }} -p {{ valkey_port }} -a "{{ valkey_password }}" ping register: valkey_ping_result changed_when: false failed_when: valkey_ping_result.stdout != "PONG" diff --git a/roles/valkey/templates/systemd-override.conf.j2 b/roles/valkey/templates/systemd-override.conf.j2 deleted file mode 100644 index 2d566fa..0000000 --- a/roles/valkey/templates/systemd-override.conf.j2 +++ /dev/null @@ -1,49 +0,0 @@ -# Redis Systemd Security Override -# Generated by rick-infra Redis role -# -# This file provides additional security hardening for the Redis service -# following the same security patterns as the PostgreSQL role. - -[Service] -# Security hardening -NoNewPrivileges=yes -PrivateTmp=yes -PrivateDevices=yes -ProtectSystem=strict -ProtectHome=yes -ProtectKernelTunables=yes -ProtectKernelModules=yes -ProtectControlGroups=yes -RestrictRealtime=yes -RestrictSUIDSGID=yes - -# Network security -RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX - -# Filesystem permissions -ReadWritePaths=/var/lib/valkey -ReadOnlyPaths=/etc/valkey - -# System call filtering -SystemCallFilter=@system-service -SystemCallFilter=~@privileged @resources @obsolete - -# Memory and resource limits -MemoryDenyWriteExecute=yes -LockPersonality=yes - -# Capabilities -CapabilityBoundingSet= -AmbientCapabilities= - -# User and group isolation -DynamicUser=no -User=valkey -Group=valkey - -# Process isolation -PrivateUsers=yes -RemoveIPC=yes - -# Additional Redis-specific security -UMask=0027 \ No newline at end of file diff --git a/roles/valkey/templates/valkey.conf.j2 b/roles/valkey/templates/valkey.conf.j2 index 17cadcb..6970133 100644 --- a/roles/valkey/templates/valkey.conf.j2 +++ b/roles/valkey/templates/valkey.conf.j2 @@ -31,7 +31,7 @@ tcp-keepalive {{ valkey_tcp_keepalive }} # ================================================================= # Require password for all operations -requirepass {{ valkey_requirepass }} +requirepass {{ valkey_password }} # ================================================================= # Memory Management @@ -127,4 +127,4 @@ client-output-buffer-limit pubsub 32mb 8mb 60 # - Database 3+: Future applications # # Connection example: -# redis-cli -h {{ valkey_bind }} -p {{ valkey_port }} -a {{ valkey_requirepass }} -n 1 \ No newline at end of file +# valkey-cli -h {{ valkey_bind }} -p {{ valkey_port }} -a {{ valkey_password }} -n 1 diff --git a/roles/valkey/templates/valkey.service.j2 b/roles/valkey/templates/valkey.service.j2 new file mode 100644 index 0000000..70b052b --- /dev/null +++ b/roles/valkey/templates/valkey.service.j2 @@ -0,0 +1,78 @@ +# Valkey Systemd Service +# Generated by rick-infra Valkey role +# +# This service provides a secure, hardened Valkey instance with proper configuration loading + +[Unit] +Description=Valkey (Redis-compatible) Key-Value Store +Documentation=https://valkey.io/ +After=network.target +Wants=network-online.target + +[Service] +Type=notify +User=valkey +Group=valkey + +# Core service configuration - ensures config file is loaded +ExecStart=/usr/bin/valkey-server /etc/valkey/valkey.conf --supervised systemd +ExecReload=/bin/kill -USR2 $MAINPID + +# Restart configuration +Restart=on-failure +RestartSec=5s +TimeoutStartSec=60 +TimeoutStopSec=60 + +# Runtime directory +RuntimeDirectory=valkey +RuntimeDirectoryMode=755 + +# Resource limits +LimitNOFILE=10032 + +# Security hardening +NoNewPrivileges=yes +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=strict +ProtectHome=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectControlGroups=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes + +# Network security +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX + +# Filesystem permissions +ReadWritePaths=/var/lib/valkey +ReadOnlyPaths=/etc/valkey + +# System call filtering +SystemCallFilter=@system-service +SystemCallFilter=~@privileged @resources @obsolete + +# Memory and resource limits +MemoryDenyWriteExecute=yes +LockPersonality=yes + +# Capabilities (remove all unnecessary capabilities) +CapabilityBoundingSet= +AmbientCapabilities= + +# Process isolation +PrivateUsers=yes +RemoveIPC=yes + +# Additional security +UMask=0027 + +# Ensure service stops cleanly +KillMode=mixed +KillSignal=SIGTERM + +[Install] +WantedBy=multi-user.target +Alias=redis.service \ No newline at end of file diff --git a/site.yml b/site.yml index bb653ea..5a2aedb 100644 --- a/site.yml +++ b/site.yml @@ -13,13 +13,15 @@ # tags: ['postgresql', 'infrastructure', 'database'] # - role: valkey # tags: ['valkey', 'redis', 'infrastructure', 'cache'] - - role: podman - tags: ['podman', 'containers', 'infrastructure'] + # - role: podman + # tags: ['podman', 'containers', 'infrastructure'] # - role: caddy # tags: ['caddy', 'infrastructure', 'web'] # Application services - # - role: gitea - # tags: ['gitea', 'git', 'development'] + - role: authentik + tags: ['authentik', 'auth', 'sso', 'containers'] + - role: gitea + tags: ['gitea', 'git', 'development'] # - role: sigvild-gallery # tags: ['sigvild', 'gallery', 'wedding']