aboutsummaryrefslogtreecommitdiffstats
path: root/docs/operations.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/operations.md')
-rw-r--r--docs/operations.md54
1 files changed, 45 insertions, 9 deletions
diff --git a/docs/operations.md b/docs/operations.md
index 4f59208..da73fce 100644
--- a/docs/operations.md
+++ b/docs/operations.md
@@ -107,23 +107,59 @@ One node at a time, restarting each with its files. What to know:
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 the
-proxy's address alone (`--user-header`, `--trusted-proxies`, by default this host's
-loopback addresses); 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. With nginx:
+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://127.0.0.1:3000;
+ 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
+ }
}
```
-and the node started with `--user-header X-Remote-User`. The names the proxy gives are
-the users of `--protocol-writers` and of who wrote what ([`api.md`](api.md)).
+(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. The dashboard's events have a keepalive every 15 s; `/stream`
@@ -131,7 +167,7 @@ sends nothing while nothing changes, so nginx ends a quiet one after its
`proxy_read_timeout` (60 s): raise that for it, or have clients reconnect.
The API may also be served under a path of its own (`location /continuum/ {
-proxy_pass http://127.0.0.1:3000/; }`): its redirects (`Location`), the dashboard's
+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