# # NODE1 Hardened Nginx Configuration # Version: 2.0 # Last Updated: 2026-01-26 # # Features: # - TLS 1.2/1.3 only with modern ciphers # - HSTS with preload # - Rate limiting (standard + heavy endpoints) # - WAF-lite rules (block scanners, sensitive files) # - Security headers (XSS, CSRF, clickjacking) # - Logging with privacy (no auth headers) # # === Rate Limit Zones === limit_req_zone $binary_remote_addr zone=api_per_ip:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=heavy_per_ip:10m rate=2r/s; limit_req_zone $binary_remote_addr zone=webhook_per_ip:10m rate=50r/s; limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m; # === Logging format (no Authorization header) === log_format api_safe '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' 'rt=$request_time uct="$upstream_connect_time" ' 'uht="$upstream_header_time" urt="$upstream_response_time"'; # === Upstream === upstream gateway_upstream { server 127.0.0.1:9300; keepalive 64; } # === HTTP → HTTPS redirect === server { listen 80; listen [::]:80; server_name gateway.daarion.city api.daarion.io 144.76.224.179 _; # Allow ACME challenge for certificate renewal location /.well-known/acme-challenge/ { root /var/www/html; } # Redirect everything else to HTTPS location / { return 301 https://$host$request_uri; } } # === Main HTTPS Server === server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name gateway.daarion.city api.daarion.io 144.76.224.179; # === SSL Configuration === ssl_certificate /etc/letsencrypt/live/gateway.daarion.city/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/gateway.daarion.city/privkey.pem; # Modern SSL (TLS 1.2+ only) ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # SSL session ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; # OCSP Stapling ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; # === Security Headers === # HSTS (2 years, with preload) add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; # Prevent clickjacking add_header X-Frame-Options "SAMEORIGIN" always; # Prevent MIME sniffing add_header X-Content-Type-Options "nosniff" always; # XSS Protection add_header X-XSS-Protection "1; mode=block" always; # Referrer policy add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Content Security Policy (adjust as needed) add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' wss: https:;" always; # === Logging === access_log /var/log/nginx/api-access.log api_safe; error_log /var/log/nginx/api-error.log warn; # === WAF-lite: Block sensitive files === location ~* \.(env|git|sql|bak|swp|old|backup|log|ini|conf|config|yml|yaml|json|xml|db|sqlite)$ { return 444; # Close connection without response } # Block common attack paths location ~* ^/(\.git|\.svn|\.hg|\.env|wp-admin|wp-login|phpmyadmin|admin\.php|xmlrpc\.php) { return 444; } # Block suspicious query strings if ($query_string ~* "(union|select|insert|drop|delete|update|truncate|exec|script|alert)") { return 403; } # === Rate limit status (internal only) === location = /nginx-status { stub_status on; allow 127.0.0.1; deny all; } # === Health check (no rate limit) === location = /health { limit_req off; proxy_pass http://gateway_upstream/health; proxy_http_version 1.1; proxy_set_header Connection ""; } # === Webhook endpoints (higher burst for Telegram) === location ~ ^/(webhook|telegram|bot) { limit_req zone=webhook_per_ip burst=100 nodelay; limit_conn conn_per_ip 50; proxy_pass http://gateway_upstream; proxy_http_version 1.1; proxy_set_header Connection ""; 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 30s; proxy_read_timeout 30s; client_max_body_size 10m; } # === Heavy endpoints (stricter limit) === location ~ ^/(v1/rag|v1/image|v1/search|v1/embed|v1/generate) { limit_req zone=heavy_per_ip burst=5 nodelay; limit_conn conn_per_ip 10; proxy_pass http://gateway_upstream; proxy_http_version 1.1; proxy_set_header Connection ""; 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; # Longer timeouts for heavy operations proxy_connect_timeout 10s; proxy_send_timeout 300s; proxy_read_timeout 300s; client_max_body_size 100m; } # === Default API (standard rate limit) === location / { limit_req zone=api_per_ip burst=20 nodelay; limit_conn conn_per_ip 20; proxy_pass http://gateway_upstream; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $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 5s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_cache_bypass $http_upgrade; client_max_body_size 10m; } # === Error pages === error_page 429 = @rate_limited; location @rate_limited { default_type application/json; return 429 '{"error": "rate_limit_exceeded", "message": "Too many requests", "retry_after": 1}'; } error_page 403 = @forbidden; location @forbidden { default_type application/json; return 403 '{"error": "forbidden", "message": "Access denied"}'; } } # === WebSocket upgrade mapping === map $http_upgrade $connection_upgrade { default upgrade; '' close; } # === Admin Panel (localhost only, via SSH tunnel) === server { listen 127.0.0.1:8080; server_name localhost; location /grafana/ { proxy_pass http://127.0.0.1:3030/; proxy_http_version 1.1; proxy_set_header Host $host; } location /prometheus/ { proxy_pass http://127.0.0.1:9090/; proxy_http_version 1.1; proxy_set_header Host $host; } }