Simplify Caddy infrastructure to use file-based configuration instead of complex API registration system

This commit is contained in:
2025-11-15 00:11:46 +01:00
parent 7788410bfc
commit 8162e789ee
13 changed files with 706 additions and 216 deletions

View 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
View 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

View File

@@ -1,5 +1,4 @@
# Setup guide
## Get a VPS with Arch Linux OS
- We are using [Hostinger](https://hostinger.com)
- Find it's IP in the Hostinger Dashboard
@@ -12,5 +11,5 @@
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@<VPS_IP>
```
- 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```