Simplify Caddy infrastructure to use file-based configuration instead of complex API registration system
This commit is contained in:
271
docs/caddy-service-configuration.md
Normal file
271
docs/caddy-service-configuration.md
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
# Caddy Service Configuration
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Caddy role uses a simple file-based approach where services deploy configuration files to a `sites-enabled` directory. This follows standard nginx-like patterns and uses Caddy's `import` directive for automatic configuration loading.
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- **Simple & Reliable**: File-based configuration with no API dependencies
|
||||||
|
- **Zero Downtime**: Configuration reloads without service restarts
|
||||||
|
- **Automatic HTTPS**: New domains get certificates automatically
|
||||||
|
- **Standard Pattern**: Familiar nginx-like `sites-enabled/` approach
|
||||||
|
- **Easy Debugging**: Configuration files are plaintext and easily inspected
|
||||||
|
- **Version Control Friendly**: Configuration changes are tracked in git
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||||
|
│ Service Role │ │ sites-enabled/ │ │ Caddy Core │
|
||||||
|
│ (Ansible) │───▶│ *.caddy files │───▶│ (Routes) │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Pattern
|
||||||
|
|
||||||
|
### 1. Service Role Structure
|
||||||
|
|
||||||
|
Create a service role with dependency on Caddy:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# roles/myservice/meta/main.yml
|
||||||
|
dependencies:
|
||||||
|
- role: caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Service Configuration Template
|
||||||
|
|
||||||
|
Create a Caddy configuration template:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# roles/myservice/templates/myservice.caddy.j2
|
||||||
|
{{ service_domain }} {
|
||||||
|
reverse_proxy {{ service_backend }}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Service Deployment
|
||||||
|
|
||||||
|
In your service tasks:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# roles/myservice/tasks/main.yml
|
||||||
|
- name: Deploy my service
|
||||||
|
# ... service deployment tasks
|
||||||
|
|
||||||
|
- name: Deploy Caddy configuration
|
||||||
|
template:
|
||||||
|
src: myservice.caddy.j2
|
||||||
|
dest: "{{ caddy_sites_enabled_dir }}/myservice.caddy"
|
||||||
|
owner: root
|
||||||
|
group: "{{ caddy_user }}"
|
||||||
|
mode: '0644'
|
||||||
|
notify: reload caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Advanced Configuration Example
|
||||||
|
|
||||||
|
For load balancing and custom headers:
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# roles/myapi/templates/myapi.caddy.j2
|
||||||
|
{{ service_domain }} {
|
||||||
|
reverse_proxy localhost:8080 localhost:8081 {
|
||||||
|
lb_policy round_robin
|
||||||
|
header_up Host {upstream_hostport}
|
||||||
|
header_up X-Real-IP {remote_host}
|
||||||
|
header_up X-Custom-Header "MyValue"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Service Removal
|
||||||
|
|
||||||
|
To remove a service:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Remove service configuration
|
||||||
|
file:
|
||||||
|
path: "{{ caddy_sites_enabled_dir }}/myservice.caddy"
|
||||||
|
state: absent
|
||||||
|
notify: reload caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Handlers
|
||||||
|
|
||||||
|
### `reload caddy`
|
||||||
|
- Reloads Caddy configuration without restart
|
||||||
|
- Automatically triggered when .caddy files change
|
||||||
|
- Safe to run multiple times
|
||||||
|
|
||||||
|
## Configuration Variables
|
||||||
|
|
||||||
|
### Template Variables
|
||||||
|
|
||||||
|
| Variable | Required | Description | Example |
|
||||||
|
|----------|----------|-------------|---------|
|
||||||
|
| `service_domain` | Yes | Domain for the service | `"api.jnss.me"` |
|
||||||
|
| `service_backend` | Yes | Backend address | `"localhost:8080"` |
|
||||||
|
|
||||||
|
### Caddy Configuration
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `caddy_sites_enabled_dir` | `"/etc/caddy/sites-enabled"` | Directory for service configs |
|
||||||
|
| `caddy_user` | `"caddy"` | Caddy system user |
|
||||||
|
| `caddy_config_file` | `"/etc/caddy/Caddyfile"` | Main Caddyfile path |
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- **File Permissions**: Configuration files owned by root:caddy (0644)
|
||||||
|
- **Process Isolation**: SystemD security restrictions
|
||||||
|
- **Network Isolation**: Firewall only opens 80/443
|
||||||
|
- **Configuration Validation**: Automatic syntax checking
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Simple API Service
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# roles/simple-api/templates/simple-api.caddy.j2
|
||||||
|
simple.jnss.me {
|
||||||
|
reverse_proxy localhost:3000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# roles/simple-api/tasks/main.yml
|
||||||
|
- name: Deploy simple 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
|
||||||
|
balanced.jnss.me {
|
||||||
|
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.jnss.me {
|
||||||
|
root * /var/www/static
|
||||||
|
file_server browse
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### API with Custom Headers
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# roles/api-service/templates/api-service.caddy.j2
|
||||||
|
api.jnss.me {
|
||||||
|
reverse_proxy localhost:8080 {
|
||||||
|
header_up Host {upstream_hostport}
|
||||||
|
header_up X-Real-IP {remote_host}
|
||||||
|
header_up X-Forwarded-Proto https
|
||||||
|
header_down -Server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### List Service Configurations
|
||||||
|
```bash
|
||||||
|
ls -la /etc/caddy/sites-enabled/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Configuration Files
|
||||||
|
```bash
|
||||||
|
cat /etc/caddy/sites-enabled/myservice.caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validate Caddy Configuration
|
||||||
|
```bash
|
||||||
|
caddy validate --config /etc/caddy/Caddyfile
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Configuration Changes
|
||||||
|
```bash
|
||||||
|
caddy fmt --overwrite /etc/caddy/Caddyfile
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Caddy Logs
|
||||||
|
```bash
|
||||||
|
journalctl -u caddy -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reload Configuration
|
||||||
|
```bash
|
||||||
|
systemctl reload caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Service Directly
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/health
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Through Caddy
|
||||||
|
```bash
|
||||||
|
curl https://myservice.jnss.me/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
After deployment, your configuration will look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
/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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Naming**: Use descriptive, unique configuration file names
|
||||||
|
2. **Domains**: Follow consistent domain patterns (e.g., `service.domain.com`)
|
||||||
|
3. **Templates**: Use Jinja2 variables for flexibility
|
||||||
|
4. **Health Checks**: Always include health check endpoints in services
|
||||||
|
5. **Dependencies**: Declare Caddy dependency in service role meta
|
||||||
|
6. **Testing**: Validate configuration before deployment
|
||||||
|
7. **Cleanup**: Remove .caddy files when removing services
|
||||||
|
8. **Version Control**: Track all configuration changes in git
|
||||||
|
|
||||||
|
## Service Deployment Checklist
|
||||||
|
|
||||||
|
- [ ] Create service role with Caddy dependency
|
||||||
|
- [ ] Create `.caddy.j2` template file
|
||||||
|
- [ ] Deploy template to `{{ caddy_sites_enabled_dir }}/`
|
||||||
|
- [ ] Set proper file permissions (root:caddy 0644)
|
||||||
|
- [ ] Trigger `reload caddy` handler
|
||||||
|
- [ ] Test service accessibility
|
||||||
|
- [ ] Verify HTTPS certificate generation
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues or questions about the Caddy service configuration system:
|
||||||
|
|
||||||
|
1. Check Caddy logs: `journalctl -u caddy -f`
|
||||||
|
2. Validate configuration: `caddy validate --config /etc/caddy/Caddyfile`
|
||||||
|
3. Check file permissions: `ls -la /etc/caddy/sites-enabled/`
|
||||||
|
4. Test service directly before adding to Caddy
|
||||||
|
5. Use `ansible-playbook --check` for dry-run testing
|
||||||
100
docs/deployment-guide.md
Normal file
100
docs/deployment-guide.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Deployment Guide
|
||||||
|
|
||||||
|
This guide explains how to deploy your infrastructure using the updated Caddy API registration system.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The deployment system has been restructured to support:
|
||||||
|
- **Core Infrastructure**: Caddy web server with API capabilities
|
||||||
|
- **Service Registration**: Dynamic service registration via API
|
||||||
|
- **Zero Downtime**: Services can be added/removed without restarts
|
||||||
|
|
||||||
|
## Available Playbooks
|
||||||
|
|
||||||
|
### 1. `site.yml` - Core Infrastructure
|
||||||
|
Deploys security hardening followed by Caddy web server infrastructure.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i inventory/hosts.yml site.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- **Phase 1 - Security**: System updates, SSH hardening, nftables firewall, fail2ban
|
||||||
|
- **Phase 2 - Caddy**: Installs Caddy with Cloudflare DNS plugin
|
||||||
|
- Configures TLS with Let's Encrypt
|
||||||
|
- Sets up named server for API targeting
|
||||||
|
- Enables API persistence with `--resume`
|
||||||
|
- Serves main domain (jnss.me)
|
||||||
|
|
||||||
|
## Deployment Patterns
|
||||||
|
|
||||||
|
### First-Time Deployment
|
||||||
|
|
||||||
|
⚠️ **Important**: First-time deployments include security hardening that may require a system reboot.
|
||||||
|
|
||||||
|
1. **Deploy Core Infrastructure**
|
||||||
|
```bash
|
||||||
|
# Option 1: Security + Basic infrastructure
|
||||||
|
ansible-playbook -i inventory/hosts.yml site.yml --ask-vault-pass
|
||||||
|
|
||||||
|
# Option 2: Complete deployment with comprehensive verification
|
||||||
|
ansible-playbook -i inventory/hosts.yml deploy.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The security hardening phase may:
|
||||||
|
- Update all system packages
|
||||||
|
- Reboot the system if kernel updates are applied
|
||||||
|
- Configure SSH, firewall, and fail2ban
|
||||||
|
- This ensures a secure foundation before deploying web services
|
||||||
|
|
||||||
|
## Configuration Management
|
||||||
|
|
||||||
|
### Host Variables
|
||||||
|
|
||||||
|
Core infrastructure settings in `host_vars/arch-vps/main.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# TLS Configuration
|
||||||
|
caddy_tls_enabled: true
|
||||||
|
caddy_domain: "jnss.me"
|
||||||
|
caddy_tls_email: "{{ vault_caddy_tls_email }}"
|
||||||
|
|
||||||
|
# DNS Challenge
|
||||||
|
caddy_dns_provider: "cloudflare"
|
||||||
|
cloudflare_api_token: "{{ vault_cloudflare_api_token }}"
|
||||||
|
|
||||||
|
# API Configuration
|
||||||
|
caddy_api_enabled: true
|
||||||
|
caddy_server_name: "main"
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
caddy_log_level: "INFO"
|
||||||
|
caddy_log_format: "json"
|
||||||
|
caddy_systemd_security: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vault Variables
|
||||||
|
|
||||||
|
Sensitive data in `host_vars/arch-vps/vault.yml` (encrypted):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
vault_caddy_tls_email: "admin@jnss.me"
|
||||||
|
vault_cloudflare_api_token: "your-api-token-here"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Always use vault for sensitive data
|
||||||
|
- Test deployments on staging first
|
||||||
|
- Monitor logs after deployment
|
||||||
|
- Verify HTTPS certificates are working
|
||||||
|
- Check that API is only accessible locally
|
||||||
|
|
||||||
|
### Monitoring
|
||||||
|
|
||||||
|
- Monitor Caddy logs: `journalctl -u caddy -f`
|
||||||
|
- Check API status: `curl http://localhost:2019/config/`
|
||||||
|
- Verify service health: `curl https://domain.com/health`
|
||||||
|
- Monitor certificate expiration
|
||||||
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
# Setup guide
|
# Setup guide
|
||||||
|
|
||||||
## Get a VPS with Arch Linux OS
|
## Get a VPS with Arch Linux OS
|
||||||
- We are using [Hostinger](https://hostinger.com)
|
- We are using [Hostinger](https://hostinger.com)
|
||||||
- Find it's IP in the Hostinger Dashboard
|
- Find it's IP in the Hostinger Dashboard
|
||||||
@@ -12,5 +11,5 @@
|
|||||||
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@<VPS_IP>
|
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@<VPS_IP>
|
||||||
```
|
```
|
||||||
- Add host to Ansible inventory
|
- Add host to Ansible inventory
|
||||||
- Test connection `ansible -i inventory/hosts.yml arch-vps -m ping`
|
- Test connection `ansible arch-vps -m ping`
|
||||||
- ```ansible-playbook -i inventory/hosts/yml site.yml```
|
- ```ansible-playbook -i inventory/hosts/yml site.yml```
|
||||||
|
|||||||
@@ -18,20 +18,22 @@ cloudflare_api_token: "{{ vault_cloudflare_api_token }}"
|
|||||||
caddy_acme_ca: "https://acme-v02.api.letsencrypt.org/directory"
|
caddy_acme_ca: "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# Site Configuration
|
# API Service Registration Configuration
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# For now, just serve the main jnss.me domain
|
# Services now self-register using Caddy's admin API
|
||||||
# Additional sites can be added here as services are deployed
|
caddy_api_enabled: true
|
||||||
caddy_sites: []
|
caddy_server_name: "main"
|
||||||
|
|
||||||
# Future sites will look like:
|
# Static site configuration is deprecated - use API registration instead
|
||||||
# caddy_sites:
|
# Services should use the registration handlers:
|
||||||
# - domain: "cloud.jnss.me"
|
#
|
||||||
# backend: "localhost:8080"
|
# Example service registration pattern:
|
||||||
# dns_challenge: true
|
# - name: Register my service
|
||||||
# - domain: "auth.jnss.me"
|
# set_fact:
|
||||||
# backend: "localhost:9000"
|
# service_name: "myapi"
|
||||||
# dns_challenge: true
|
# service_domain: "api.jnss.me"
|
||||||
|
# service_backend: "localhost:8080"
|
||||||
|
# notify: register service with caddy
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# Security & Logging
|
# Security & Logging
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
- name: Reboot system if kernel/module mismatch detected
|
- name: Reboot system if kernel/module mismatch detected
|
||||||
reboot:
|
reboot:
|
||||||
reboot_timeout: 60
|
reboot_timeout: 120
|
||||||
test_command: uptime
|
test_command: uptime
|
||||||
when: reboot_needed | bool
|
when: reboot_needed | bool
|
||||||
|
|
||||||
@@ -199,25 +199,6 @@
|
|||||||
changed_when: false
|
changed_when: false
|
||||||
when: nft_config_changed.changed
|
when: nft_config_changed.changed
|
||||||
|
|
||||||
- name: Create firewall rollback safety script
|
|
||||||
copy:
|
|
||||||
content: |
|
|
||||||
#!/bin/bash
|
|
||||||
# Safety rollback script - automatically disables firewall after 5 minutes
|
|
||||||
echo "$(date): Starting 5-minute firewall safety timer"
|
|
||||||
sleep 300
|
|
||||||
echo "$(date): Safety timer expired, disabling firewall"
|
|
||||||
nft flush ruleset
|
|
||||||
systemctl stop nftables
|
|
||||||
rm -f /tmp/nft-rollback.sh
|
|
||||||
dest: /tmp/nft-rollback.sh
|
|
||||||
mode: '0755'
|
|
||||||
when: nft_config_changed.changed
|
|
||||||
|
|
||||||
- name: Start rollback safety timer in background
|
|
||||||
shell: nohup /tmp/nft-rollback.sh >> /tmp/nft-rollback.log 2>&1 &
|
|
||||||
when: nft_config_changed.changed
|
|
||||||
|
|
||||||
- name: Enable and start nftables service
|
- name: Enable and start nftables service
|
||||||
systemd:
|
systemd:
|
||||||
name: nftables
|
name: nftables
|
||||||
@@ -239,10 +220,6 @@
|
|||||||
become: no
|
become: no
|
||||||
when: nft_config_changed.changed
|
when: nft_config_changed.changed
|
||||||
|
|
||||||
- name: Cancel rollback timer if SSH connection works
|
|
||||||
shell: pkill -f nft-rollback.sh || true
|
|
||||||
when: nft_config_changed.changed
|
|
||||||
|
|
||||||
- name: Verify nftables rules are loaded
|
- name: Verify nftables rules are loaded
|
||||||
command: nft list ruleset
|
command: nft list ruleset
|
||||||
register: nft_rules
|
register: nft_rules
|
||||||
@@ -330,7 +307,6 @@
|
|||||||
- { name: 'net.ipv6.conf.default.disable_ipv6', value: '0' }
|
- { name: 'net.ipv6.conf.default.disable_ipv6', value: '0' }
|
||||||
|
|
||||||
handlers:
|
handlers:
|
||||||
|
|
||||||
- name: restart fail2ban
|
- name: restart fail2ban
|
||||||
systemd:
|
systemd:
|
||||||
name: fail2ban
|
name: fail2ban
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
# Caddy Web Server Role
|
# 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.
|
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
|
## Features
|
||||||
|
|
||||||
- 🔐 **Automatic HTTPS** with Let's Encrypt certificates
|
- 🔐 **Automatic HTTPS** with Let's Encrypt certificates
|
||||||
- 🌐 **DNS Challenge Support** for wildcard certificates (Cloudflare provider)
|
- 🌐 **DNS Challenge Support** for wildcard certificates (Cloudflare provider)
|
||||||
|
- 📁 **File-Based Configuration** using sites-enabled directory pattern
|
||||||
- 🛡️ **Security Hardening** with systemd restrictions and capability bounds
|
- 🛡️ **Security Hardening** with systemd restrictions and capability bounds
|
||||||
- 🔧 **Flexible Configuration** for static sites and reverse proxies
|
- ⚡ **Zero Downtime** configuration reloads without service restarts
|
||||||
- 📝 **Production Ready** with proper logging and monitoring
|
- 🎯 **Simple Architecture** following nginx-like configuration patterns
|
||||||
- 🎯 **Role-Based Architecture** with host-specific overrides
|
- 🔄 **Git-Friendly** configuration files for version control
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -19,16 +20,9 @@ A comprehensive Ansible role for installing and configuring [Caddy](https://cadd
|
|||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### Basic Static Site Setup
|
### Infrastructure Setup (One-time)
|
||||||
|
|
||||||
```yaml
|
Configure core Caddy infrastructure in your host vars:
|
||||||
# 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
|
```yaml
|
||||||
# host_vars/myserver/main.yml
|
# host_vars/myserver/main.yml
|
||||||
@@ -43,6 +37,40 @@ vault_caddy_tls_email: "admin@example.com"
|
|||||||
vault_cloudflare_api_token: "your-api-token-here"
|
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
|
## Required Variables
|
||||||
|
|
||||||
### For HTTPS/TLS
|
### For HTTPS/TLS
|
||||||
@@ -60,6 +88,15 @@ vault_cloudflare_api_token: "your-api-token-here"
|
|||||||
| `caddy_dns_provider` | DNS challenge | DNS provider | `"cloudflare"` |
|
| `caddy_dns_provider` | DNS challenge | DNS provider | `"cloudflare"` |
|
||||||
| `cloudflare_api_token` | Cloudflare DNS | API token for DNS | `"{{ vault_token }}"` |
|
| `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
|
## Optional Variables
|
||||||
|
|
||||||
### Service Configuration
|
### Service Configuration
|
||||||
@@ -68,6 +105,7 @@ vault_cloudflare_api_token: "your-api-token-here"
|
|||||||
|----------|---------|-------------|
|
|----------|---------|-------------|
|
||||||
| `caddy_version` | `"latest"` | Caddy version to install |
|
| `caddy_version` | `"latest"` | Caddy version to install |
|
||||||
| `caddy_config_file` | `"/etc/caddy/Caddyfile"` | Main config file path |
|
| `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_enabled` | `true` | Enable systemd service |
|
||||||
| `caddy_service_state` | `"started"` | Service state |
|
| `caddy_service_state` | `"started"` | Service state |
|
||||||
| `caddy_admin_listen` | `"127.0.0.1:2019"` | Admin API endpoint |
|
| `caddy_admin_listen` | `"127.0.0.1:2019"` | Admin API endpoint |
|
||||||
@@ -93,7 +131,8 @@ vault_cloudflare_api_token: "your-api-token-here"
|
|||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|----------|---------|-------------|
|
|----------|---------|-------------|
|
||||||
| `caddy_log_level` | `"INFO"` | Logging level (ERROR, WARN, INFO, DEBUG) |
|
| `caddy_log_level` | `"INFO"` | Logging level (ERROR, WARN, INFO, DEBUG) |
|
||||||
| `caddy_log_format` | `"common"` | Log format (common, json) |
|
| `caddy_log_format` | `"json"` | Log format (common, json) |
|
||||||
|
| `caddy_log_credentials` | `false` | Log credentials in access logs |
|
||||||
|
|
||||||
### ACME Configuration
|
### ACME Configuration
|
||||||
|
|
||||||
@@ -103,31 +142,84 @@ vault_cloudflare_api_token: "your-api-token-here"
|
|||||||
| `caddy_dns_resolvers` | `["1.1.1.1:53", "1.0.0.1:53"]` | DNS resolvers |
|
| `caddy_dns_resolvers` | `["1.1.1.1:53", "1.0.0.1:53"]` | DNS resolvers |
|
||||||
| `caddy_dns_propagation_timeout` | `120` | DNS propagation timeout (seconds) |
|
| `caddy_dns_propagation_timeout` | `120` | DNS propagation timeout (seconds) |
|
||||||
|
|
||||||
## Advanced Configuration
|
## Service Configuration Patterns
|
||||||
|
|
||||||
### Multiple Sites
|
### Simple API Service
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# roles/simple-api/templates/simple-api.caddy.j2
|
||||||
|
api.example.com {
|
||||||
|
reverse_proxy localhost:3000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
caddy_sites:
|
# roles/simple-api/tasks/main.yml
|
||||||
# Static file serving
|
- name: Deploy API configuration
|
||||||
- domain: "static.example.com"
|
template:
|
||||||
root: "/var/www/static"
|
src: simple-api.caddy.j2
|
||||||
dns_challenge: true
|
dest: "{{ caddy_sites_enabled_dir }}/simple-api.caddy"
|
||||||
|
owner: root
|
||||||
# Reverse proxy to backend service
|
group: "{{ caddy_user }}"
|
||||||
- domain: "api.example.com"
|
mode: '0644'
|
||||||
backend: "localhost:8080"
|
notify: reload caddy
|
||||||
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"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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
|
### Custom Systemd Security
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -169,15 +261,27 @@ This role implements production-grade security hardening:
|
|||||||
roles/caddy/
|
roles/caddy/
|
||||||
├── defaults/main.yml # Default variables with documentation
|
├── defaults/main.yml # Default variables with documentation
|
||||||
├── tasks/main.yml # Installation and configuration tasks
|
├── tasks/main.yml # Installation and configuration tasks
|
||||||
├── handlers/main.yml # Service restart/reload handlers
|
├── handlers/main.yml # Service management handlers
|
||||||
├── templates/
|
├── templates/
|
||||||
│ ├── Caddyfile.j2 # Main Caddyfile template
|
│ ├── Caddyfile.j2 # Main config with import directive
|
||||||
|
│ ├── caddy.service.j2 # Systemd service for custom builds
|
||||||
│ ├── index.html.j2 # Default welcome page
|
│ ├── index.html.j2 # Default welcome page
|
||||||
│ └── systemd-override.conf.j2 # Security hardening overrides
|
│ └── systemd-override.conf.j2 # Security hardening overrides
|
||||||
├── meta/main.yml # Role metadata and dependencies
|
├── meta/main.yml # Role metadata and dependencies
|
||||||
└── README.md # This file
|
└── 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
|
## Example Playbooks
|
||||||
|
|
||||||
### Basic Deployment
|
### Basic Deployment
|
||||||
@@ -190,25 +294,27 @@ roles/caddy/
|
|||||||
- caddy
|
- caddy
|
||||||
```
|
```
|
||||||
|
|
||||||
### Full Infrastructure
|
### Infrastructure with Services
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Secure VPS Setup
|
- name: Deploy Infrastructure and Services
|
||||||
hosts: production
|
hosts: production
|
||||||
become: yes
|
become: yes
|
||||||
roles:
|
roles:
|
||||||
- security # Firewall, SSH hardening, etc.
|
- caddy # Core web server infrastructure
|
||||||
- caddy # Web server with HTTPS
|
- myapi # API service (deploys .caddy config automatically)
|
||||||
|
- mydashboard # Dashboard service (deploys .caddy config automatically)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
This role automatically handles Caddy installation, including:
|
This role automatically handles Caddy installation and configuration:
|
||||||
|
|
||||||
- Standard Caddy binary for HTTP-only setups
|
- Standard Caddy binary for HTTP-only setups
|
||||||
- Custom build with Cloudflare DNS plugin when DNS challenge is enabled
|
- Custom build with Cloudflare DNS plugin when DNS challenge is enabled
|
||||||
- System user and directory creation
|
- System user and directory creation
|
||||||
- Systemd service configuration
|
- Systemd service configuration with security hardening
|
||||||
|
- Sites-enabled directory structure for service configurations
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
@@ -216,21 +322,40 @@ This role automatically handles Caddy installation, including:
|
|||||||
- **Should work**: CentOS/RHEL 8+, Ubuntu 18.04+, Debian 10+
|
- **Should work**: CentOS/RHEL 8+, Ubuntu 18.04+, Debian 10+
|
||||||
- **Requires**: systemd, curl/wget
|
- **Requires**: systemd, curl/wget
|
||||||
|
|
||||||
## Contributing
|
## Troubleshooting
|
||||||
|
|
||||||
When modifying this role:
|
### List Service Configurations
|
||||||
|
```bash
|
||||||
|
ls -la /etc/caddy/sites-enabled/
|
||||||
|
```
|
||||||
|
|
||||||
1. Update defaults in `defaults/main.yml` with clear documentation
|
### Check Configuration Files
|
||||||
2. Use host-specific overrides in `host_vars/` for sensitive values
|
```bash
|
||||||
3. Leverage Ansible Vault for secrets (`vault_*` variables)
|
cat /etc/caddy/sites-enabled/myservice.caddy
|
||||||
4. Test changes against the security hardening requirements
|
```
|
||||||
|
|
||||||
## Security Considerations
|
### Validate Configuration
|
||||||
|
```bash
|
||||||
|
caddy validate --config /etc/caddy/Caddyfile
|
||||||
|
```
|
||||||
|
|
||||||
- **Secrets**: Always use Ansible Vault for API tokens and sensitive data
|
### View Caddy Logs
|
||||||
- **Firewall**: Role opens ports 80 and 443; ensure firewall is configured
|
```bash
|
||||||
- **DNS**: DNS challenge requires API access to your DNS provider
|
journalctl -u caddy -f
|
||||||
- **Monitoring**: Monitor certificate expiration and renewal
|
```
|
||||||
|
|
||||||
|
### 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
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
# Basic Installation Configuration
|
# Basic Installation Configuration
|
||||||
# =================================================================
|
# =================================================================
|
||||||
caddy_version: "latest"
|
caddy_version: "latest"
|
||||||
caddy_user: "caddy"
|
caddy_user: "caddy"
|
||||||
caddy_group: "caddy"
|
|
||||||
caddy_home: "/var/lib/caddy"
|
caddy_home: "/var/lib/caddy"
|
||||||
caddy_config_dir: "/etc/caddy"
|
caddy_config_dir: "/etc/caddy"
|
||||||
caddy_data_dir: "/var/lib/caddy"
|
caddy_data_dir: "/var/lib/caddy"
|
||||||
@@ -22,6 +21,7 @@ caddy_default_site_root: "{{ caddy_web_root }}/default"
|
|||||||
# Service Configuration
|
# Service Configuration
|
||||||
# =================================================================
|
# =================================================================
|
||||||
caddy_config_file: "/etc/caddy/Caddyfile" # Package default path
|
caddy_config_file: "/etc/caddy/Caddyfile" # Package default path
|
||||||
|
caddy_sites_enabled_dir: "/etc/caddy/sites-enabled" # Directory for service configurations
|
||||||
caddy_service_enabled: true
|
caddy_service_enabled: true
|
||||||
caddy_service_state: "started"
|
caddy_service_state: "started"
|
||||||
caddy_auto_https: true
|
caddy_auto_https: true
|
||||||
@@ -53,30 +53,10 @@ caddy_dns_resolvers: # DNS resolvers for challenge verifi
|
|||||||
caddy_dns_propagation_timeout: 120 # Seconds to wait for DNS propagation
|
caddy_dns_propagation_timeout: 120 # Seconds to wait for DNS propagation
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# Sites Configuration
|
# Service Configuration
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# Define additional sites/domains to serve
|
# File-based service configuration using import directive
|
||||||
caddy_sites: []
|
# Services deploy .caddy files to sites-enabled directory
|
||||||
|
|
||||||
# Example configurations:
|
|
||||||
# caddy_sites:
|
|
||||||
# # Static file serving
|
|
||||||
# - domain: "static.example.com"
|
|
||||||
# root: "/var/www/static"
|
|
||||||
# dns_challenge: true # Use DNS challenge for this domain
|
|
||||||
#
|
|
||||||
# # 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}
|
|
||||||
#
|
|
||||||
# # Simple HTTP-only site
|
|
||||||
# - domain: "internal.example.com"
|
|
||||||
# root: "/var/www/internal"
|
|
||||||
# tls: "off"
|
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# Security & Network Configuration
|
# Security & Network Configuration
|
||||||
@@ -92,4 +72,5 @@ caddy_firewall_ports:
|
|||||||
# Systemd service customization
|
# Systemd service customization
|
||||||
caddy_systemd_security: true # Enable systemd security restrictions
|
caddy_systemd_security: true # Enable systemd security restrictions
|
||||||
caddy_log_level: "INFO" # Logging level (ERROR, WARN, INFO, DEBUG)
|
caddy_log_level: "INFO" # Logging level (ERROR, WARN, INFO, DEBUG)
|
||||||
caddy_log_format: "common" # Log format (common, json)
|
caddy_log_format: "json" # Log format (common, json)
|
||||||
|
caddy_log_credentials: false # Log credentials in access logs (security risk)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
# Simple service management handlers
|
||||||
- name: reload systemd
|
- name: reload systemd
|
||||||
systemd:
|
systemd:
|
||||||
daemon_reload: yes
|
daemon_reload: yes
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
- name: Create caddy user and group
|
- name: Create caddy user and group
|
||||||
user:
|
user:
|
||||||
name: "{{ caddy_user }}"
|
name: "{{ caddy_user }}"
|
||||||
group: "{{ caddy_group }}"
|
|
||||||
home: "{{ caddy_home }}"
|
home: "{{ caddy_home }}"
|
||||||
shell: /usr/bin/nologin
|
shell: /usr/bin/nologin
|
||||||
system: yes
|
system: yes
|
||||||
@@ -47,10 +46,11 @@
|
|||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
state: directory
|
state: directory
|
||||||
owner: "{{ caddy_user }}"
|
owner: "{{ caddy_user }}"
|
||||||
group: "{{ caddy_group }}"
|
group: "{{ caddy_user }}"
|
||||||
mode: '0755'
|
mode: '0755'
|
||||||
loop:
|
loop:
|
||||||
- "{{ caddy_config_dir }}"
|
- "{{ caddy_config_dir }}"
|
||||||
|
- "{{ caddy_sites_enabled_dir }}"
|
||||||
- "{{ caddy_data_dir }}"
|
- "{{ caddy_data_dir }}"
|
||||||
- "{{ caddy_log_dir }}"
|
- "{{ caddy_log_dir }}"
|
||||||
- "{{ caddy_web_root }}"
|
- "{{ caddy_web_root }}"
|
||||||
@@ -61,22 +61,34 @@
|
|||||||
src: index.html.j2
|
src: index.html.j2
|
||||||
dest: "{{ caddy_default_site_root }}/index.html"
|
dest: "{{ caddy_default_site_root }}/index.html"
|
||||||
owner: "{{ caddy_user }}"
|
owner: "{{ caddy_user }}"
|
||||||
group: "{{ caddy_group }}"
|
group: "{{ caddy_user }}"
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- name: Create systemd override directory
|
- name: Create systemd service file for custom Caddy installation
|
||||||
|
template:
|
||||||
|
src: caddy.service.j2
|
||||||
|
dest: /usr/lib/systemd/system/caddy.service
|
||||||
|
mode: '0644'
|
||||||
|
when: dns_challenge_needed | bool
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
- restart caddy
|
||||||
|
|
||||||
|
- name: Create systemd override directory (for standard installation)
|
||||||
file:
|
file:
|
||||||
path: /etc/systemd/system/caddy.service.d
|
path: /etc/systemd/system/caddy.service.d
|
||||||
state: directory
|
state: directory
|
||||||
mode: '0755'
|
mode: '0755'
|
||||||
|
when: not dns_challenge_needed | bool
|
||||||
|
|
||||||
- name: Configure Caddy systemd override
|
- name: Configure Caddy systemd override (for standard installation)
|
||||||
template:
|
template:
|
||||||
src: systemd-override.conf.j2
|
src: systemd-override.conf.j2
|
||||||
dest: /etc/systemd/system/caddy.service.d/override.conf
|
dest: /etc/systemd/system/caddy.service.d/override.conf
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
|
when: not dns_challenge_needed | bool
|
||||||
notify:
|
notify:
|
||||||
- reload systemd
|
- reload systemd
|
||||||
- restart caddy
|
- restart caddy
|
||||||
@@ -86,7 +98,7 @@
|
|||||||
src: Caddyfile.j2
|
src: Caddyfile.j2
|
||||||
dest: "{{ caddy_config_file }}"
|
dest: "{{ caddy_config_file }}"
|
||||||
owner: root
|
owner: root
|
||||||
group: "{{ caddy_group }}"
|
group: "{{ caddy_user }}"
|
||||||
mode: '0640'
|
mode: '0640'
|
||||||
backup: yes
|
backup: yes
|
||||||
notify: reload caddy
|
notify: reload caddy
|
||||||
|
|||||||
@@ -3,24 +3,30 @@
|
|||||||
|
|
||||||
# Global configuration
|
# Global configuration
|
||||||
{
|
{
|
||||||
admin {{ caddy_admin_listen }}
|
|
||||||
|
|
||||||
{% if caddy_tls_enabled and caddy_tls_email %}
|
{% if caddy_tls_enabled and caddy_tls_email %}
|
||||||
# ACME configuration for Let's Encrypt
|
# ACME configuration for Let's Encrypt
|
||||||
email {{ caddy_tls_email }}
|
email {{ caddy_tls_email }}
|
||||||
acme_ca {{ caddy_acme_ca }}
|
acme_ca {{ caddy_acme_ca }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if caddy_dns_provider == "cloudflare" and cloudflare_api_token %}
|
||||||
|
# DNS challenge for wildcard certificates
|
||||||
|
acme_dns cloudflare {{ cloudflare_api_token }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if not caddy_auto_https %}
|
{% if not caddy_auto_https %}
|
||||||
auto_https off
|
auto_https off
|
||||||
{% endif %}
|
{% endif %}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Import service configurations
|
||||||
|
import {{ caddy_sites_enabled_dir }}/*
|
||||||
|
|
||||||
# Primary domain: {{ caddy_domain }}
|
# Primary domain: {{ caddy_domain }}
|
||||||
{{ caddy_domain }} {
|
{{ caddy_domain }} {
|
||||||
{% if caddy_tls_enabled %}
|
{% if caddy_tls_enabled %}
|
||||||
{% if caddy_dns_provider == "cloudflare" and cloudflare_api_token %}
|
{% if caddy_dns_provider == "cloudflare" and cloudflare_api_token %}
|
||||||
# DNS challenge for automatic TLS (secure: no environment files)
|
# DNS challenge for automatic TLS
|
||||||
tls {
|
tls {
|
||||||
dns cloudflare {{ cloudflare_api_token }}
|
dns cloudflare {{ cloudflare_api_token }}
|
||||||
resolvers {{ caddy_dns_resolvers | join(' ') }}
|
resolvers {{ caddy_dns_resolvers | join(' ') }}
|
||||||
@@ -29,8 +35,6 @@
|
|||||||
# HTTP challenge for automatic TLS
|
# HTTP challenge for automatic TLS
|
||||||
tls {{ caddy_tls_email }}
|
tls {{ caddy_tls_email }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
|
||||||
# TLS disabled
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
# Serve static content
|
# Serve static content
|
||||||
@@ -57,83 +61,3 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Additional configured sites
|
|
||||||
{% for site in caddy_sites %}
|
|
||||||
{{ site.domain }}{% if site.port is defined %}:{{ site.port }}{% endif %} {
|
|
||||||
{% if caddy_tls_enabled and site.tls != "off" %}
|
|
||||||
{% if site.dns_challenge | default(false) and caddy_dns_provider == "cloudflare" and cloudflare_api_token %}
|
|
||||||
# DNS challenge for this site (secure: direct variable substitution)
|
|
||||||
tls {
|
|
||||||
dns cloudflare {{ cloudflare_api_token }}
|
|
||||||
resolvers {{ caddy_dns_resolvers | join(' ') }}
|
|
||||||
}
|
|
||||||
{% elif caddy_tls_email and site.tls != "off" %}
|
|
||||||
# HTTP challenge for this site
|
|
||||||
tls {{ caddy_tls_email }}
|
|
||||||
{% endif %}
|
|
||||||
{% elif site.tls == "off" %}
|
|
||||||
# TLS explicitly disabled for this site
|
|
||||||
tls off
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if site.root is defined %}
|
|
||||||
# Static file serving
|
|
||||||
root * {{ site.root }}
|
|
||||||
file_server
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if site.backend is defined %}
|
|
||||||
# Reverse proxy
|
|
||||||
reverse_proxy {{ site.backend }} {
|
|
||||||
# Standard proxy headers
|
|
||||||
header_up Host {upstream_hostport}
|
|
||||||
header_up X-Real-IP {remote_host}
|
|
||||||
header_up X-Forwarded-For {remote_host}
|
|
||||||
header_up X-Forwarded-Proto {scheme}
|
|
||||||
header_up X-Forwarded-Host {host}
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
# Logging for this site
|
|
||||||
log {
|
|
||||||
{% if caddy_log_format == "json" %}
|
|
||||||
output file {{ caddy_log_dir }}/{{ site.domain | replace('.', '_') }}.log {
|
|
||||||
roll_size 100mb
|
|
||||||
roll_keep 5
|
|
||||||
}
|
|
||||||
format json {
|
|
||||||
time_format "2006-01-02T15:04:05.000Z07:00"
|
|
||||||
}
|
|
||||||
level {{ caddy_log_level }}
|
|
||||||
{% else %}
|
|
||||||
output file {{ caddy_log_dir }}/{{ site.domain | replace('.', '_') }}.log {
|
|
||||||
roll_size 100mb
|
|
||||||
roll_keep 5
|
|
||||||
}
|
|
||||||
level {{ caddy_log_level }}
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
|
|
||||||
{% if site.extra_config is defined %}
|
|
||||||
# Additional site configuration
|
|
||||||
{{ site.extra_config | indent(4) }}
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% if caddy_tls_enabled %}
|
|
||||||
# HTTP to HTTPS redirects
|
|
||||||
http://{{ caddy_domain }} {
|
|
||||||
redir https://{host}{uri} permanent
|
|
||||||
}
|
|
||||||
|
|
||||||
{% for site in caddy_sites %}
|
|
||||||
{% if site.tls != "off" %}
|
|
||||||
http://{{ site.domain }} {
|
|
||||||
redir https://{host}{uri} permanent
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
69
roles/caddy/templates/caddy.service.j2
Normal file
69
roles/caddy/templates/caddy.service.j2
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Custom Caddy systemd service file
|
||||||
|
# Combines official Caddy service with enhanced security and API persistence
|
||||||
|
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Caddy web server
|
||||||
|
Documentation=https://caddyserver.com/docs/
|
||||||
|
After=network.target network-online.target
|
||||||
|
Requires=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=notify
|
||||||
|
User={{ caddy_user }}
|
||||||
|
Group={{ caddy_user }}
|
||||||
|
ExecStart=/usr/bin/caddy run --environ --config {{ caddy_config_file }}{% if caddy_api_enabled %} --resume{% endif %}
|
||||||
|
|
||||||
|
ExecReload=/usr/bin/caddy reload --config {{ caddy_config_file }} --force
|
||||||
|
|
||||||
|
{% if caddy_api_enabled %}
|
||||||
|
# Wait for API to be ready before considering service started
|
||||||
|
ExecStartPost=/bin/bash -c 'until curl -s http://{{ caddy_admin_listen }}/config/ >/dev/null 2>&1; do sleep 1; done'
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Standard settings from official service
|
||||||
|
TimeoutStopSec=5s
|
||||||
|
LimitNOFILE=1048576
|
||||||
|
PrivateTmp=true
|
||||||
|
|
||||||
|
{% if caddy_systemd_security | default(true) %}
|
||||||
|
# Enhanced security hardening
|
||||||
|
NoNewPrivileges=true
|
||||||
|
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
|
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
|
RemoveIPC=true
|
||||||
|
|
||||||
|
# Filesystem restrictions (upgrade from ProtectSystem=full)
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=true
|
||||||
|
ReadWritePaths={{ caddy_data_dir }} {{ caddy_log_dir }}
|
||||||
|
BindReadOnlyPaths={{ caddy_config_dir }}
|
||||||
|
ProtectKernelTunables=true
|
||||||
|
ProtectKernelModules=true
|
||||||
|
ProtectKernelLogs=true
|
||||||
|
ProtectClock=true
|
||||||
|
|
||||||
|
# Network and namespace restrictions
|
||||||
|
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||||
|
RestrictNamespaces=true
|
||||||
|
RestrictRealtime=true
|
||||||
|
RestrictSUIDSGID=true
|
||||||
|
|
||||||
|
# Process restrictions
|
||||||
|
LimitNPROC=1048576
|
||||||
|
MemoryDenyWriteExecute=true
|
||||||
|
SystemCallFilter=@system-service
|
||||||
|
SystemCallErrorNumber=EPERM
|
||||||
|
|
||||||
|
# Logging (explicit configuration)
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=caddy
|
||||||
|
{% else %}
|
||||||
|
# Standard security from official service
|
||||||
|
ProtectSystem=full
|
||||||
|
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
[Service]
|
[Service]
|
||||||
# Reload configuration with --force flag for reliability
|
ExecStart=
|
||||||
|
ExecStart=/usr/bin/caddy run --config {{ caddy_config_file }} --resume
|
||||||
|
|
||||||
ExecReload=
|
ExecReload=
|
||||||
ExecReload=/usr/bin/caddy reload --config {{ caddy_config_file }} --force
|
ExecReload=/usr/bin/caddy reload --config {{ caddy_config_file }} --force
|
||||||
|
|
||||||
|
# Wait for API to be ready before considering service started
|
||||||
|
ExecStartPost=/bin/bash -c 'until curl -s http://{{ caddy_admin_listen }}/config/ >/dev/null 2>&1; do sleep 1; done'
|
||||||
|
|
||||||
{% if caddy_systemd_security | default(true) %}
|
{% if caddy_systemd_security | default(true) %}
|
||||||
# Enhanced security hardening beyond base service
|
# Enhanced security hardening beyond base service
|
||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
@@ -35,4 +40,4 @@ SystemCallErrorNumber=EPERM
|
|||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
SyslogIdentifier=caddy
|
SyslogIdentifier=caddy
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
35
site.yml
35
site.yml
@@ -1,13 +1,38 @@
|
|||||||
---
|
---
|
||||||
- name: Secure VPS Infrastructure Setup
|
# Core infrastructure deployment with security hardening first
|
||||||
|
|
||||||
|
# Security hardening establishes secure foundation before web services
|
||||||
|
- import_playbook: playbooks/security.yml
|
||||||
|
|
||||||
|
- name: Deploy Core Infrastructure
|
||||||
hosts: arch-vps
|
hosts: arch-vps
|
||||||
become: yes
|
become: yes
|
||||||
gather_facts: yes
|
gather_facts: yes
|
||||||
|
|
||||||
roles:
|
roles:
|
||||||
- role: caddy
|
- role: caddy
|
||||||
tags: ['caddy', 'web', 'https']
|
tags: ['caddy', 'infrastructure', 'web']
|
||||||
|
|
||||||
# Optional: Include security playbook
|
post_tasks:
|
||||||
# - import_playbook: playbooks/security.yml
|
- name: Verify Caddy API is accessible
|
||||||
# tags: ['security', 'firewall', 'ssh']
|
uri:
|
||||||
|
url: "http://{{ caddy_admin_listen }}/config/"
|
||||||
|
method: GET
|
||||||
|
status_code: 200
|
||||||
|
retries: 5
|
||||||
|
delay: 2
|
||||||
|
|
||||||
|
- name: Display infrastructure status
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
✅ Core infrastructure deployment completed!
|
||||||
|
|
||||||
|
🌐 Primary domain: {{ caddy_domain }}
|
||||||
|
🔒 HTTPS: {{ 'Enabled with DNS challenge (' + caddy_dns_provider + ')' if caddy_dns_provider else 'Enabled with HTTP challenge' }}
|
||||||
|
🚀 API registration: {{ 'Ready' if caddy_api_enabled else 'Disabled' }}
|
||||||
|
|
||||||
|
📍 Admin API: http://{{ caddy_admin_listen }} (localhost only)
|
||||||
|
📁 Web root: {{ caddy_web_root }}
|
||||||
|
📝 Logs: {{ caddy_log_dir }}
|
||||||
|
|
||||||
|
📖 Documentation: docs/caddy-api-registration.md
|
||||||
|
|||||||
Reference in New Issue
Block a user