events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; # Logging access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log warn; # Verberg nginx versie server_tokens off; # Gzip gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml; gzip_min_length 1000; # ── Rate limiting zones ──────────────────────────────────────────────────── # Auth endpoints: login, OAuth flow, superadmin — streng limit_req_zone $binary_remote_addr zone=auth:10m rate=10r/m; # API endpoints — soepeler voor normale interactie limit_req_zone $binary_remote_addr zone=api:10m rate=120r/m; # Upload endpoint — apart beperkt limit_req_zone $binary_remote_addr zone=upload:10m rate=5r/m; # Algemeen — vangt alles op wat niet specifiek gelimiteerd is limit_req_zone $binary_remote_addr zone=general:10m rate=60r/m; # Geef 429 terug bij rate limit (ipv standaard 503) limit_req_status 429; upstream flask { server backend:5000; keepalive 32; } server { listen 80; server_name _; # ── Security headers ─────────────────────────────────────────────────── # Talisman (Flask) voegt HSTS en CSP toe — nginx vult aan met de rest add_header X-Frame-Options "DENY" 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 Permissions-Policy "geolocation=(), microphone=(), camera=()" always; # Maximale upload grootte (voor doelen JSON bestanden) client_max_body_size 10M; # Timeouts — voorkomt Slowloris aanvallen client_body_timeout 12s; client_header_timeout 12s; send_timeout 10s; keepalive_timeout 65s; # ── Auth endpoints — strenge rate limiting ───────────────────────────── # Dekt login, OAuth start, OAuth callback en superadmin login location ~ ^/auth/ { limit_req zone=auth burst=8 nodelay; limit_req_log_level warn; proxy_pass http://flask; proxy_set_header Host $host; 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_http_version 1.1; proxy_set_header Connection ""; } # ── Upload endpoint — extra streng ───────────────────────────────────── location /admin/doelen/upload { limit_req zone=upload burst=2 nodelay; client_max_body_size 50M; # grotere bestanden toegestaan hier proxy_pass http://flask; proxy_set_header Host $host; 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_read_timeout 120s; } # ── API endpoints ────────────────────────────────────────────────────── location /api/ { limit_req zone=api burst=30 nodelay; proxy_pass http://flask; proxy_set_header Host $host; 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_http_version 1.1; proxy_set_header Connection ""; } # ── Admin endpoints ──────────────────────────────────────────────────── location /admin/ { limit_req zone=general burst=20 nodelay; proxy_pass http://flask; proxy_set_header Host $host; 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_http_version 1.1; proxy_set_header Connection ""; } # ── Alle andere requests ─────────────────────────────────────────────── location / { limit_req zone=general burst=20 nodelay; proxy_pass http://flask; proxy_set_header Host $host; 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_http_version 1.1; proxy_set_header Connection ""; proxy_read_timeout 60s; } # ── Blokkeer toegang tot verborgen bestanden (.git, .env, ...) ───────── location ~ /\. { deny all; return 404; } } }