map $http_upgrade $connection_upgrade { default upgrade; '' close; } # Helpful for larger uploads via API (tweak as you wish) client_max_body_size 400m; log_format mtls_debug ' [$time_local] $remote_addr:$remote_port → $server_name:$server_port "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" TLS=$ssl_protocol/$ssl_cipher ClientVerify=$ssl_client_verify ClientSerial=$ssl_client_serial ClientSubject="$ssl_client_s_dn" ClientIssuer="$ssl_client_i_dn" RequestTime=$request_time ProxyUpstreamAddr=$upstream_addr ProxyStatus=$upstream_status '; # Default access & error logs for both 80 and 443 servers access_log /var/log/nginx/access.log mtls_debug; error_log /var/log/nginx/error.log warn; server { listen 80; server_name _; # Common proxy settings 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; # --- Frontend (Vite dev server) --- # Pass EVERYTHING else to Vite dev server on `web:5173` # Includes HMR websocket support. location / { proxy_pass http://web:5173; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } # --- API --- # Strip the /api prefix (so backend sees /foo instead of /api/foo) location /api/ { proxy_pass http://snoop-api:8080/; # note the trailing slash -> strips /api proxy_http_version 1.1; # WebSocket/SSE-friendly defaults for API (covers /api/ws etc.) proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } # --- MQTT over WebSocket (EMQX) --- # Clients connect to ws:///mqtt/ws location /mqtt/ws { proxy_pass http://emqx:8083/mqtt; # EMQX WS path is /mqtt proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } } # ============================================== # HTTPS :443 — mTLS enforced only on listed paths # ============================================== server { listen 443 ssl http2; server_name _; access_log /var/log/nginx/access.log mtls_debug; error_log /var/log/nginx/error.log info; ssl_certificate /etc/nginx/ssl/certs/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/certs/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; # mTLS trust chain (from Vault) ssl_client_certificate /etc/nginx/ssl/iot_int_cert.pem; ssl_verify_client optional; # locations below require SUCCESS ssl_verify_depth 3; # Client cert revocation (optional) # ssl_crl /etc/nginx/ssl/iot.crl; # Forward client cert details upstream proxy_set_header X-SSL-Client-Verify $ssl_client_verify; proxy_set_header X-SSL-Client-Serial $ssl_client_serial; proxy_set_header X-SSL-Client-Issuer $ssl_client_i_dn; proxy_set_header X-SSL-Client-DN $ssl_client_s_dn; proxy_set_header X-SSL-Client-Cert $ssl_client_escaped_cert; proxy_read_timeout 3600s; proxy_send_timeout 3600s; error_page 495 = @mtls_error; # location @mtls_error { # return 403 "ERR_SSL_VERSION_OR_CIPHER_MISMATCH \n"; # } location @mtls_error { return 403 "mTLS required or client certificate invalid.\n"; } location = /_mtls_check { return 200 "$ssl_client_verify\n$ssl_client_s_dn\n$ssl_client_i_dn\n"; } # ---- mTLS-protected paths ---- location ^~ /api/records/upload { if ($ssl_client_verify != SUCCESS) { return 495; } rewrite ^/api/(.*)$ /$1 break; proxy_pass http://snoop-api:8080; } location ^~ /api/tasks { if ($ssl_client_verify != SUCCESS) { return 495; } rewrite ^/api/(.*)$ /$1 break; proxy_pass http://snoop-api:8080; } location ^~ /api/renew { if ($ssl_client_verify != SUCCESS) { return 495; } rewrite ^/api/(.*)$ /$1 break; proxy_pass http://snoop-api:8080; } # MediaMTX HLS location ^~ /hls/ { proxy_pass http://mediamtx:8888/; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } # MediaMTX WebRTC (WHIP/WHEP/test) location ^~ /whip/ { if ($ssl_client_verify != SUCCESS) { return 495; } proxy_pass http://mediamtx:8889; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_request_buffering off; client_max_body_size 35m; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } # MQTT WS entry points (guarded by mTLS) location = /loc { if ($ssl_client_verify != SUCCESS) { return 495; } proxy_pass http://emqx:8083/mqtt; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } location = /gtasks { if ($ssl_client_verify != SUCCESS) { return 495; } proxy_pass http://emqx:8083/mqtt; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } location ^~ /api/ { proxy_pass http://snoop-api:8080/; # trailing slash strips /api proxy_http_version 1.1; 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; # (Optional) WS/SSE friendly defaults proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } }