Controller-UI security settings

Controller-UI security settings

These examples cover various TLS and cross-site related settings of the UI server.

Running without TLS

The Controller-UI serves plain HTTP on -http-port by default, but it assumes running behind a TLS-terminating reverse proxy or dedicated TLS terminator such as Hitch. As a result, the authentication cookies are set with the Secure flag, which browsers only send over HTTPS.

If you use a plaintext connection all the way to the browser, logins will not stick unless the Secure flag is disabled with -secure-cookies=false.

The start command of the Controller-UI should look like this:

varnish-controller-ui -api-hosts=http://api-gw:8002 -secure-cookies=false

To serve HTTPS from the UI server itself, on -https-port (default 8443), see HTTPS.

Cross-site requests

Cross-site protection is based on the browser Origin and Sec-Fetch-Site request headers. State-changing requests from another origin are rejected unless that origin is listed in -trusted-origins, which also serves as the CORS allowed-origins list:

varnish-controller-ui -api-hosts=http://api-gw:8002 -trusted-origins=https://tools.example.com

A rejected request is answered with 403 cross-origin request forbidden and logged, so the origin that has to be allowed can be read from the log:

Rejected cross-origin request: method=POST path=/api/v1/auth/login origin="https://other.example.com" sec-fetch-site="cross-site"

Origins are compared as scheme and host, plus the port if it is not the default one, so list every origin the browser actually uses.

Individual paths can be taken out of the browser protections entirely with -browser-security-exclusions, for non-browser clients, webhooks and public endpoints. An excluded path skips the cross-site check, CORS and the security response headers, and on /api/ it is proxied without the UI server requiring or injecting the authentication cookie, so the request has to carry its own credentials. Paths are matched exactly, not as prefixes:

varnish-controller-ui -api-hosts=http://api-gw:8002 -browser-security-exclusions=/healthz,/metrics

Security response headers

Argument Default Effect
-hsts false Sends Strict-Transport-Security: max-age=31536000. Only honored by browsers over HTTPS.
-csp a restrictive policy Sends Content-Security-Policy with the given policy. An empty value disables the header.
-x-frame-options deny Sends X-Frame-Options. sameorigin allows framing from the same origin, an empty value disables the header and leaves framing to the CSP frame-ancestors directive.

X-Content-Type-Options: nosniff is always sent.

Framing the UI from another origin needs that origin in -trusted-origins as well, since the authentication cookies are SameSite=Strict and the cross-site check rejects the request otherwise.

Behind a reverse proxy

If you run the Controller-UI behind Varnish or any other reverse proxy, make sure that the reverse proxy does not modify the Host header, so that the origin the browser sees matches the origin the UI server sees. Set X-Forwarded-Proto as well, which is how the UI server knows the external scheme.

For correct client IPs in the request log behind a proxy, list the proxy addresses in -trusted-proxy-cidrs; only then is X-Forwarded-For trusted. Without it any client could choose the address the Controller logs, rate-limits and locks out on.

varnish-controller-ui -api-hosts=http://api-gw:8002 -trusted-proxy-cidrs=10.0.0.0/8

Preserving the Host header in Varnish

In Varnish (both Varnish Enterprise and the open source Varnish Cache), do not modify the req.http.host variable in your VCL.

Preserving the Host header in nginx

If you run the Controller-UI behind nginx, add the following configuration directives behind your proxy_pass ... entry:

proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;

®Varnish Software, Wallingatan 12, 111 60 Stockholm, Organization nr. 556805-6203