363 lines
10 KiB
Markdown
363 lines
10 KiB
Markdown
# 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.
|