5.8 KiB
Put ThothII behind Nginx
This example assumes Nginx runs on the Linux host, ThothII frontend listens only on
127.0.0.1:8080, and a separate authentication gateway validates the user's real login/session.
Replace the documentation domain, certificate paths, and auth-gateway address.
For ThothII-managed generic OIDC, preserve the configured public origin and the exact callback
/api/auth/oidc/callback; the browser and API remain one same-origin surface. Run
tht auth check and workspace Test after proxy changes.
Trust boundary
Nginx is the only public listener. It terminates TLS, performs an auth_request, and proxies only
to frontend; frontend then uses its private same-origin /api route to reach core. Never proxy
the public listener directly to core port 8787.
The authentication gateway must return 2xx only after validating a real credential or session. It
returns normalized X-Thoth-Principal-* and X-Thoth-Is-Admin response headers. Nginx clears all
client-supplied public and private-hop identity headers and copies only those successful auth
response values into X-Thoth-Trusted-* on the private hop to frontend.
Forwarding identity headers alone does not authenticate a user.
Do not substitute a static header, network allowlist, or client-provided header for the authentication gateway. The admin value must come from reviewed identity-provider authorization, not from a username supplied by the browser.
Example configuration
Install an Nginx build that includes ngx_http_auth_request_module. Save a reviewed version of
this server block under the host's Nginx configuration directory:
server {
listen 80;
server_name thoth.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name thoth.example.com;
ssl_certificate /etc/nginx/tls/thoth/fullchain.pem;
ssl_certificate_key /etc/nginx/tls/thoth/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location = /_authenticate {
internal;
proxy_pass http://auth-gateway:4180/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Method $request_method;
# The auth service derives identity from the real login/session, never these headers.
proxy_set_header X-Authenticated-User "";
proxy_set_header X-Thoth-Principal-Issuer "";
proxy_set_header X-Thoth-Principal-Subject "";
proxy_set_header X-Thoth-Principal-Display-Name "";
proxy_set_header X-Thoth-Is-Admin "";
proxy_set_header X-Thoth-Trusted-Principal-Issuer "";
proxy_set_header X-Thoth-Trusted-Principal-Subject "";
proxy_set_header X-Thoth-Trusted-Principal-Display-Name "";
proxy_set_header X-Thoth-Trusted-Is-Admin "";
}
location / {
auth_request /_authenticate;
auth_request_set $thoth_principal_issuer
$upstream_http_x_thoth_principal_issuer;
auth_request_set $thoth_principal_subject
$upstream_http_x_thoth_principal_subject;
auth_request_set $thoth_principal_display_name
$upstream_http_x_thoth_principal_display_name;
auth_request_set $thoth_is_admin
$upstream_http_x_thoth_is_admin;
# Discard browser claims. Carry only successful auth-subrequest values to frontend.
proxy_set_header X-Authenticated-User "";
proxy_set_header X-Thoth-Principal-Issuer "";
proxy_set_header X-Thoth-Principal-Subject "";
proxy_set_header X-Thoth-Principal-Display-Name "";
proxy_set_header X-Thoth-Is-Admin "";
proxy_set_header X-Thoth-Trusted-Principal-Issuer $thoth_principal_issuer;
proxy_set_header X-Thoth-Trusted-Principal-Subject $thoth_principal_subject;
proxy_set_header X-Thoth-Trusted-Principal-Display-Name $thoth_principal_display_name;
proxy_set_header X-Thoth-Trusted-Is-Admin $thoth_is_admin;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Connection "";
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
# SSE must reach the browser without response buffering or cache delay.
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
add_header X-Accel-Buffering no always;
}
}
Keep the certificate private key outside the ThothII tree. Do not log cookies, authorization headers, auth response bodies, or trusted identity headers.
Validate and reload
First keep the public firewall rule closed. Confirm ThothII responds only on loopback, validate the full Nginx configuration, then reload without stopping existing connections:
curl --fail http://127.0.0.1:8080/health
sudo nginx -t
sudo systemctl reload nginx
If nginx -t reports that auth_request is unknown, install the distribution package/module that
provides it. Do not remove authentication to make the syntax check pass.
Test authentication and SSE
Open the firewall only after all of these pass:
- An unauthenticated HTTPS request is redirected to login or returns 401/403.
- Supplying forged
X-Thoth-Principal-*,X-Thoth-Is-Admin, orX-Thoth-Trusted-*request headers does not grant access. - A real authenticated non-admin can use the application but cannot open Pi Management.
- A real authenticated admin can use Pi Management.
- A browser session receives live model updates for longer than the default proxy timeout without
batching; a protected
curl --no-buffertest using a real short-lived login cookie is also acceptable. Delete the cookie jar immediately afterward.
Re-run these checks after changing the identity provider, auth gateway, Nginx, or ThothII release.