NGINX mTLS Proxy
Terminate browser TLS and device mTLS at the reverse proxy while keeping the API local-only.
NGINX mTLS proxy
Use the checked-in NGINX template when you want Sentinel Secure X to terminate browser TLS and device mTLS at the proxy while the Flask API stays on the local Waitress listener.
Recommended flow
- Keep the API bound to the local host, for example
http://127.0.0.1:8000, and do not expose that listener directly. - In the control-plane
.envfile, keepSENTINEL_REQUIRE_DEVICE_MTLS=trueand setSENTINEL_TRUST_PROXY_CERT_HEADERS=truebecause NGINX will forward the verified client-certificate metadata that the heartbeat path depends on. - Run the proxy-aware control-plane preflight:
python3 -m server.preflight \
--env-file /opt/sentinel-secure-x/.env \
--expect-proxy-cert-headers
- Render and install the proxy config:
sudo python3 deploy/nginx/install_mtls_proxy.py \
--output-path /etc/nginx/conf.d/sentinel-secure-x.conf \
--server-name sentinel.example.com \
--tls-cert-path /etc/nginx/certs/server.crt \
--tls-key-path /etc/nginx/certs/server.key \
--client-ca-path /etc/nginx/certs/device-ca.crt \
--upstream-url http://127.0.0.1:8000 \
--env-file /opt/sentinel-secure-x/.env \
--check-nginx-config \
--reload-nginx
- Smoke-check the proxy path over HTTPS:
python3 deploy/nginx/smoke_test_proxy.py \
--base-url https://sentinel.example.com
This verifies that /api/health/live and /api/health/ready are reachable through the proxy and that POST /api/heartbeat is rejected with 401 when no client certificate is presented.
If you want a stronger end-to-end probe with a real device certificate, add:
python3 deploy/nginx/smoke_test_proxy.py \
--base-url https://sentinel.example.com \
--client-cert /path/to/device.crt \
--client-key /path/to/device.key \
--device-id device-demo-01
If the proxy uses a private CA, add --ca-file /path/to/proxy-ca.pem or --insecure for an intentional one-off check. When you use the authenticated probe, the device certificate common name should match --device-id.
Notes
- The rendered config keeps browser access open, but it requires a verified client certificate specifically on
POST /api/heartbeat. - The proxy forwards the certificate headers Sentinel expects when
SENTINEL_TRUST_PROXY_CERT_HEADERS=true, including subject, issuer, serial number, certificate PEM, fingerprint, and validity timestamps. - The helper defaults the upstream origin to
http://127.0.0.1:8000, which matches the checked-in systemd API service. - The installer can run the same proxy smoke test automatically when you add
--smoke-test. - For a quick local preview, run
make nginx-proxy-renderor pass custom arguments withmake nginx-proxy-render NGINX_PROXY_ARGS='--stdout --server-name sentinel.example.com'. - For a quick local proxy check, run
make nginx-proxy-smoke NGINX_PROXY_SMOKE_ARGS='--base-url https://sentinel.example.com --ca-file /path/to/proxy-ca.pem'. - Use
--stdoutto preview the rendered config without writing it. - If you already manage NGINX elsewhere, you can still use the template as a reference as long as the forwarded certificate headers and heartbeat protection remain equivalent.