diff options
Diffstat (limited to 'docs/operations.md')
| -rw-r--r-- | docs/operations.md | 54 |
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 |
