chore: complete update

This commit is contained in:
2025-07-17 16:38:55 +02:00
parent 64a7051137
commit ec5526e2b2
46 changed files with 3139 additions and 1 deletions

31
ansible/nginx-cdn-germany/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Cache
*.cache
.cache/
# Ansible
*.retry
.ansible/
# System
.DS_Store
Thumbs.db
# Logs
*.log
# Backups
*.backup
*.bak
# SSL Keys (niemals committen!)
*.key
*.pem
*.crt
# Secrets
vault.yml
secrets.yml
# Temporäre Dateien
*.tmp
*.temp

View File

@@ -0,0 +1,64 @@
# Einfache CDN-Verwaltung mit Make
.PHONY: deploy check health purge-cache warm-cache status reload
# Standard deployment
deploy:
@echo "🚀 Deploying Simple CDN..."
chmod +x scripts/deploy.sh
./scripts/deploy.sh
# Deployment mit Check-Modus (Dry-Run)
check:
@echo "🔍 Checking deployment (dry-run)..."
ansible-playbook -i inventories/production/hosts.yml playbooks/deploy-simple-cdn.yml --check --diff
# Health check aller Nodes
health:
@echo "🏥 Checking CDN health..."
ansible cdn_nodes -i inventories/production/hosts.yml -m uri -a "url=https://{{ inventory_hostname }}/health method=GET"
# Cache leeren
purge-cache:
@echo "🧹 Purging cache on all nodes..."
ansible cdn_nodes -i inventories/production/hosts.yml -m shell -a "find /var/cache/nginx/ -type f -delete"
@echo "✅ Cache purged on all nodes"
# Cache warming
warm-cache:
@echo "🔥 Warming cache..."
chmod +x scripts/warm-cache.sh
./scripts/warm-cache.sh
# Status-Report
status:
@echo "📊 CDN Status Report..."
ansible cdn_nodes -i inventories/production/hosts.yml -m shell -a "echo '=== {{ inventory_hostname }} ===' && /usr/local/bin/cdn-monitor && echo ''"
# Nginx neuladen
reload:
@echo "⚙️ Reloading nginx configuration..."
ansible cdn_nodes -i inventories/production/hosts.yml -m systemd -a "name=nginx state=reloaded"
# SSL-Zertifikate erneuern
renew-ssl:
@echo "🔐 Renewing SSL certificates..."
ansible cdn_nodes -i inventories/production/hosts.yml -m shell -a "certbot renew --quiet"
# Interaktive Verwaltung
manage:
@echo "🔧 Starting interactive management..."
ansible-playbook -i inventories/production/hosts.yml playbooks/manage-cdn.yml
# Hilfe
help:
@echo "📖 Available commands:"
@echo " make deploy - Deploy CDN"
@echo " make check - Test deployment (dry-run)"
@echo " make health - Check all nodes health"
@echo " make purge-cache - Clear all cache"
@echo " make warm-cache - Warm cache with popular URLs"
@echo " make status - Show detailed status"
@echo " make reload - Reload nginx config"
@echo " make renew-ssl - Renew SSL certificates"
@echo " make manage - Interactive management"

View File

@@ -0,0 +1,48 @@
# Simple Nginx CDN für Deutschland
Dieses Ansible-Projekt erstellt ein einfaches, aber effektives CDN nur mit Nginx für deutsche Server.
## Schnellstart
1. **Konfiguration anpassen:**
```bash
# Server-IPs eintragen
vim inventories/production/hosts.yml
# Domains anpassen
vim inventories/production/group_vars/all/main.yml
```
2. **Deployment:**
```bash
# Testen
ansible-playbook -i inventories/production/hosts.yml playbooks/deploy-simple-cdn.yml --check
# Deployen
ansible-playbook -i inventories/production/hosts.yml playbooks/deploy-simple-cdn.yml
```
3. **Verwalten:**
```bash
# Cache leeren
make purge-cache
# Status prüfen
make health
```
## Struktur
- `inventories/` - Server-Konfiguration
- `roles/` - Ansible-Rollen
- `playbooks/` - Deployment-Skripte
- `scripts/` - Hilfsskripte
## Features
✅ Nginx-basiertes CDN
✅ SSL mit Let's Encrypt
✅ DSGVO-konforme Logs
✅ Einfaches Monitoring
✅ Cache-Management
✅ Rate Limiting

View File

