Files
rick-infra/roles/caddy/README.md
Joakim 7788410bfc Complete production-ready Caddy infrastructure with security hardening
- Add comprehensive Caddy role with HTTPS/TLS, DNS challenges, and systemd security
- Implement optimized systemd overrides with enhanced security restrictions
- Create detailed documentation with usage examples and variable references
- Establish proper Ansible configuration with vault integration
- Update site.yml for infrastructure orchestration with role-based deployment
- Add host-specific configuration structure for scalable multi-environment setup
2025-11-12 22:36:34 +01:00

7.1 KiB

Caddy Web Server Role

A comprehensive Ansible role for installing and configuring Caddy web server with automatic HTTPS, DNS challenges, and production security hardening.

Features

  • 🔐 Automatic HTTPS with Let's Encrypt certificates
  • 🌐 DNS Challenge Support for wildcard certificates (Cloudflare provider)
  • 🛡️ Security Hardening with systemd restrictions and capability bounds
  • 🔧 Flexible Configuration for static sites and reverse proxies
  • 📝 Production Ready with proper logging and monitoring
  • 🎯 Role-Based Architecture with host-specific overrides

Requirements

  • Systemd-based Linux distribution (tested on Arch Linux)
  • Ansible 2.9+
  • For DNS challenges: Cloudflare account with API token

Quick Start

Basic Static Site Setup

# host_vars/myserver/main.yml
caddy_tls_enabled: true
caddy_domain: "example.com"
caddy_tls_email: "admin@example.com"

Production Setup with DNS Challenge

# host_vars/myserver/main.yml
caddy_tls_enabled: true
caddy_domain: "example.com"
caddy_tls_email: "{{ vault_caddy_tls_email }}"
caddy_dns_provider: "cloudflare"
cloudflare_api_token: "{{ vault_cloudflare_api_token }}"

# host_vars/myserver/vault.yml (encrypted)
vault_caddy_tls_email: "admin@example.com"
vault_cloudflare_api_token: "your-api-token-here"

Required Variables

For HTTPS/TLS

Variable Required When Description Example
caddy_tls_enabled Always for HTTPS Enable TLS/HTTPS true
caddy_tls_email HTTPS enabled Email for Let's Encrypt "admin@example.com"
caddy_domain HTTPS enabled Primary domain "example.com"

For DNS Challenge (Wildcard Certificates)

Variable Required When Description Example
caddy_dns_provider DNS challenge DNS provider "cloudflare"
cloudflare_api_token Cloudflare DNS API token for DNS "{{ vault_token }}"

Optional Variables

Service Configuration

Variable Default Description
caddy_version "latest" Caddy version to install
caddy_config_file "/etc/caddy/Caddyfile" Main config file path
caddy_service_enabled true Enable systemd service
caddy_service_state "started" Service state
caddy_admin_listen "127.0.0.1:2019" Admin API endpoint

Directory Configuration

Variable Default Description
caddy_config_dir "/etc/caddy" Configuration directory
caddy_data_dir "/var/lib/caddy" Data/state directory
caddy_log_dir "/var/log/caddy" Log directory
caddy_web_root "/var/www" Web root directory

Security Configuration

Variable Default Description
caddy_systemd_security true Enable systemd security restrictions
caddy_firewall_ports [80, 443] Ports to open in firewall

Logging Configuration

Variable Default Description
caddy_log_level "INFO" Logging level (ERROR, WARN, INFO, DEBUG)
caddy_log_format "common" Log format (common, json)

ACME Configuration

Variable Default Description
caddy_acme_ca Let's Encrypt Prod ACME CA directory URL
caddy_dns_resolvers ["1.1.1.1:53", "1.0.0.1:53"] DNS resolvers
caddy_dns_propagation_timeout 120 DNS propagation timeout (seconds)

Advanced Configuration

Multiple Sites

caddy_sites:
  # Static file serving
  - domain: "static.example.com"
    root: "/var/www/static"
    dns_challenge: true

  # Reverse proxy to backend service  
  - domain: "api.example.com"
    backend: "localhost:8080"
    dns_challenge: true
    extra_config: |
      header_up Host {upstream_hostport}
      header_up X-Real-IP {remote_host}

  # HTTP-only internal site
  - domain: "internal.example.com"
    root: "/var/www/internal"
    tls: "off"

Custom Systemd Security

caddy_systemd_security: false  # Disable if you need custom security settings

Staging Environment

# Use Let's Encrypt staging for testing
caddy_acme_ca: "https://acme-staging-v02.api.letsencrypt.org/directory"

Security Features

This role implements production-grade security hardening:

Systemd Security Restrictions

  • NoNewPrivileges: Prevents privilege escalation
  • CapabilityBoundingSet: Limits to CAP_NET_ADMIN and CAP_NET_BIND_SERVICE
  • ProtectSystem=strict: Read-only filesystem protection
  • ProtectKernelLogs/Modules/Tunables: Kernel protection
  • RestrictAddressFamilies: Limits to IPv4, IPv6, and Unix sockets
  • RestrictNamespaces/Realtime/SUIDSGID: Additional restrictions
  • MemoryDenyWriteExecute: Prevents code injection
  • SystemCallFilter=@system-service: Whitelist system calls

File System Security

  • Dedicated caddy user and group
  • Proper directory permissions
  • Read-only configuration binding
  • Isolated temporary files

File Structure

roles/caddy/
├── defaults/main.yml          # Default variables with documentation
├── tasks/main.yml             # Installation and configuration tasks  
├── handlers/main.yml          # Service restart/reload handlers
├── templates/
│   ├── Caddyfile.j2          # Main Caddyfile template
│   ├── index.html.j2         # Default welcome page
│   └── systemd-override.conf.j2  # Security hardening overrides
├── meta/main.yml              # Role metadata and dependencies
└── README.md                  # This file

Example Playbooks

Basic Deployment

- name: Deploy Caddy Web Server
  hosts: webservers
  become: yes
  roles:
    - caddy

Full Infrastructure

- name: Secure VPS Setup
  hosts: production
  become: yes
  roles:
    - security     # Firewall, SSH hardening, etc.
    - caddy        # Web server with HTTPS

Dependencies

This role automatically handles Caddy installation, including:

  • Standard Caddy binary for HTTP-only setups
  • Custom build with Cloudflare DNS plugin when DNS challenge is enabled
  • System user and directory creation
  • Systemd service configuration

Compatibility

  • Tested: Arch Linux
  • Should work: CentOS/RHEL 8+, Ubuntu 18.04+, Debian 10+
  • Requires: systemd, curl/wget

Contributing

When modifying this role:

  1. Update defaults in defaults/main.yml with clear documentation
  2. Use host-specific overrides in host_vars/ for sensitive values
  3. Leverage Ansible Vault for secrets (vault_* variables)
  4. Test changes against the security hardening requirements

Security Considerations

  • Secrets: Always use Ansible Vault for API tokens and sensitive data
  • Firewall: Role opens ports 80 and 443; ensure firewall is configured
  • DNS: DNS challenge requires API access to your DNS provider
  • Monitoring: Monitor certificate expiration and renewal

License

This role is part of the rick-infra project infrastructure configuration.