# Caddy Web Server Role A comprehensive Ansible role for installing and configuring [Caddy](https://caddyserver.com/) 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 ```yaml # host_vars/myserver/main.yml caddy_tls_enabled: true caddy_domain: "example.com" caddy_tls_email: "admin@example.com" ``` ### Production Setup with DNS Challenge ```yaml # 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 ```yaml 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 ```yaml caddy_systemd_security: false # Disable if you need custom security settings ``` ### Staging Environment ```yaml # 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 ```yaml - name: Deploy Caddy Web Server hosts: webservers become: yes roles: - caddy ``` ### Full Infrastructure ```yaml - 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.