# Caddy Web Server Role A modern Ansible role for installing and configuring [Caddy](https://caddyserver.com/) web server with automatic HTTPS, file-based service configuration, and security hardening. ## Features - 🔐 **Automatic HTTPS** with Let's Encrypt certificates - 🌐 **DNS Challenge Support** for wildcard certificates (Cloudflare provider) - 📁 **File-Based Configuration** using sites-enabled directory pattern - 🛡️ **Security Hardening** with systemd restrictions and capability bounds - ⚡ **Zero Downtime** configuration reloads without service restarts - 🎯 **Simple Architecture** following nginx-like configuration patterns - 🔄 **Git-Friendly** configuration files for version control ## Requirements - Systemd-based Linux distribution (tested on Arch Linux) - Ansible 2.9+ - For DNS challenges: Cloudflare account with API token ## Quick Start ### Infrastructure Setup (One-time) Configure core Caddy infrastructure in your host vars: ```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" ``` ### Service Configuration (Per-Service) Services deploy configuration files to the sites-enabled directory: ```yaml # In your service role (e.g., roles/myapi/tasks/main.yml) - name: Deploy API service # ... your service deployment tasks ... - name: Deploy Caddy configuration template: src: myapi.caddy.j2 dest: "{{ caddy_sites_enabled_dir }}/myapi.caddy" owner: root group: "{{ caddy_user }}" mode: '0644' notify: reload caddy ``` ```caddyfile # roles/myapi/templates/myapi.caddy.j2 {{ service_domain }} { reverse_proxy {{ service_backend }} } ``` ### Service Role Dependencies ```yaml # roles/myapi/meta/main.yml dependencies: - role: caddy ``` ## 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 }}"` | ### For Service Configuration (Per-Service) | Variable | Required | Description | Example | |----------|----------|-------------|---------| | `service_domain` | Yes | Domain for the service | `"api.example.com"` | | `service_backend` | Yes | Backend address | `"localhost:8080"` | These are typically set in service role defaults or passed as template variables. ## 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_sites_enabled_dir` | `"/etc/caddy/sites-enabled"` | Service config directory | | `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` | `"json"` | Log format (common, json) | | `caddy_log_credentials` | `false` | Log credentials in access logs | ### 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) | ## Service Configuration Patterns ### Simple API Service ```caddyfile # roles/simple-api/templates/simple-api.caddy.j2 api.example.com { reverse_proxy localhost:3000 } ``` ```yaml # roles/simple-api/tasks/main.yml - name: Deploy API configuration template: src: simple-api.caddy.j2 dest: "{{ caddy_sites_enabled_dir }}/simple-api.caddy" owner: root group: "{{ caddy_user }}" mode: '0644' notify: reload caddy ``` ### Load Balanced Service ```caddyfile # roles/balanced-api/templates/balanced-api.caddy.j2 api.example.com { reverse_proxy localhost:8080 localhost:8081 localhost:8082 { lb_policy least_conn health_timeout 5s health_interval 30s } } ``` ### Static File Service ```caddyfile # roles/static-files/templates/static-files.caddy.j2 static.example.com { root * /var/www/static file_server browse } ``` ### Service Cleanup ```yaml # Remove service when decommissioning - name: Remove service configuration file: path: "{{ caddy_sites_enabled_dir }}/old-service.caddy" state: absent notify: reload caddy ``` ## Available Handlers This role provides Ansible handlers for service management: ### `reload caddy` - Reloads Caddy configuration without restart - Automatically triggered when .caddy files change - Picks up new configurations from sites-enabled directory - Safe to run multiple times ### `restart caddy` - Fully restarts the Caddy service - Used for major configuration changes or binary updates - May cause brief downtime ### `reload systemd` - Reloads systemd daemon configuration - Used when service files are modified ## Advanced Configuration ### 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 management handlers ├── templates/ │ ├── Caddyfile.j2 # Main config with import directive │ ├── caddy.service.j2 # Systemd service for custom builds │ ├── index.html.j2 # Default welcome page │ └── systemd-override.conf.j2 # Security hardening overrides ├── meta/main.yml # Role metadata and dependencies └── README.md # This file ``` ## Directory Structure After Deployment ``` /etc/caddy/ ├── Caddyfile # Main config with "import sites-enabled/*" └── sites-enabled/ # Service configurations ├── api.caddy # API service config ├── dashboard.caddy # Dashboard service config └── static.caddy # Static files config ``` ## Example Playbooks ### Basic Deployment ```yaml - name: Deploy Caddy Web Server hosts: webservers become: yes roles: - caddy ``` ### Infrastructure with Services ```yaml - name: Deploy Infrastructure and Services hosts: production become: yes roles: - caddy # Core web server infrastructure - myapi # API service (deploys .caddy config automatically) - mydashboard # Dashboard service (deploys .caddy config automatically) ``` ## Dependencies This role automatically handles Caddy installation and configuration: - 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 with security hardening - Sites-enabled directory structure for service configurations ## Compatibility - **Tested**: Arch Linux - **Should work**: CentOS/RHEL 8+, Ubuntu 18.04+, Debian 10+ - **Requires**: systemd, curl/wget ## Troubleshooting ### List Service Configurations ```bash ls -la /etc/caddy/sites-enabled/ ``` ### Check Configuration Files ```bash cat /etc/caddy/sites-enabled/myservice.caddy ``` ### Validate Configuration ```bash caddy validate --config /etc/caddy/Caddyfile ``` ### View Caddy Logs ```bash journalctl -u caddy -f ``` ### Reload Configuration ```bash systemctl reload caddy ``` ## Examples See other roles associated with the rick-infra project ## Additional Documentation - [Caddy Service Configuration Guide](../../docs/caddy-service-configuration.md) - Comprehensive usage guide - [Caddy Official Documentation](https://caddyserver.com/docs/) - Official Caddy documentation ## License This role is part of the rick-infra project infrastructure configuration.