@@ -0,0 +1,115 @@
# SETUP.md - Einrichtungsanleitung
## 1. Vorbereitung
### Server vorbereiten
```bash
# Für jeden CDN-Server (als root):
apt update && apt upgrade -y
apt install -y python3 python3-pip
```
### SSH-Keys einrichten
```bash
# Auf deinem lokalen Rechner:
ssh-keygen -t rsa -b 4096 -C "cdn-deployment"
ssh-copy-id root@cdn-fra1.example.de
ssh-copy-id root@cdn-ham1.example.de
ssh-copy-id root@cdn-muc1.example.de
```
## 2. Konfiguration anpassen
### Domains und IPs ändern
```bash
# 1. Server-IPs eintragen
vim inventories/production/hosts.yml
# 2. Domain-Namen anpassen
vim inventories/production/group_vars/all/main.yml
```
**Wichtig:** Ändere diese Werte:
- `cdn_domain: "cdn.example.de"` → deine CDN-Domain
- `ssl_email: "admin@example.de"` → deine E-Mail
- `origin_domain: "www.example.de"` → deine Website
- Alle IP-Adressen in `hosts.yml`
## 3. DNS konfigurieren
Bevor du deployest, stelle sicher dass deine CDN-Domain zu den Servern zeigt:
```bash
# A-Records für deine CDN-Domain:
cdn.example.de. IN A 10.0.1.10 # Frankfurt
cdn.example.de. IN A 10.0.2.10 # Hamburg
cdn.example.de. IN A 10.0.3.10 # München
```
## 4. Deployment
```bash
# Testen
make check
# Deployen
make deploy
# Health-Check
make health
```
## 5. Testen
```bash
# CDN testen
curl -I https://cdn.example.de/health
# Cache-Header prüfen
curl -I https://cdn.example.de/some-static-file.css
# Performance testen
time curl -o /dev/null -s https://cdn.example.de/
```
## 6. Wartung
```bash
# Cache leeren
make purge-cache
# Status prüfen
make status
# SSL erneuern
make renew-ssl
# Interaktive Verwaltung
make manage
```
## Troubleshooting
### Ansible-Verbindung testen
```bash
ansible all -m ping
```
### Nginx-Konfiguration prüfen
```bash
ansible cdn_nodes -m shell -a "nginx -t"
```
### Logs anschauen
```bash
ansible cdn_nodes -m shell -a "tail -f /var/log/nginx/error.log"
```
### SSL-Probleme
```bash
# SSL-Status prüfen
ansible cdn_nodes -m shell -a "certbot certificates"
# Manuell erneuern
ansible cdn_nodes -m shell -a "certbot renew --force-renewal"
```

View File

@@ -0,0 +1,15 @@
[defaults]
inventory = inventories/production/hosts.yml
host_key_checking = False
timeout = 30
forks = 5
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
control_path = /tmp/ansible-ssh-%%h-%%p-%%r

View File

@@ -0,0 +1,19 @@
#!/bin/bash
# Scripts ausführbar machen
chmod +x scripts/deploy.sh
chmod +x scripts/warm-cache.sh
echo "✅ CDN-Projekt wurde erfolgreich erstellt!"
echo ""
echo "Nächste Schritte:"
echo "1. Konfiguration anpassen:"
echo " - vim inventories/production/hosts.yml"
echo " - vim inventories/production/group_vars/all/main.yml"
echo ""
echo "2. SETUP.md lesen für detaillierte Anleitung"
echo ""
echo "3. Deployment testen:"
echo " make check"
echo ""
echo "4. CDN deployen:"
echo " make deploy"

View File

@@ -0,0 +1,26 @@
---
# Globale Variablen für das CDN
# Domain-Konfiguration (ÄNDERE DIESE!)
cdn_domain: "cdn.example.de" # Deine CDN-Domain
ssl_email: "admin@example.de" # E-Mail für SSL-Zertifikate
origin_domain: "www.example.de" # Deine Haupt-Website
# Cache-Einstellungen
cache_settings:
static_files_ttl: "1y" # CSS, JS, Fonts
images_ttl: "30d" # Bilder
html_ttl: "5m" # HTML-Seiten
api_ttl: "0" # APIs (kein Caching)
# DSGVO-Einstellungen
gdpr_settings:
log_retention_days: 30
anonymize_ips: true
cookie_consent_required: true
# Rate Limiting
rate_limits:
api: "10r/s"
static: "100r/s"
images: "50r/s"

View File

