feat: CI/CD pipeline setup complete - Ansible playbooks updated, secrets configured, workflow ready

This commit is contained in:
2025-10-31 01:39:24 +01:00
parent 55c04e4fd0
commit e26eb2aa12
601 changed files with 44184 additions and 32477 deletions

View File

@@ -0,0 +1,20 @@
# Traefik Configuration
# Copy this file to .env and adjust values
# Timezone
TZ=Europe/Berlin
# Let's Encrypt Email
ACME_EMAIL=kontakt@michaelschiemer.de
# Domain
DOMAIN=michaelschiemer.de
# Dashboard Authentication
# Generate password hash with: htpasswd -nb admin your_password
# Replace $ with $$ in docker-compose.yml
TRAEFIK_DASHBOARD_USER=admin
TRAEFIK_DASHBOARD_PASSWORD_HASH=
# Log Level (DEBUG, INFO, WARN, ERROR)
LOG_LEVEL=INFO

View File

@@ -0,0 +1,372 @@
# Traefik Stack - Reverse Proxy with SSL
## Overview
Traefik acts as the central reverse proxy for all services, handling:
- Automatic SSL certificate generation via Let's Encrypt
- HTTP to HTTPS redirection
- Service discovery via Docker labels
- Security headers and compression
- Rate limiting and access control
## Services
- **traefik.michaelschiemer.de** - Traefik Dashboard (BasicAuth protected)
## Prerequisites
1. **Docker Network**
```bash
docker network create traefik-public
```
2. **ACME Storage File**
```bash
touch acme.json
chmod 600 acme.json
```
3. **DNS Configuration**
Point these domains to your server IP (94.16.110.151):
- `michaelschiemer.de`
- `*.michaelschiemer.de` (wildcard)
## Configuration
### 1. Create Environment File
```bash
cp .env.example .env
```
### 2. Generate Dashboard Password
```bash
# Generate password hash
htpasswd -nb admin your_secure_password
# Example output:
# admin:$apr1$8kj9d7lj$r.x5jhLVPLuCDLvJ6x0Hd0
# Important: In docker-compose.yml, replace $ with $$
# admin:$$apr1$$8kj9d7lj$$r.x5jhLVPLuCDLvJ6x0Hd0
```
Update the `traefik.http.middlewares.traefik-auth.basicauth.users` label in `docker-compose.yml`.
### 3. Adjust Configuration (Optional)
Edit `traefik.yml` for:
- Log levels
- Certificate resolvers
- Additional entry points
- Metrics configuration
## Deployment
### Initial Setup
```bash
# Create network
docker network create traefik-public
# Create acme.json
touch acme.json
chmod 600 acme.json
# Create log directories
mkdir -p logs
# Start Traefik
docker compose up -d
```
### Verify Deployment
```bash
# Check container status
docker compose ps
# Check logs
docker compose logs -f
# Test dashboard access
curl -I https://traefik.michaelschiemer.de
# Check certificate
openssl s_client -connect traefik.michaelschiemer.de:443 -servername traefik.michaelschiemer.de < /dev/null
```
## Middleware Configuration
Traefik provides several reusable middlewares in `dynamic/middlewares.yml`:
### Security Headers
```yaml
labels:
- "traefik.http.routers.myapp.middlewares=security-headers-global@file"
```
### Rate Limiting
```yaml
labels:
# Strict: 50 req/s
- "traefik.http.routers.myapp.middlewares=rate-limit-strict@file"
# Moderate: 100 req/s
- "traefik.http.routers.myapp.middlewares=rate-limit-moderate@file"
# Lenient: 200 req/s
- "traefik.http.routers.myapp.middlewares=rate-limit-lenient@file"
```
### Compression
```yaml
labels:
- "traefik.http.routers.myapp.middlewares=gzip-compression@file"
```
### Middleware Chains
```yaml
labels:
# Default chain: Security + Compression
- "traefik.http.routers.myapp.middlewares=default-chain@file"
# Admin chain: Security + Compression + Rate Limiting
- "traefik.http.routers.myapp.middlewares=admin-chain@file"
```
## Service Integration
### Example Service Configuration
Add these labels to any Docker service to expose it through Traefik:
```yaml
services:
myapp:
image: myapp:latest
networks:
- traefik-public
labels:
# Enable Traefik
- "traefik.enable=true"
# Router configuration
- "traefik.http.routers.myapp.rule=Host(`app.michaelschiemer.de`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls=true"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
# Service configuration
- "traefik.http.services.myapp.loadbalancer.server.port=80"
# Middleware (optional)
- "traefik.http.routers.myapp.middlewares=default-chain@file"
networks:
traefik-public:
external: true
```
## Monitoring
### Dashboard Access
Access the Traefik dashboard at: https://traefik.michaelschiemer.de
Default credentials (change in production):
- Username: `admin`
- Password: (set via htpasswd)
### Logs
```bash
# Access logs (HTTP requests)
tail -f logs/access.log
# Traefik logs (errors, warnings)
tail -f logs/traefik.log
# Container logs
docker compose logs -f traefik
```
### Prometheus Metrics
Traefik exposes Prometheus metrics for monitoring:
```yaml
# Add to Prometheus scrape config
- job_name: 'traefik'
static_configs:
- targets: ['traefik:8082']
```
## Troubleshooting
### Certificate Issues
```bash
# Check acme.json permissions
ls -la acme.json
# Should be: -rw------- (600)
# View certificate status
docker compose logs traefik | grep -i "certificate"
# Force certificate renewal
rm acme.json
touch acme.json
chmod 600 acme.json
docker compose restart
```
### DNS Issues
```bash
# Verify DNS resolution
dig michaelschiemer.de
dig git.michaelschiemer.de
# Check from external
nslookup michaelschiemer.de 8.8.8.8
```
### Service Not Accessible
```bash
# Check Traefik can reach service
docker network inspect traefik-public
# Verify service labels
docker inspect <container_name> | grep -A 20 Labels
# Check Traefik logs for routing errors
docker compose logs traefik | grep -i error
```
### Port Conflicts
```bash
# Check if ports 80/443 are free
sudo netstat -tlnp | grep -E ':80|:443'
# Stop conflicting services
sudo systemctl stop nginx # or apache2
```
## Security Hardening
### 1. IP Whitelisting
Uncomment and configure in `dynamic/middlewares.yml`:
```yaml
admin-whitelist:
ipWhiteList:
sourceRange:
- "your.vpn.ip.range/32"
- "10.0.0.0/8"
```
### 2. Strong Dashboard Password
```bash
# Generate strong password
openssl rand -base64 32
# Create hash
htpasswd -nb admin "your_strong_password"
```
### 3. Rate Limiting
Apply rate limiting to sensitive endpoints:
```yaml
labels:
- "traefik.http.routers.admin.middlewares=rate-limit-strict@file"
```
### 4. DDoS Protection
```yaml
# In traefik.yml - add entry point middleware
entryPoints:
websecure:
address: ":443"
http:
middlewares:
- rate-limit-moderate@file
```
## Backup
### Important Files
- `acme.json` - SSL certificates
- `traefik.yml` - Static configuration
- `dynamic/` - Dynamic configuration
```bash
# Backup certificates
cp acme.json acme.json.backup.$(date +%Y%m%d)
# Backup configuration
tar -czf traefik-config-backup.tar.gz traefik.yml dynamic/
```
## Updates
```bash
# Pull latest image
docker compose pull
# Restart with new image
docker compose up -d
# Verify
docker compose ps
docker compose logs -f
```
## Performance Tuning
### Connection Limits
In `traefik.yml`:
```yaml
entryPoints:
websecure:
transport:
respondingTimeouts:
readTimeout: 60s
writeTimeout: 60s
lifeCycle:
requestAcceptGraceTimeout: 0s
graceTimeOut: 10s
```
### Resource Limits
In `docker-compose.yml`:
```yaml
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 256M
cpus: '0.25'
```
## Support
For issues with Traefik configuration:
1. Check official Traefik documentation: https://doc.traefik.io/traefik/
2. Review logs: `docker compose logs -f`
3. Verify network connectivity: `docker network inspect traefik-public`

