Files
k3s/.claude/skills/caddy/SKILL.md
2026-02-05 00:11:05 +08:00

390 lines
8.3 KiB
Markdown

---
name: caddy-ssl-termination
description: 专门用于 Traefik 前置 Caddy 进行 SSL 卸载的架构配置,适用于 K3s 环境。
---
# Caddy SSL Termination Skill
## Architecture Overview
**Setup**: Traefik (routing) → Caddy (HTTPS/SSL termination) → HTTP backend
- **Caddy**: Handles HTTPS (443) with automatic SSL certificates, forwards to Traefik on HTTP (80)
- **Traefik**: Routes HTTP traffic to appropriate backend services
- **Flow**: Internet → Caddy:443 (HTTPS) → Traefik:80 (HTTP) → Backend Pods
## Quick Configuration Template
### 1. Basic Caddyfile Structure
```caddy
# /etc/caddy/Caddyfile
# Domain configuration
example.com {
reverse_proxy traefik-service:80
}
# Multiple domains
app1.example.com {
reverse_proxy traefik-service:80
}
app2.example.com {
reverse_proxy traefik-service:80
}
# Wildcard subdomain (requires DNS wildcard)
*.example.com {
reverse_proxy traefik-service:80
}
```
### 2. ConfigMap for Caddyfile
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: caddy-config
namespace: default
data:
Caddyfile: |
# Global options
{
email your-email@example.com
# Use Let's Encrypt staging for testing
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
# Your domains
example.com {
reverse_proxy traefik-service:80 {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Proto {scheme}
}
}
```
### 3. Caddy Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: caddy
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: caddy
template:tadata:
labels:
app: caddy
spec:
containers:
- name: caddy
image: caddy:latest
ports:
- containerPort: 80
- containerPort: 443
- containerPort: 2019 # Admin API
volumeMounts:
- name: config
mountPath: /etc/caddy
- name: data
mountPath: /data
- name: config-cache
mountPath: /config
volumes:
- name: config
configMap:
name: caddy-config
- name: data
persistentVolumeClaim:
claimName: caddy-data
- name: config-cache
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: caddy
namespace: default
spec:
type: LoadBalancer # or NodePort
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 443
selector:
app: caddy
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: caddy-data
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
```
## Common Operations
### Reload Configuration
After updating the ConfigMap:
```bash
# Method 1: Reload via exec (preserves connections)
kubectl exec -n default deployment/caddy -- caddy reload --config /etc/caddy/Caddyfile
# Method 2: Restart pod (brief downtime)
kubectl rollout restart deployment/caddy -n default
# Method 3: Delete pod (auto-recreates)
kubectl delete pod -n default -l app=caddy
```
### Update Caddyfile
```bash
# Edit ConfigMap
kubectl edit configmap caddy-config -n default
# Or apply updated file
kubectl apply -f caddy-configmap.yaml
# Then reload
kubectl exec -n default deployment/caddy -- caddy reload --config /etc/caddy/Caddyfile
```
### View Logs
```bash
# Follow logs
kubectl logs -n default -f deployment/caddy
# Check SSL certificate issues
kubectl logs -n default deployment/caddy | grep -i "certificate\|acme\|tls"
```
### Check Configuration
```bash
# Validate Caddyfile syntax
kubectl exec -n default deployment/caddy -- caddy validate --config /etc/caddy/Caddyfile
# View current config via API
kubectl exec -n default deployment/caddy -- curl localhost:2019/config/
```
## Adding New Domain
### Step-by-step Process
1. **Update DNS**: Point new domain to Caddy's LoadBalancer IP
```bash
kubectl get svc caddy -n default -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
```
2. **Update ConfigMap**: Add new domain block
```bash
kubectl edit configmap caddy-config -n default
```
Add:
```caddy
newapp.example.com {
reverse_proxy traefik-service:80 {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Proto {scheme}
}
}
```
3. **Reload Caddy**:
```bash
kubectl exec -n default deployment/caddy -- caddy reload --config /etc/caddy/Caddyfile
```
4. **Verify**: Check logs for certificate acquisition
```bash
kubectl logs -n default deployment/caddy | tail -20
```
## Traefik Integration
### Traefik IngressRoute Example
```yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: myapp
namespace: default
spec:
entryPoints:
- web # HTTP only, Caddy handles HTTPS
routes:
- match: Host(`myapp.example.com`)
kind: Rule
services:
- name: myapp-service
port: 8080
```
### Important Notes
- Traefik should listen on HTTP (80) only
- Caddy handles all HTTPS/SSL
- Use `Host()` matcher in Traefik to route by domain
- Caddy forwards the original `Host` header to Traefik
## Troubleshooting
### SSL Certificate Issues
```bash
# Check certificate status
kubectl exec -n default deployment/caddy -- caddy list-certificates
# View ACME logs
kubectl logs -n default deployment/caddy | grep -i acme
# Common issues:
# - Port 80/443 not accessible from internet
# - DNS not pointing to correct IP
# - Rate limit hit (use staging CA for testing)
```
### Configuration Errors
```bash
# Test config before reload
kubectl exec -n default deployment/caddy -- caddy validate --config /etc/caddy/Caddyfile
# Check for syntax errors
kubectl logs -n default deployment/caddy | grep -i error
```
### Connection Issues
```bash
# Test from inside cluster
kubectl run -it --rm debug --image=curlimages/curl --restart=Never -- curl -v http://traefik-service:80
# Check if Caddy can reach Traefik
kubectl exec -n default deployment/caddy -- curl -v http://traefik-service:80
```
## Advanced Configurations
### Custom TLS Settings
```caddy
example.com {
tls {
protocols tls1.2 tls1.3
ciphers TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
}
reverse_proxy traefik-service:80
}
```
### Rate Limiting
```caddy
example.com {
rate_limit {
zone dynamic {
key {remote_host}
events 100
window 1m
}
}
reverse_proxy traefik-service:80
}
```
### Custom Error Pages
```caddy
example.com {
handle_errors {
respond "{err.status_code} {err.status_text}"
}
reverse_proxy traefik-service:80
}
```
### Health Checks
```caddy
example.com {
reverse_proxy traefik-service:80 {
health_uri /health
health_interval 10s
health_timeout 5s
}
}
```
## Quick Reference Commands
```bash
# Get Caddy pod name
kubectl get pods -n default -l app=caddy
# Reload config
kubectl exec -n default deployment/caddy -- caddy reload --config /etc/caddy/Caddyfile
# View current config
kubectl exec -n default deployment/caddy -- cat /etc/caddy/Caddyfile
# Check certificates
kubectl exec -n default deployment/caddy -- caddy list-certificates
# Restart Caddy
kubectl rollout restart deployment/caddy -n default
# Watch logs
kubectl logs -n default -f deployment/caddy
# Get LoadBalancer IP
kubectl get svc caddy -n default -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
```
## Checklist for New Service
- [ ] DNS A/AAAA record points to Caddy LoadBalancer IP
- [ ] Domain added to Caddy ConfigMap
- [ ] Caddy reloaded successfully
- [ ] Traefik IngressRoute created (HTTP only)
- [ ] Backend service accessible from Traefik
- [ ] SSL certificate acquired (check logs)
- [ ] HTTPS access working from browser
- [ ] HTTP redirects to HTTPS (Caddy default behavior)
## Notes
- Caddy automatically obtains and renews SSL certificates
- Certificates stored in `/data` volume (must be persistent)
- Reload is graceful - no connection drops
- Caddy handles HTTP→HTTPS redirect automatically
- Use staging CA for testing to avoid rate limits
- Wildcard certificates require DNS challenge (more complex setup)