@@ -0,0 +1,22 @@
---
# CDN-Node spezifische Konfiguration
# Nginx Performance
nginx_worker_processes: "auto"
nginx_worker_connections: 2048
nginx_keepalive_timeout: 65
# Performance-Tuning
tcp_optimizations:
tcp_nodelay: "on"
tcp_nopush: "on"
sendfile: "on"
# Proxy-Einstellungen
proxy_settings:
connect_timeout: "5s"
send_timeout: "10s"
read_timeout: "10s"
buffering: "on"
buffer_size: "64k"
buffers: "8 64k"

View File

@@ -0,0 +1,47 @@
---
# Inventar mit gruppierten SSH-Schlüsseln
all:
children:
origin_servers:
hosts:
origin1.example.de:
ansible_host: 192.168.1.10
origin2.example.de:
ansible_host: 192.168.1.11
vars:
ansible_ssh_private_key_file: ~/.ssh/origin_servers_key
cdn_nodes:
children:
primary_nodes:
hosts:
cdn-fra1.example.de:
ansible_host: 10.0.1.10
city: "Frankfurt"
region: "Hessen"
tier: "primary"
cache_size: "50g"
vars:
ansible_ssh_private_key_file: ~/.ssh/cdn_primary_key
secondary_nodes:
hosts:
cdn-ham1.example.de:
ansible_host: 10.0.2.10
city: "Hamburg"
region: "Hamburg"
tier: "secondary"
cache_size: "20g"
cdn-muc1.example.de:
ansible_host: 10.0.3.10
city: "München"
region: "Bayern"
tier: "secondary"
cache_size: "20g"
vars:
ansible_ssh_private_key_file: ~/.ssh/cdn_secondary_key
vars:
ansible_user: root
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

View File

@@ -0,0 +1,48 @@
---
# Inventar mit individuellen SSH-Schlüsseln
all:
children:
origin_servers:
hosts:
origin1.example.de:
ansible_host: 192.168.1.10
datacenter: "Frankfurt"
ansible_ssh_private_key_file: ~/.ssh/origin1_key
origin2.example.de:
ansible_host: 192.168.1.11
datacenter: "Frankfurt"
ansible_ssh_private_key_file: ~/.ssh/origin2_key
cdn_nodes:
hosts:
# Frankfurt - Primary
cdn-fra1.example.de:
ansible_host: 10.0.1.10
city: "Frankfurt"
region: "Hessen"
tier: "primary"
cache_size: "50g"
ansible_ssh_private_key_file: ~/.ssh/cdn_fra1_key
# Hamburg - Secondary
cdn-ham1.example.de:
ansible_host: 10.0.2.10
city: "Hamburg"
region: "Hamburg"
tier: "secondary"
cache_size: "20g"
ansible_ssh_private_key_file: ~/.ssh/cdn_ham1_key
# München - Secondary
cdn-muc1.example.de:
ansible_host: 10.0.3.10
city: "München"
region: "Bayern"
tier: "secondary"
cache_size: "20g"
ansible_ssh_private_key_file: ~/.ssh/cdn_muc1_key
vars:
ansible_user: root
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

View File

@@ -0,0 +1,45 @@
---
# Inventar für deutsches CDN
all:
children:
origin_servers:
hosts:
origin1.example.de:
ansible_host: 192.168.1.10 # Ändere diese IP
datacenter: "Frankfurt"
origin2.example.de:
ansible_host: 192.168.1.11 # Ändere diese IP
datacenter: "Frankfurt"
cdn_nodes:
hosts:
# Frankfurt - Primary
cdn-fra1.example.de:
ansible_host: 10.0.1.10 # Ändere diese IP
city: "Frankfurt"
region: "Hessen"
tier: "primary"
cache_size: "50g"
# Hamburg - Secondary
cdn-ham1.example.de:
ansible_host: 10.0.2.10 # Ändere diese IP
city: "Hamburg"
region: "Hamburg"
tier: "secondary"
cache_size: "20g"
# München - Secondary
cdn-muc1.example.de:
ansible_host: 10.0.3.10 # Ändere diese IP
city: "München"
region: "Bayern"
tier: "secondary"
cache_size: "20g"
vars:
# SSH-Konfiguration
ansible_user: root
ansible_ssh_private_key_file: ~/.ssh/id_rsa
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

View File

@@ -0,0 +1,43 @@
---
# Simple CDN Deployment
- name: Deploy Simple CDN for Germany
hosts: cdn_nodes
become: yes
serial: 1 # Ein Node nach dem anderen
pre_tasks:
- name: Update system packages
apt:
update_cache: yes
upgrade: dist
- name: Install required packages
apt:
name:
- nginx
- certbot
- python3-certbot-nginx
- curl
- htop
- bc
state: present
roles:
- nginx-cdn-config
- ssl-certificates
- simple-monitoring
post_tasks:
- name: Verify CDN health
uri:
url: "https://{{ inventory_hostname }}/health"
method: GET
status_code: 200
validate_certs: yes
retries: 5
delay: 10
- name: Display deployment success
debug:
msg: "✅ CDN Node {{ inventory_hostname }} ({{ city }}) successfully deployed!"

View File

@@ -0,0 +1,68 @@
---
# CDN Management Tasks
- name: CDN Management and Maintenance
hosts: cdn_nodes
become: yes
vars_prompt:
- name: action
prompt: "What action? (purge-cache/reload-config/check-health/view-stats/warm-cache)"
private: no
tasks:
- name: Purge all cache
shell: find /var/cache/nginx/ -type f -delete
when: action == "purge-cache"
- name: Display cache purge result
debug:
msg: "✅ Cache purged on {{ inventory_hostname }}"
when: action == "purge-cache"
- name: Reload nginx configuration
systemd:
name: nginx
state: reloaded
when: action == "reload-config"
- name: Check CDN health
uri:
url: "https://{{ inventory_hostname }}/health"
method: GET
status_code: 200
register: health_result
when: action == "check-health"
- name: Display health result
debug:
msg: "{{ health_result.content }}"
when: action == "check-health"
- name: Show cache and system statistics
shell: |
echo "=== Cache Size ==="
du -sh /var/cache/nginx/
echo "=== Cache Files ==="
find /var/cache/nginx/ -type f | wc -l
echo "=== System Load ==="
uptime
echo "=== Memory Usage ==="
free -h
echo "=== Disk Usage ==="
df -h /
register: stats_result
when: action == "view-stats"
- name: Display statistics
debug:
msg: "{{ stats_result.stdout_lines }}"
when: action == "view-stats"
- name: Warm cache with popular URLs
uri:
url: "https://{{ inventory_hostname }}{{ item }}"
method: GET
loop:
- "/"
- "/health"
ignore_errors: yes
when: action == "warm-cache"

View File

@@ -0,0 +1,10 @@
---
- name: reload nginx
systemd:
name: nginx
state: reloaded
- name: restart nginx
systemd:
name: nginx
state: restarted

View File

@@ -0,0 +1,64 @@
---
# Nginx CDN Konfiguration
- name: Remove default nginx config
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: reload nginx
- name: Create nginx directories
file:
path: "{{ item }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
loop:
- /var/cache/nginx/static
- /var/cache/nginx/images
- /var/cache/nginx/html
- /var/log/nginx/cdn
- /etc/nginx/includes
- name: Configure nginx main config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
backup: yes
notify: reload nginx
- name: Create nginx includes
template:
src: "{{ item }}.j2"
dest: "/etc/nginx/includes/{{ item }}"
loop:
- security-headers.conf
- rate-limiting.conf
- gzip-settings.conf
notify: reload nginx
- name: Configure CDN site
template:
src: cdn-site.conf.j2
dest: /etc/nginx/sites-available/cdn
backup: yes
notify: reload nginx
- name: Enable CDN site
file:
src: /etc/nginx/sites-available/cdn
dest: /etc/nginx/sites-enabled/cdn
state: link
notify: reload nginx
- name: Test nginx configuration
command: nginx -t
register: nginx_test
failed_when: nginx_test.rc != 0
- name: Start and enable nginx
systemd:
name: nginx
state: started
enabled: yes

View File

