events { worker_connections 1024; } http { # Security headers 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 Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # Hide server version server_tokens off; # Rate limiting limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=ws:10m rate=5r/s; # Connection limiting limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m; # Logging log_format security '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_time $upstream_response_time'; access_log /var/log/nginx/security.log security; error_log /var/log/nginx/error.log warn; # Redirect HTTP to HTTPS server { listen 80; server_name _; return 301 https://$host$request_uri; } # HTTPS server server { listen 443 ssl http2; server_name ml-experiments.example.com; # SSL configuration ssl_certificate /etc/nginx/ssl/cert.pem; ssl_certificate_key /etc/nginx/ssl/key.pem; ssl_trusted_certificate /etc/nginx/ssl/ca.pem; # Modern SSL configuration ssl_protocols TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; # OCSP stapling ssl_stapling on; ssl_stapling_verify on; # Security limits client_max_body_size 10M; client_body_timeout 12s; client_header_timeout 12s; keepalive_timeout 15s; send_timeout 10s; limit_conn conn_limit_per_ip 20; # API endpoints location /health { proxy_pass https://api-server:9101; 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_connect_timeout 5s; proxy_send_timeout 10s; proxy_read_timeout 10s; } # WebSocket endpoint with special rate limiting location /ws { limit_req zone=ws burst=10 nodelay; proxy_pass https://api-server:9101; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 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_connect_timeout 7d; proxy_send_timeout 7d; proxy_read_timeout 7d; # WebSocket specific headers proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key; proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol; proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version; } # API endpoints with rate limiting location /api/ { limit_req zone=api burst=20 nodelay; proxy_pass https://api-server:9101; 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_set_header X-API-Key $http_x_api_key; proxy_connect_timeout 5s; proxy_send_timeout 10s; proxy_read_timeout 10s; } # Deny all other locations location / { return 404; } # Security monitoring endpoints (admin only) location /admin/ { # IP whitelist for admin access allow 10.0.0.0/8; allow 192.168.0.0/16; allow 172.16.0.0/12; deny all; proxy_pass https://api-server:9101; 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; } # Health check for load balancers location /lb-health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } } # Default server to catch unknown hosts server { listen 443 ssl http2 default_server; server_name _; ssl_certificate /etc/nginx/ssl/cert.pem; ssl_certificate_key /etc/nginx/ssl/key.pem; return 444; } }