Graphical User Interface

The Varnish Controller UI is served by the UI server (varnish-controller-ui), a Varnish Controller component. It does the following:

  • Serves the UI assets (a single page application) over HTTP and, optionally, HTTPS
  • Proxies /api/ to one or more API-GW instances and keeps the authentication tokens in HttpOnly cookies instead of handing them to the browser
  • Applies the browser security policies (CSRF, CORS, CSP, HSTS, X-Frame-Options)
  • Implements single sign-on against an organization’s IDP
  • Optionally connects to NATS to receive its configuration, TLS certificates and ACME challenges from brainz

UI servers are not registered individually in the Controller and have no per-instance configuration. As many as needed can be run behind a load balancer, and they all share one set of configuration values.

Installation

The following components of Varnish Controller must be installed and running before installation of the GUI.

  • NATS
  • PostgreSQL
  • Brainz
  • API-GW
  • Varnish and Varnish Agent

Debian/Ubuntu Installation

  sudo apt install varnish-controller-ui

RPM-based

  sudo dnf -y install varnish-controller-ui

Configuration options for the UI Server

The UI server can be started with various configurations. Since the ui-server is running as varnish-controller user it will not be able to start on a privileged port(< 1024), such as port 80. This can be instead handled by iptables, authbind or setcap.

Example using setcap: setcap 'cap_net_bind_service=+ep' /usr/bin/varnish-controller-ui

All arguments can also be set as environment variables (VARNISH_CONTROLLER_<ARGUMENT>, e.g. VARNISH_CONTROLLER_API_HOSTS) or in the [ui-server] section of a configuration file (-config). A skeleton configuration file can be printed with -generate-config. Order of precedence, where 1 has the highest precedence:

  1. Configuration overrides from the Controller (see Controller integration over NATS)
  2. Arguments to the binary
  3. Environment variables
  4. Configuration file

Arguments that takes a duration value, such as -read-timeout, uses Go time Duration format. Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”. A combination of units can be used to express a duration, e.g. 1h10m30s.

The Runtime column states whether the setting can be changed on a running UI server from the Controller. Booleans are given as -flag=true or -flag=false. A bare -flag means true. A full list of arguments is also printed by varnish-controller-ui --help.

HTTP and HTTPS server

Argument Default Runtime Description
-http-host 0.0.0.0 no IP/host to listen on, for both the HTTP and the HTTPS listener.
-http-port 8080 no Plain HTTP port. The UI is reachable here.
-https-port 8443 no HTTPS port. The listener runs whenever at least one certificate is available, either pushed from the Controller or from -cert/-key, and stops again when the last one is removed.
-tls false no Load the local certificate files given by -cert and -key. This does not by itself decide whether HTTPS runs, see HTTPS.
-cert server.crt no Local TLS certificate (public key) for the HTTPS listener.
-key server.key no Local TLS private key for the HTTPS listener.
-certificates yes Comma-separated list of Controller TLS certificate IDs to serve on -https-port, selected per request via SNI. The first ID is the fallback when SNI matches none of them. Requires -enable-nats.
-read-timeout 1m0s no Maximum duration for reading an entire HTTP request, including the body. 0s disables the timeout.
-write-timeout 1m0s no Maximum duration before timing out writes of the HTTP response. 0s disables the timeout. See Timeouts.
-trusted-proxy-cidrs no Comma-separated list of trusted proxy CIDRs (e.g. 10.0.0.0/8,192.168.0.0/16). When a request arrives from an address in this list, the client IP is taken from X-Forwarded-For. Empty (default) makes X-Forwarded-For fully untrusted.

UI assets

Argument Default Runtime Description
-static-path /usr/share/varnish-controller-ui/www/ no Directory containing the UI assets.
-index-file index.html no Name of the single page application entry point within -static-path. Unknown paths fall back to this file.
-compress true yes Serve precompressed .br/.gz siblings of an asset when the client accepts them.
-cache 168h0m0s yes Browser cache TTL for static assets (Cache-Control: max-age). 0s disables caching. The index file is always served with no-store.

API proxy