View File

@@ -0,0 +1,80 @@
version: '3.8'
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- traefik-public
ports:
- "80:80"
- "443:443"
environment:
- TZ=Europe/Berlin
volumes:
# Docker socket for service discovery
- /var/run/docker.sock:/var/run/docker.sock:ro
# Static configuration
- ./traefik.yml:/traefik.yml:ro
# Dynamic configuration
- ./dynamic:/dynamic:ro
# SSL certificates
- ./acme.json:/acme.json
# Logs
- ./logs:/logs
labels:
# Enable Traefik for itself
- "traefik.enable=true"
# Dashboard
- "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.michaelschiemer.de`)"
- "traefik.http.routers.traefik-dashboard.entrypoints=websecure"
- "traefik.http.routers.traefik-dashboard.tls=true"
- "traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.traefik-dashboard.service=api@internal"
- "traefik.http.routers.traefik-dashboard.middlewares=traefik-auth"
# BasicAuth for dashboard (user: admin, password: generate with htpasswd)
# htpasswd -nb admin your_password
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$8kj9d7lj$$r.x5jhLVPLuCDLvJ6x0Hd0"
# Allow ACME challenges without redirect (higher priority)
- "traefik.http.routers.acme-challenge.rule=PathPrefix(`/.well-known/acme-challenge`)"
- "traefik.http.routers.acme-challenge.entrypoints=web"
- "traefik.http.routers.acme-challenge.priority=200"
# Global redirect to HTTPS (lower priority, matches everything else)
- "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.routers.http-catchall.priority=1"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"
# Security headers middleware
- "traefik.http.middlewares.security-headers.headers.frameDeny=true"
- "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.security-headers.headers.browserXssFilter=true"
- "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.security-headers.headers.stsPreload=true"
# Compression middleware
- "traefik.http.middlewares.compression.compress=true"
# Rate limiting middleware (100 requests per second)
- "traefik.http.middlewares.rate-limit.ratelimit.average=100"
- "traefik.http.middlewares.rate-limit.ratelimit.burst=50"
healthcheck:
test: ["CMD", "traefik", "healthcheck", "--ping"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
traefik-public:
external: true

View File

@@ -0,0 +1,15 @@
http:
routers:
gitea:
rule: Host(`git.michaelschiemer.de`)
entrypoints:
- websecure
service: gitea
tls:
certResolver: letsencrypt
priority: 100
services:
gitea:
loadBalancer:
servers:
- url: http://gitea:3000

View File

@@ -0,0 +1,68 @@
# Dynamic Middleware Configuration
http:
middlewares:
# Security headers for all services
security-headers-global:
headers:
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
customFrameOptionsValue: "SAMEORIGIN"
contentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"
referrerPolicy: "strict-origin-when-cross-origin"
permissionsPolicy: "geolocation=(), microphone=(), camera=()"
# Compression for better performance
gzip-compression:
compress:
excludedContentTypes:
- text/event-stream
# Rate limiting - strict
rate-limit-strict:
rateLimit:
average: 50
burst: 25
period: 1s
# Rate limiting - moderate
rate-limit-moderate:
rateLimit:
average: 100
burst: 50
period: 1s
# Rate limiting - lenient
rate-limit-lenient:
rateLimit:
average: 200
burst: 100
period: 1s
# IP whitelist for admin services (example)
# Uncomment and adjust for production
# admin-whitelist:
# ipWhiteList:
# sourceRange:
# - "127.0.0.1/32"
# - "10.0.0.0/8"
# Chain multiple middlewares
default-chain:
chain:
middlewares:
- security-headers-global
- gzip-compression
admin-chain:
chain:
middlewares:
- security-headers-global
- gzip-compression
- rate-limit-strict
# - admin-whitelist # Uncomment for IP whitelisting

View File

@@ -0,0 +1,85 @@
# Static Configuration for Traefik
# Global Configuration
global:
checkNewVersion: true
sendAnonymousUsage: false
# API and Dashboard
api:
dashboard: true
insecure: false
# Entry Points
entryPoints:
web:
address: ":80"
# No global redirect - ACME challenges need HTTP access
# Redirects are handled per-router via middleware
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
domains:
- main: michaelschiemer.de
sans:
- "*.michaelschiemer.de"
middlewares:
- security-headers@docker
- compression@docker
# Certificate Resolvers
certificatesResolvers:
letsencrypt:
acme:
email: kontakt@michaelschiemer.de
storage: /acme.json
caServer: https://acme-v02.api.letsencrypt.org/directory
# Use HTTP-01 challenge (requires port 80 accessible)
httpChallenge:
entryPoint: web
# Uncomment for DNS challenge (requires DNS provider)
# dnsChallenge:
# provider: cloudflare
# delayBeforeCheck: 30
# Providers
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik-public
watch: true
file:
directory: /dynamic
watch: true
# Logging
log:
level: INFO
filePath: /logs/traefik.log
format: json
# Access Logs
accessLog:
filePath: /logs/access.log
format: json
bufferingSize: 100
filters:
statusCodes:
- "400-499"
- "500-599"
# Metrics
metrics:
prometheus:
addEntryPointsLabels: true
addRoutersLabels: true
addServicesLabels: true
# Ping
ping:
entryPoint: web