@@ -0,0 +1,195 @@
server {
listen 80;
listen [::]:80;
server_name {{ cdn_domain }};
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ cdn_domain }};
# SSL Configuration (wird von Certbot automatisch gefüllt)
ssl_certificate /etc/letsencrypt/live/{{ cdn_domain }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ cdn_domain }}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# CDN Headers
add_header X-CDN-Node "{{ city }}, Deutschland";
add_header X-Cache-Status $upstream_cache_status always;
add_header X-Served-By "nginx-{{ inventory_hostname }}";
# Security Headers
include /etc/nginx/includes/security-headers.conf;
# Logging
access_log /var/log/nginx/cdn/{{ cdn_domain }}.access.log cdn_format;
error_log /var/log/nginx/cdn/{{ cdn_domain }}.error.log warn;
# Rate Limiting
include /etc/nginx/includes/rate-limiting.conf;
# Gzip Compression
include /etc/nginx/includes/gzip-settings.conf;
# Nginx Status für Monitoring
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 10.0.0.0/8;
deny all;
}
##
# Static Files (CSS, JS, Fonts) - Lange Cache-Zeit
##
location ~* \.(css|js|woff|woff2|ttf|eot|ico)$ {
proxy_pass https://origin_servers;
proxy_ssl_verify off;
# Cache Settings
proxy_cache static_cache;
proxy_cache_valid 200 302 {{ cache_settings.static_files_ttl }};
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_cache_revalidate on;
# Headers
expires {{ cache_settings.static_files_ttl }};
add_header Cache-Control "public, immutable";
add_header Vary "Accept-Encoding";
# Rate Limiting
limit_req zone=static_files burst=50 nodelay;
# Proxy Headers
proxy_set_header Host {{ origin_domain }};
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
##
# Images - Mittlere Cache-Zeit
##
location ~* \.(jpg|jpeg|png|gif|webp|svg|avif|bmp|tiff)$ {
proxy_pass https://origin_servers;
proxy_ssl_verify off;
# Cache Settings
proxy_cache images_cache;
proxy_cache_valid 200 302 {{ cache_settings.images_ttl }};
proxy_cache_valid 404 5m;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
# Headers
expires {{ cache_settings.images_ttl }};
add_header Cache-Control "public";
add_header Vary "Accept";
# Rate Limiting
limit_req zone=images burst=30 nodelay;
# Proxy Headers
proxy_set_header Host {{ origin_domain }};
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
##
# API Endpoints - Kein Caching
##
location /api/ {
proxy_pass https://origin_servers;
proxy_ssl_verify off;
# Kein Caching
proxy_no_cache 1;
proxy_cache_bypass 1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
# Rate Limiting
limit_req zone=api burst=10 nodelay;
# CORS Headers
add_header Access-Control-Allow-Origin "https://{{ origin_domain }}";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With";
add_header Access-Control-Allow-Credentials true;
# Proxy Headers
proxy_set_header Host {{ origin_domain }};
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout {{ proxy_settings.connect_timeout }};
proxy_send_timeout {{ proxy_settings.send_timeout }};
proxy_read_timeout {{ proxy_settings.read_timeout }};
}
##
# Cache Purge (nur interne IPs)
##
location ~ /purge(/.*) {
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 192.168.0.0/16;
deny all;
proxy_cache_purge static_cache $scheme$proxy_host$1;
}
##
# Health Check
##
location /health {
access_log off;
return 200 "OK - CDN Node {{ city }}\nRegion: {{ region }}\nTier: {{ tier }}\nTimestamp: $time_iso8601\n";
add_header Content-Type text/plain;
}
##
# Default Location - HTML Caching
##
location / {
proxy_pass https://origin_servers;
proxy_ssl_verify off;
# Cache Settings für HTML
proxy_cache html_cache;
proxy_cache_valid 200 {{ cache_settings.html_ttl }};
proxy_cache_valid 404 1m;
proxy_cache_bypass $arg_nocache $cookie_sessionid $http_authorization;
# Headers
add_header Cache-Control "public, max-age=300";
# Proxy Headers
proxy_set_header Host {{ origin_domain }};
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-CDN-Node "{{ city }}";
# Timeouts
proxy_connect_timeout {{ proxy_settings.connect_timeout }};
proxy_send_timeout {{ proxy_settings.send_timeout }};
proxy_read_timeout {{ proxy_settings.read_timeout }};
# Buffering
proxy_buffering {{ proxy_settings.buffering }};
proxy_buffer_size {{ proxy_settings.buffer_size }};
proxy_buffers {{ proxy_settings.buffers }};
}
}

View File

@@ -0,0 +1,20 @@
# Gzip Compression Settings
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_proxied any;
gzip_disable "msie6";
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml
application/json
application/ld+json;

View File

@@ -0,0 +1,75 @@
user www-data;
worker_processes {{ nginx_worker_processes }};
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections {{ nginx_worker_connections }};
use epoll;
multi_accept on;
}
http {
##
# Basic Settings
##
sendfile {{ tcp_optimizations.sendfile }};
tcp_nopush {{ tcp_optimizations.tcp_nopush }};
tcp_nodelay {{ tcp_optimizations.tcp_nodelay }};
keepalive_timeout {{ nginx_keepalive_timeout }};
types_hash_max_size 2048;
server_tokens off;
server_names_hash_bucket_size 64;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# DSGVO-konforme Logging
##
map $remote_addr $anonymized_ip {
~(?P<ip>\d+\.\d+\.\d+)\.\d+ $ip.0;
~(?P<ipv6>[^:]+:[^:]+:[^:]+:[^:]+):.* $ipv6::;
default 0.0.0.0;
}
log_format cdn_format '$anonymized_ip - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time '
'cache="$upstream_cache_status" '
'cdn_node="{{ inventory_hostname }}"';
access_log /var/log/nginx/access.log cdn_format;
error_log /var/log/nginx/error.log warn;
##
# Cache Paths
##
proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:100m
max_size={{ cache_size }} inactive=7d use_temp_path=off;
proxy_cache_path /var/cache/nginx/images levels=1:2 keys_zone=images_cache:100m
max_size={{ cache_size }} inactive=30d use_temp_path=off;
proxy_cache_path /var/cache/nginx/html levels=1:2 keys_zone=html_cache:50m
max_size=5g inactive=1h use_temp_path=off;
##
# Upstream zu Origin-Servern
##
upstream origin_servers {
{% for host in groups['origin_servers'] %}
server {{ hostvars[host]['ansible_default_ipv4']['address'] }}:443
weight=1 max_fails=3 fail_timeout=30s;
{% endfor %}
keepalive 32;
keepalive_requests 1000;
keepalive_timeout 60s;
}
##
# Include configurations
##
include /etc/nginx/includes/*.conf;
include /etc/nginx/sites-enabled/*;
}

View File

@@ -0,0 +1,9 @@
# Rate Limiting Zones
limit_req_zone $binary_remote_addr zone=api:10m rate={{ rate_limits.api }};
limit_req_zone $binary_remote_addr zone=static_files:10m rate={{ rate_limits.static }};
limit_req_zone $binary_remote_addr zone=images:10m rate={{ rate_limits.images }};
# Connection Limiting
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn perip 10;

View File

@@ -0,0 +1,10 @@
# Security Headers für CDN
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# DSGVO Header
add_header X-Data-Processing "Art. 6 Abs. 1 lit. f DSGVO" always;

View File

@@ -0,0 +1,29 @@
---
# Einfaches Monitoring ohne Prometheus
- name: Create simple monitoring script
template:
src: simple-monitor.sh.j2
dest: /usr/local/bin/cdn-monitor
mode: '0755'
- name: Setup monitoring cron job
cron:
name: "CDN Health Monitor"
minute: "*/5"
job: "/usr/local/bin/cdn-monitor"
user: root
- name: Create log rotation for monitoring logs
copy:
content: |
/var/log/nginx/cdn-monitor.log {
weekly
missingok
rotate 4
compress
delaycompress
notifempty
}
dest: /etc/logrotate.d/cdn-monitor
mode: '0644'

View File

@@ -0,0 +1,71 @@
#!/bin/bash
# Einfaches CDN Monitoring für {{ inventory_hostname }}
LOG_FILE="/var/log/nginx/cdn-monitor.log"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
CDN_DOMAIN="{{ cdn_domain }}"
# Health Check
health_check() {
local response=$(curl -s -o /dev/null -w "%{http_code}" "https://$CDN_DOMAIN/health")
if [ "$response" = "200" ]; then
echo "[$TIMESTAMP] ✅ Health check OK" >> $LOG_FILE
return 0
else
echo "[$TIMESTAMP] ❌ Health check FAILED (HTTP $response)" >> $LOG_FILE
return 1
fi
}
# Nginx-Statistiken
nginx_stats() {
local stats=$(curl -s http://127.0.0.1/nginx_status 2>/dev/null)
if [ $? -eq 0 ]; then
local active_conn=$(echo "$stats" | grep "Active connections" | awk '{print $3}')
local total_requests=$(echo "$stats" | grep "server accepts" | awk '{print $3}')
echo "[$TIMESTAMP] 📊 Active: $active_conn, Total: $total_requests" >> $LOG_FILE
fi
}
# Cache-Größe prüfen
cache_check() {
local cache_size=$(du -sh /var/cache/nginx/ 2>/dev/null | cut -f1)
local cache_files=$(find /var/cache/nginx/ -type f 2>/dev/null | wc -l)
echo "[$TIMESTAMP] 💾 Cache: $cache_size ($cache_files files)" >> $LOG_FILE
}
# System-Ressourcen
system_check() {
local load=$(uptime | awk -F'load average:' '{print $2}' | awk -F',' '{print $1}' | tr -d ' ')
local memory=$(free | grep Mem | awk '{printf "%.1f", $3/$2 * 100.0}')
local disk=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
echo "[$TIMESTAMP] 🖥️ Load: $load, Memory: ${memory}%, Disk: ${disk}%" >> $LOG_FILE
# Warnungen bei hoher Auslastung
if (( $(echo "$load > 5.0" | bc -l 2>/dev/null || echo 0) )); then
echo "[$TIMESTAMP] ⚠️ HIGH LOAD WARNING: $load" >> $LOG_FILE
fi
if (( $(echo "$memory > 90.0" | bc -l 2>/dev/null || echo 0) )); then
echo "[$TIMESTAMP] ⚠️ HIGH MEMORY WARNING: ${memory}%" >> $LOG_FILE
fi
if [ "$disk" -gt 85 ]; then
echo "[$TIMESTAMP] ⚠️ HIGH DISK USAGE WARNING: ${disk}%" >> $LOG_FILE
fi
}
# Hauptausführung
main() {
health_check
nginx_stats
cache_check
system_check
# Log-Datei begrenzen (nur letzte 1000 Zeilen behalten)
tail -n 1000 $LOG_FILE > ${LOG_FILE}.tmp && mv ${LOG_FILE}.tmp $LOG_FILE
}
main

View File

@@ -0,0 +1,30 @@
---
# SSL-Zertifikate mit Let's Encrypt
- name: Check if certificate exists
stat:
path: "/etc/letsencrypt/live/{{ cdn_domain }}/fullchain.pem"
register: cert_exists
- name: Generate SSL certificate with certbot
command: >
certbot certonly --nginx
-d {{ cdn_domain }}
--non-interactive
--agree-tos
--email {{ ssl_email }}
when: not cert_exists.stat.exists
- name: Setup SSL certificate renewal
cron:
name: "Renew SSL certificates"
minute: "0"
hour: "3"
job: "certbot renew --quiet --deploy-hook 'systemctl reload nginx'"
user: root
- name: Test SSL certificate renewal (dry-run)
command: certbot renew --dry-run
register: renewal_test
failed_when: renewal_test.rc != 0
changed_when: false

View File

@@ -0,0 +1,44 @@
#!/bin/bash
# Simple CDN Deployment Script
set -e
INVENTORY_FILE="inventories/production/hosts.yml"
PLAYBOOK="playbooks/deploy-simple-cdn.yml"
echo "🚀 Starting Simple CDN Deployment for Germany..."
# Pre-deployment checks
echo "🔍 Running pre-deployment checks..."
if ! ansible all -i $INVENTORY_FILE -m ping; then
echo "❌ Some hosts are not reachable. Please check your inventory."
exit 1
fi
echo "📋 Testing ansible configuration..."
if ! ansible-playbook $PLAYBOOK -i $INVENTORY_FILE --check --diff; then
echo "❌ Configuration test failed. Please fix errors first."
exit 1
fi
read -p "Continue with deployment? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Deployment cancelled."
exit 0
fi
# Deployment
echo "🔧 Deploying CDN nodes..."
ansible-playbook $PLAYBOOK -i $INVENTORY_FILE
# Post-deployment verification
echo "✅ Verifying deployment..."
ansible cdn_nodes -i $INVENTORY_FILE -m uri -a "url=https://{{ inventory_hostname }}/health method=GET status_code=200"
echo "🎉 CDN Deployment completed successfully!"
echo ""
echo "Next steps:"
echo "1. Update your DNS to point to the CDN nodes"
echo "2. Test your CDN: curl -I https://your-cdn-domain.de/health"
echo "3. Monitor with: ansible-playbook -i $INVENTORY_FILE playbooks/manage-cdn.yml"

View File

@@ -0,0 +1,125 @@
#!/bin/bash
# SSH-Schlüssel Management für CDN
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
show_help() {
echo "CDN SSH Key Management"
echo ""
echo "Usage: $0 [OPTION]"
echo ""
echo "Options:"
echo " single - Ein Schlüssel für alle Nodes (Standard)"
echo " individual - Separater Schlüssel pro Node"
echo " grouped - Gruppierte Schlüssel (Primary/Secondary)"
echo " generate - SSH-Schlüssel generieren"
echo " deploy - Öffentliche Schlüssel zu Servern kopieren"
echo " help - Diese Hilfe anzeigen"
}
generate_single_key() {
echo "🔑 Generiere einen SSH-Schlüssel für alle CDN-Nodes..."
if [ ! -f ~/.ssh/cdn_key ]; then
ssh-keygen -t ed25519 -C "cdn-deployment" -f ~/.ssh/cdn_key -N ""
echo "✅ Schlüssel generiert: ~/.ssh/cdn_key"
else
echo " Schlüssel existiert bereits: ~/.ssh/cdn_key"
fi
# Inventar anpassen
sed -i 's|ansible_ssh_private_key_file: .*|ansible_ssh_private_key_file: ~/.ssh/cdn_key|' \
"$SCRIPT_DIR/../inventories/production/hosts.yml"
echo "✅ Inventar aktualisiert"
}
generate_individual_keys() {
echo "🔑 Generiere individuelle SSH-Schlüssel..."
NODES=("cdn_fra1" "cdn_ham1" "cdn_muc1" "origin1" "origin2")
for node in "${NODES[@]}"; do
if [ ! -f ~/.ssh/${node}_key ]; then
ssh-keygen -t ed25519 -C "cdn-${node}" -f ~/.ssh/${node}_key -N ""
echo "✅ Schlüssel generiert: ~/.ssh/${node}_key"
else
echo " Schlüssel existiert bereits: ~/.ssh/${node}_key"
fi
done
echo "✅ Alle individuellen Schlüssel generiert"
echo "💡 Verwende: cp inventories/production/hosts-individual-keys.yml.example inventories/production/hosts.yml"
}
generate_grouped_keys() {
echo "🔑 Generiere gruppierte SSH-Schlüssel..."
GROUPS=("origin_servers" "cdn_primary" "cdn_secondary")
for group in "${GROUPS[@]}"; do
if [ ! -f ~/.ssh/${group}_key ]; then
ssh-keygen -t ed25519 -C "cdn-${group}" -f ~/.ssh/${group}_key -N ""
echo "✅ Schlüssel generiert: ~/.ssh/${group}_key"
else
echo " Schlüssel existiert bereits: ~/.ssh/${group}_key"
fi
done
echo "✅ Alle gruppierten Schlüssel generiert"
echo "💡 Verwende: cp inventories/production/hosts-grouped-keys.yml.example inventories/production/hosts.yml"
}
deploy_keys() {
echo "🚀 Deploye öffentliche Schlüssel zu den Servern..."
# Lese IPs aus dem Inventar
IPS=$(grep "ansible_host:" "$SCRIPT_DIR/../inventories/production/hosts.yml" | awk '{print $2}' | sort | uniq)
for ip in $IPS; do
echo "Deploying to $ip..."
# Versuche verschiedene Schlüssel
for key in ~/.ssh/*_key ~/.ssh/cdn_key ~/.ssh/id_rsa; do
if [ -f "$key" ]; then
echo " Versuche Schlüssel: $key"
if ssh-copy-id -i "${key}.pub" "root@$ip" 2>/dev/null; then
echo " ✅ Erfolgreich: $key -> $ip"
break
fi
fi
done
done
}
case "$1" in
"single")
generate_single_key
;;
"individual")
generate_individual_keys
;;
"grouped")
generate_grouped_keys
;;
"generate")
echo "Welche Art von Schlüsseln?"
echo "1) Ein Schlüssel für alle (empfohlen für Start)"
echo "2) Individuelle Schlüssel pro Node (sicherste)"
echo "3) Gruppierte Schlüssel (Kompromiss)"
read -p "Wähle (1-3): " choice
case $choice in
1) generate_single_key ;;
2) generate_individual_keys ;;
3) generate_grouped_keys ;;
*) echo "Ungültige Auswahl" ;;
esac
;;
"deploy")
deploy_keys
;;
"help"|*)
show_help
;;
esac

View File

@@ -0,0 +1,37 @@
#!/bin/bash
# Cache Warming Script
INVENTORY_FILE="inventories/production/hosts.yml"
# URLs zum Cache-Warming
URLS=(
"/"
"/health"
# Füge hier deine wichtigsten URLs hinzu:
# "/css/main.css"
# "/js/app.js"
# "/images/logo.png"
)
echo "🔥 Starting cache warming for all CDN nodes..."
# Hole alle CDN Node Hostnamen
CDN_NODES=$(ansible-inventory -i $INVENTORY_FILE --list | jq -r '.cdn_nodes.hosts[]' 2>/dev/null || ansible cdn_nodes -i $INVENTORY_FILE --list-hosts | grep -v hosts)
for node in $CDN_NODES; do
echo "Warming cache for: $node"
for url in "${URLS[@]}"; do
echo " Warming: $url"
response=$(curl -s -o /dev/null -w "%{http_code}" "https://${node}${url}" || echo "000")
if [ "$response" = "200" ]; then
echo " ✅ OK"
else
echo " ❌ Failed (HTTP $response)"
fi
sleep 0.5
done
echo ""
done
echo "🎉 Cache warming completed!"