- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
14 KiB
SSL/TLS Certificate Setup with Let's Encrypt
Automatische SSL/TLS-Zertifikat-Verwaltung mit Let's Encrypt und Certbot für Production Deployment.
Übersicht
Das Framework nutzt Let's Encrypt für kostenlose, automatisch erneuernde SSL/TLS-Zertifikate via Certbot im Docker-Container.
Features:
- Automatische Zertifikat-Ausstellung
- Automatische Erneuerung alle 12 Stunden
- Wildcard-Zertifikate möglich (DNS-01 Challenge)
- Zero-Downtime Erneuerung
- Nginx-Integration via Shared Volumes
Architektur
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Certbot │───▶│ Let's Encrypt │◀───│ ACME Server │
│ Container │ │ Certificates │ │ (HTTP-01) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Shared Volumes: /etc/letsencrypt, /var/www/certbot │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────┐
│ Nginx Web │
│ Container │
└─────────────────┘
Voraussetzungen
DNS-Konfiguration
Bevor SSL-Zertifikate ausgestellt werden können:
- A Record muss auf Server-IP zeigen:
# Prüfen
dig +short example.com
# Erwartetes Ergebnis: Ihre Server-IP (z.B. 203.0.113.42)
- AAAA Record für IPv6 (optional):
dig +short example.com AAAA
- CAA Record für Let's Encrypt (empfohlen):
example.com. CAA 0 issue "letsencrypt.org"
example.com. CAA 0 issuewild "letsencrypt.org"
Firewall-Konfiguration
Port 80 und 443 müssen offen sein:
# UFW
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Iptables
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
Erste Zertifikat-Ausstellung
Schritt 1: Nginx-Konfiguration für ACME Challenge
Erstelle temporäre Nginx-Config für initiale Zertifikat-Ausstellung:
docker/nginx/conf.d/certbot-challenge.conf:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# ACME Challenge für Let's Encrypt
location /.well-known/acme-challenge/ {
root /var/www/certbot;
try_files $uri =404;
}
# Redirect alle anderen Requests zu HTTPS (nach Zertifikat-Ausstellung)
location / {
return 301 https://$host$request_uri;
}
}
Schritt 2: Initiales Certbot-Setup
Starte nur Web und Certbot Container für initiale Zertifikat-Ausstellung:
# 1. Erstelle Certbot-Verzeichnisse
mkdir -p ./certbot/{conf,www,logs}
# 2. Starte nur Nginx für ACME Challenge
docker-compose up -d web
# 3. Teste ACME Challenge Endpoint
curl -I http://example.com/.well-known/acme-challenge/test
# Sollte 404 zurückgeben (Endpoint erreichbar)
# 4. Initiale Zertifikat-Ausstellung (dry-run)
docker run --rm \
-v $(pwd)/certbot/conf:/etc/letsencrypt \
-v $(pwd)/certbot/www:/var/www/certbot \
-v $(pwd)/certbot/logs:/var/log/letsencrypt \
certbot/certbot:latest certonly \
--webroot \
--webroot-path=/var/www/certbot \
--email admin@example.com \
--agree-tos \
--no-eff-email \
--dry-run \
-d example.com \
-d www.example.com
# 5. Echte Zertifikat-Ausstellung (ohne --dry-run)
docker run --rm \
-v $(pwd)/certbot/conf:/etc/letsencrypt \
-v $(pwd)/certbot/www:/var/www/certbot \
-v $(pwd)/certbot/logs:/var/log/letsencrypt \
certbot/certbot:latest certonly \
--webroot \
--webroot-path=/var/www/certbot \
--email admin@example.com \
--agree-tos \
--no-eff-email \
-d example.com \
-d www.example.com
Output bei Erfolg:
Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/example.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/example.com/privkey.pem
Your cert will expire on 2025-04-15.
Schritt 3: Nginx HTTPS-Konfiguration
Nach erfolgreicher Zertifikat-Ausstellung, aktiviere HTTPS:
docker/nginx/conf.d/default.conf (Production):
# HTTP Server - Redirect zu HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# ACME Challenge für Zertifikat-Erneuerung
location /.well-known/acme-challenge/ {
root /var/www/certbot;
try_files $uri =404;
}
# Redirect zu HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS Server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL Zertifikate
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL Konfiguration (Mozilla Modern)
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# Security Headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Application
root /var/www/html/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Schritt 4: Starte Production Stack
# Mit Certbot für automatische Erneuerung
docker-compose -f docker-compose.yml -f docker-compose.production.yml up -d
# Prüfe Logs
docker-compose logs -f certbot
# Teste HTTPS
curl -I https://example.com
Automatische Erneuerung
Der Certbot-Container erneuert Zertifikate automatisch alle 12 Stunden:
docker-compose.production.yml:
certbot:
image: certbot/certbot:latest
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot --quiet; sleep 12h & wait $${!}; done;'"
volumes:
- certbot-conf:/etc/letsencrypt
- certbot-www:/var/www/certbot
- certbot-logs:/var/log/letsencrypt
Manuelle Erneuerung:
# Test Renewal (dry-run)
docker-compose exec certbot certbot renew --dry-run
# Force Renewal (vor Ablauf)
docker-compose exec certbot certbot renew --force-renewal
# Nginx Reload nach Erneuerung
docker-compose exec web nginx -s reload
Wildcard-Zertifikate (DNS-01 Challenge)
Für Wildcard-Zertifikate (*.example.com) ist DNS-01 Challenge erforderlich:
Voraussetzungen
- DNS-Provider API-Zugang (Cloudflare, Route53, etc.)
- Certbot DNS Plugin installiert
Beispiel: Cloudflare
docker-compose.production.yml (erweitert):
certbot:
image: certbot/dns-cloudflare:latest
environment:
- CLOUDFLARE_EMAIL=admin@example.com
- CLOUDFLARE_API_KEY=${CLOUDFLARE_API_KEY}
volumes:
- certbot-conf:/etc/letsencrypt
- ./certbot/cloudflare.ini:/cloudflare.ini:ro
command: certonly --dns-cloudflare --dns-cloudflare-credentials /cloudflare.ini -d example.com -d *.example.com
certbot/cloudflare.ini:
dns_cloudflare_email = admin@example.com
dns_cloudflare_api_key = your_cloudflare_api_key
Ausstellung:
docker run --rm \
-v $(pwd)/certbot/conf:/etc/letsencrypt \
-v $(pwd)/certbot/cloudflare.ini:/cloudflare.ini:ro \
certbot/dns-cloudflare:latest certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /cloudflare.ini \
--email admin@example.com \
--agree-tos \
--no-eff-email \
-d example.com \
-d *.example.com
Zertifikat-Monitoring
Ablaufdatum prüfen
# Via OpenSSL
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Via Certbot
docker-compose exec certbot certbot certificates
# Output:
# Certificate Name: example.com
# Expiry Date: 2025-04-15 12:34:56+00:00 (VALID: 89 days)
Automatisches Monitoring
Nagios/Icinga Check:
#!/bin/bash
DAYS_LEFT=$(echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | \
openssl x509 -noout -checkend $((86400 * 30)))
if [ $? -eq 0 ]; then
echo "OK - Certificate valid for more than 30 days"
exit 0
else
echo "CRITICAL - Certificate expires in less than 30 days"
exit 2
fi
Troubleshooting
Problem: ACME Challenge fehlgeschlagen
Symptom:
Challenge failed for domain example.com
Lösung:
# 1. Prüfe DNS
dig +short example.com
# Muss auf Server-IP zeigen
# 2. Prüfe Port 80 erreichbar
curl -I http://example.com/.well-known/acme-challenge/test
# 3. Prüfe Nginx Logs
docker-compose logs web
# 4. Prüfe Certbot Logs
docker-compose logs certbot
cat certbot/logs/letsencrypt.log
Problem: Rate Limit erreicht
Let's Encrypt hat Rate Limits:
- 50 Zertifikate pro Domain pro Woche
- 5 fehlgeschlagene Validierungen pro Stunde
Lösung:
# Nutze Staging-Umgebung für Tests
docker run --rm \
-v $(pwd)/certbot/conf:/etc/letsencrypt \
-v $(pwd)/certbot/www:/var/www/certbot \
certbot/certbot:latest certonly \
--staging \
--webroot -w /var/www/certbot \
-d example.com
# Warte 1 Stunde bei fehlgeschlagenen Validierungen
Problem: Zertifikat-Erneuerung schlägt fehl
Symptom:
Failed to renew certificate example.com
Lösung:
# 1. Manuelle Erneuerung mit Debug
docker-compose exec certbot certbot renew --force-renewal --debug
# 2. Prüfe Webroot-Pfad
docker-compose exec web ls -la /var/www/certbot/.well-known/acme-challenge/
# 3. Prüfe Nginx Config
docker-compose exec web nginx -t
# 4. Reload Nginx nach Config-Änderung
docker-compose exec web nginx -s reload
Problem: Mixed Content Warnings
Nach HTTPS-Umstellung erscheinen Mixed Content Warnings.
Lösung:
# Content Security Policy Header
add_header Content-Security-Policy "upgrade-insecure-requests" always;
# In Application
# Verwende relative URLs oder HTTPS:
<script src="/js/app.js"></script> # Relativ - empfohlen
<script src="https://example.com/js/app.js"></script> # Absolut HTTPS
Security Best Practices
1. SSL-Konfiguration Härten
Mozilla SSL Configuration Generator: https://ssl-config.mozilla.org/
# Modern Configuration (nur TLS 1.3)
ssl_protocols TLSv1.3;
# Intermediate Configuration (TLS 1.2 + 1.3)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...';
ssl_prefer_server_ciphers off;
2. HSTS Preload
Nach erfolgreicher HTTPS-Umstellung:
- HSTS Header mit Preload:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
- Submit to Preload List: https://hstspreload.org/
3. OCSP Stapling
Verbessert SSL-Handshake-Performance:
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
4. Certificate Transparency Monitoring
Monitor für Certificate Transparency Logs:
Testing & Validation
SSL Labs Test
Teste SSL-Konfiguration:
# Online
https://www.ssllabs.com/ssltest/analyze.html?d=example.com
# CLI via testssl.sh
docker run --rm -ti drwetter/testssl.sh:latest example.com
Ziel: A+ Rating
Security Headers Check
curl -I https://example.com | grep -i "strict-transport-security\|x-frame-options\|x-content-type-options"
Certificate Chain Validation
openssl s_client -connect example.com:443 -showcerts
Backup & Recovery
Backup Zertifikate
# Backup /etc/letsencrypt
docker run --rm \
-v certbot-conf:/etc/letsencrypt \
-v $(pwd)/backups:/backups \
alpine tar czf /backups/letsencrypt-$(date +%Y%m%d).tar.gz -C / etc/letsencrypt
# Verschlüsselt
gpg --symmetric --cipher-algo AES256 backups/letsencrypt-$(date +%Y%m%d).tar.gz
Restore Zertifikate
# Entschlüsseln
gpg --decrypt backups/letsencrypt-20250115.tar.gz.gpg > letsencrypt-restore.tar.gz
# Restore
docker run --rm \
-v certbot-conf:/etc/letsencrypt \
-v $(pwd):/backups \
alpine tar xzf /backups/letsencrypt-restore.tar.gz -C /
# Nginx Reload
docker-compose exec web nginx -s reload
Automation Scripts
Siehe:
scripts/ssl-setup.sh- Initiale SSL-Einrichtungscripts/ssl-renew.sh- Manuelle Erneuerungscripts/ssl-check.sh- Status-Check
Next Steps
Nach SSL-Setup:
- Teste HTTPS: https://example.com
- SSL Labs Test: A+ Rating verifizieren
- Monitor Ablaufdatum: Automatisches Monitoring einrichten
- HSTS Preload: Nach Stabilisierung eintragen
- Firewall: Port 80 nur für ACME Challenge offen lassen