Argument Default Runtime Description
-api-hosts yes Comma-separated list of API-GW base URLs to proxy /api/ to, without trailing slash (e.g. https://api.example.com). Several hosts are used round-robin.
-api-tls-allow-insecure false yes Accept unverified TLS certificates when proxying to the API-GW.
-compile-timeout 1m0s no Timeout for compilation of VCL, also used to derive the proxy and browser timeouts. Set it to the same value as on brainz and the API-GW. See Timeouts.

Browser security and cookies

Argument Default Runtime Description
-trusted-origins yes Comma-separated list of trusted cross-origins (e.g. https://foo.example.com). Used both to allow unsafe (state-changing) requests past the CSRF check and as the CORS allowed-origins list.
-secure-cookies true yes Set the Secure flag on the cookies the UI server issues. Keep enabled whenever the browser reaches the UI over HTTPS, including when TLS is terminated in front of the UI server.
-cookie-domain yes Domain attribute for the authentication cookies. Empty means host-only.
-hsts false yes Send Strict-Transport-Security: max-age=31536000. Only honored by browsers over HTTPS.
-csp see below yes Content-Security-Policy response header value. An empty value disables the header.
-x-frame-options deny yes X-Frame-Options response header value, either deny or sameorigin. An empty value disables the header and leaves framing to the CSP frame-ancestors directive.
-browser-security-exclusions yes Comma-separated request paths that bypass the browser protections entirely: the cross-origin check, CORS, the security response headers, and the requirement for an authentication cookie on /api/ (the request is proxied with whatever credentials it carries itself). Exact paths, not prefixes. For non-browser clients, webhooks and public endpoints.

The default -csp value is:

default-src 'self'; font-src 'self' data:; script-src 'self' 'unsafe-eval'; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';base-uri 'self';form-action 'self'

SSO

Argument Default Runtime Description
-sso-org-hosts yes Comma-separated list of <host>=<organization> mappings, used by /sso to find the organization from the host the request came in on when no org query parameter is given (e.g. acme.example.com=acme,foo.example.com=foo). Ports are ignored and hosts are matched case-insensitively.

NATS

NATS is optional for the UI server. These settings only take effect with -enable-nats.

Argument Default Runtime Description
-enable-nats false no Connect to NATS to receive TLS certificates, configuration and ACME HTTP-01 challenges from the Controller. Without it the UI server runs standalone.
-nats-server localhost:4222 no host:port of the NATS server (user:pass@host:port).
-nats-token no NATS token for authentication.
-nats-ca no Own CA certificate for the NATS TLS connection.
-nats-client-cert no NATS client TLS certificate.
-nats-client-key no NATS client TLS key.
-nats-insecure-tls false no Do not verify the NATS server certificate. Not for production.
-nats-timeout 10s no Timeout for sending messages over NATS.
-nats-reconnect-time 5s no Maximum time between NATS connection retries.
-nats-ping-interval 2s no Time between pings towards the NATS servers.
-nats-ping-outs 2 no Number of missed pings before reconnecting.

General

Argument Default Runtime Description
-base-dir /var/lib/varnish-controller/varnish-controller-ui-server/ no Directory for files the UI server generates, such as the persisted configuration override. Must be writable by the user the UI server runs as.
-user varnish-controller no User to run as when started as root.
-group varnish-controller no Group to run as when started as root.
-log warning yes Loglevel, valid: debug, info, warning, error, quiet.
-log-timestamp true yes Add a timestamp to log lines.
-request-log false yes Log every incoming HTTP request (method, host, URL and headers).
-config no Path to a configuration file.
-generate-config false no Print a configuration file with all parameters and exit.
-version false no Show version and exit.

The -http-host and -http-port is used to define where the UI will be reachable, the -api-hosts is used to define to which API-GW the UI talks to.

Minimal setup to run the UI server, the UI server will run with all default values. The UI will be reachable at the server’s IP port 8080. For a plain-HTTP setup without any TLS in front, disable the Secure flag on the cookies:

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

To run the UI server on a different host and port (host: 192.0.0.1 and port: 5050) run the following. Your application can now be reached at 192.0.0.1:5050

varnish-controller-ui -api-hosts=http://api-gw:8002 -http-port 5050 -http-host 192.0.0.1 -secure-cookies=false

If you want to run your own UI you can run the service and point to your single page application. The -index-file is used to define which file to load within the -static-path.

varnish-controller-ui -api-hosts=http://api-gw:8002 -static-path=/path/to/your/ui -index-file=app.html

Controller Integration over NATS

With -enable-nats, the UI server connects to NATS and receives from the controller:

  • Configuration: the settings marked Runtime above are stored under the global ui-server component in the Controller and broadcast to all UI servers on change, so they can be changed without a restart. Since UI servers are nameless, the configuration is addressed by type rather than per instance, and only a system administrator may change it:

    vcli configs list -f type=ui-server
    vcli configs update -f type=ui-server -f flag=log -v debug
    vcli configs delete -f type=ui-server -f flag=log
    

    A setting given on the command line, in the environment or in the configuration file cannot be overridden this way. Clear it there first, and the override takes effect. The last applied configuration is persisted under -base-dir, so a UI server keeps it across a restart and while NATS is unavailable. brainz re-sends it periodically, so a UI server that was unreachable when it changed heals by itself. See Config/ConfigSets for the general mechanism.

  • TLS certificates: controller-managed certificates listed in -certificates are pushed by brainz and served on -https-port, see HTTPS.

  • ACME challenges: HTTP-01 challenges for the UI server’s own domains are answered by the UI server itself on /.well-known/acme-challenge/, so no VCL and no agent is involved. Certificate authorities always request the challenge on port 80, so that port on the UI’s hostname has to reach the UI server’s -http-port, through a port mapping or a proxy that forwards the path.

NATS is optional: without it the UI server still serves the UI and proxies the API, using only the -cert/-key certificate. Single sign-on requires NATS.

HTTPS

The plain HTTP listener on -http-port always runs. The HTTPS listener on -https-port runs for as long as at least one certificate is available, from either of two sources:

  • Local files: -tls together with -cert and -key.
  • The Controller: certificate IDs in -certificates, pushed over NATS.

Certificates from the Controller take precedence over the local files, and the local certificate serves again if the pushed list is cleared. A missing or unusable local certificate file is not fatal. It is logged, and the UI server keeps serving, since the Controller may still supply a certificate.

-certificates is a comma-separated list of TLS certificate IDs as known to the Controller, set by a system administrator:

vcli certificates ls
vcli configs update -f type=ui-server -f flag=certificates -v 3,7

The certificate matching the SNI name of the connection is served, with the first ID in the list as the fallback for a connection that matches none of them. A wildcard certificate matches one label, as it does in TLS. A non-existent ID is rejected when the configuration is set, a certificate that has not been obtained yet is skipped until it is, and a renewal is pushed to the fleet automatically. The certificates are held in memory only, so their private keys never touch the UI server’s disk. This is what makes ACME certificates usable for the UI itself.

Single Sign-On (SSO)

Since version 7.6.0, the UI server can log a user in directly through the organization’s IDP. The user only sees the IDP’s own login screen, and no login screen at all when they already have a session with the IDP.

Browsing to https://<ui-host>/sso?org=<organization> starts the login. The org parameter is optional: without it, the organization is looked up from the host the request came in on through the -sso-org-hosts mapping (e.g. -sso-org-hosts acme.example.com=acme). If no organization can be determined, the user is sent to the regular login page.

The browser is then redirected to the IDP and returns to /sso/callback, which the UI server forwards to the API-GW. Session handling, account provisioning and the authentication cookies are identical to an interactive IDP login, and the client secret never leaves brainz.

Requirements:

  • The UI server must be connected to NATS (-enable-nats).
  • The organization must have an IDP configured in the controller.
  • Add https://<ui-host>/sso/callback as an allowed redirect URI to the organization’s existing OIDC client at the IDP.
  • The flow needs a refresh token, so offline_access (or the IDP’s equivalent) must be enabled for the client.

The UI must be reached by the same hostname as the registered redirect URI; reaching it by another hostname breaks the token exchange. A failed or cancelled SSO attempt ends on the regular login page, and a retry guard limits SSO to one automatic attempt per browser per 5 minutes.

Browser security and reverse proxies

The UI server rejects unsafe (state-changing) cross-origin requests using the Sec-Fetch-Site and Origin request headers. There is no CSRF token. The same-origin case needs no configuration, neither over HTTP nor over HTTPS. Two cases do:

  • The UI is reached over plain HTTP. Set -secure-cookies=false, otherwise the browser drops the Secure cookies the UI server sets and the login does not complete.
  • Another origin has to be allowed to call the UI server, or the UI runs behind a proxy that changes the Host header. Add the origin to -trusted-origins.

The security settings for the Controller-UI examples cover these, the security response headers, and what a reverse proxy has to forward so that client addresses and origins survive the hop.

Timeouts

The timeouts along the chain of browser, UI server, API-GW and brainz must each outlast the layer they wrap. Otherwise a timeout is reported by the outer layer, which knows nothing about what actually went wrong, and the API-GW’s answer is lost, including the trace ID needed to find the request in the API-GW and brainz logs.

The UI server derives its upstream and browser timeouts from -compile-timeout, adding a margin per boundary, so setting -compile-timeout to the same value as on the API-GW and brainz is normally all that is needed. -write-timeout is raised automatically if the configured value would expire before the UI server stops waiting for the API-GW, and that is logged as a warning at startup.

Identifying the UI Server

The UI server identifies itself with a Server: VarnishControllerUI/<version> response header. Since the UI server proxies /api/ requests, it is possible to point vcli at it by mistake; vcli detects this and refuses with a message to use an API-GW endpoint instead (default port 8002). It also refuses an API-GW whose major or minor version differs from its own. See Errors.

If a reverse proxy rewrites the Server header, the check is skipped, and vcli instead reports a login response that carries no token.

Deprecated and renamed arguments

The arguments of the previous standalone UI server still work. A renamed one is translated and logs a warning, and one whose feature is gone is accepted and ignored. The same applies to the VARNISH_UI_SERVER_-prefixed environment variables, which are read as a fallback for the VARNISH_CONTROLLER_ ones. Both will be removed in a future release, so update your configuration.

For the full mapping, and the behavior changes that come with it, see Upgrading.

UI behind Varnish

It is possible to run the UI behind Varnish. Just make sure that you do not cache the API requests or just do not cache the UI entirely. Here is an example VCL file to run the UI with Varnish:

vcl 4.1;

import std;

backend controller-ui {
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv {
    # Force https
    if (std.port(local.ip) == 80) {
        set req.http.Location = "https://" + req.http.host + req.url;
        return (synth(301));
    }

    if (req.http.host == "controller.varnish-software.com") {
        set req.backend_hint = controller-ui;
        return (pass);
    } else {
        return(synth(404));
    }
}

sub vcl_synth {
    # Redirects
    if (req.http.location) {
        if (resp.status == 301) {
            set resp.http.Location = req.http.location;
            return (deliver);
        }
        if (resp.status == 302) {
            set resp.http.Location = req.http.location;
            return (deliver);
        }
    }
}

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