aboutsummaryrefslogtreecommitdiffstats
path: root/docs/operations.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/operations.md')
-rw-r--r--docs/operations.md68
1 files changed, 68 insertions, 0 deletions
diff --git a/docs/operations.md b/docs/operations.md
index fe09537..fae3b6c 100644
--- a/docs/operations.md
+++ b/docs/operations.md
@@ -103,6 +103,74 @@ One node at a time, restarting each with its files. What to know:
switching ([`remakes.md`](remakes.md), Limits). Don't remake while the swarm runs two versions.
* **The dashboard** reloads by itself once its node serves another version of it.
+## Behind a reverse proxy
+
+The HTTP API speaks plain HTTP on `127.0.0.1:3000`; a reverse proxy in front adds TLS,
+and can authenticate users itself (single sign-on, client certificates, its own
+password file). It then names the user in a header, which the node believes from where
+the proxy connects alone (`--user-header`, `--trusted-proxies`); requests from
+elsewhere still need `--htpasswd`, which becomes optional. The proxy must set or clear
+that header on every request, or a client could set it itself.
+
+Where the proxy connects decides who can pose as it. Every user of a host connects
+from its loopback addresses, so trusting those (as `--trusted-proxies` does by
+default) trusts everyone on the host. A Unix socket is better: `--bind-addr` takes
+`unix:PATH` too, as many places as given, and makes the socket readable and writable
+by the node's user and group only, so only members of that group (the proxy's user,
+added to it) can connect at all. Trust it alone with `--trusted-proxies unix`, and keep
+a TCP address for the rest (a command-line tool, with `--htpasswd`):
+
+```sh
+continuum --bind-addr 127.0.0.1:3000,unix:/run/continuum/api.sock \
+ --user-header X-Remote-User --trusted-proxies unix --htpasswd htpasswd ...
+```
+
+```nginx
+location / {
+ auth_basic "continuum";
+ auth_basic_user_file /etc/nginx/continuum.htpasswd;
+ proxy_set_header X-Remote-User $remote_user;
+ proxy_pass http://unix:/run/continuum/api.sock;
+}
+```
+
+The directory of the socket must exist and let the proxy's user through (with systemd,
+`RuntimeDirectory=continuum` and `RuntimeDirectoryMode=0750`, the proxy's user in the
+node's group). A socket left behind by an earlier run is replaced, and the node removes
+its own when it stops. With a proxy on another host, trust its address instead, and
+have the node listen where only that host reaches it.
+
+Where a socket won't do, the host's firewall can keep everyone but the proxy's user
+off the API's port, as Linux filters local connections by the user that opens them.
+With nftables, and nginx running as `www-data`:
+
+```
+table inet continuum {
+ chain output {
+ type filter hook output priority 0;
+ oifname "lo" tcp dport 3000 meta skuid != "www-data" reject
+ }
+}
+```
+
+(with iptables: `-A OUTPUT -o lo -p tcp --dport 3000 -m owner ! --uid-owner www-data -j
+REJECT`). The loopback addresses can then stay trusted; root can still get past it, as it
+can past a socket's permissions. Every address of the API on loopback is trusted alike,
+so a command-line tool of another user goes through the proxy then.
+
+The names the proxy gives are the users of `--protocol-writers` and of who wrote what
+([`api.md`](api.md)).
+
+What streams (`/stream`, the dashboard's events) says `X-Accel-Buffering: no`, so nginx
+passes it on as it comes, and says it's alive while nothing changes: the dashboard's
+events every 15 s, `/stream` every 30 s (a blank line, `--stream-heartbeat`), within
+nginx's `proxy_read_timeout` (60 s). With a proxy that gives up sooner, lower
+`--stream-heartbeat` below its timeout.
+
+The API may also be served under a path of its own (`location /continuum/ {
+proxy_pass http://unix:/run/continuum/api.sock:/; }`): its redirects (`Location`), the dashboard's
+requests and the Swagger UI's are relative, so they stay under it.
+
## What to alert on
From the metrics (`GET /admin/stats` as Prometheus, [`measurements.md`](measurements.md)):