diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/api.md | 7 | ||||
| -rw-r--r-- | docs/dashboard.md | 6 | ||||
| -rw-r--r-- | docs/example.toml | 14 | ||||
| -rw-r--r-- | docs/openapi.yaml | 3 | ||||
| -rw-r--r-- | docs/operations.md | 22 | ||||
| -rw-r--r-- | docs/trust.md | 5 |
6 files changed, 47 insertions, 10 deletions
diff --git a/docs/api.md b/docs/api.md index 5b4dac4..e813845 100644 --- a/docs/api.md +++ b/docs/api.md @@ -31,8 +31,9 @@ curl -u alice:secret -X POST 'localhost:3000/store/stats/visits?increment=1' # * Bodies are JSON (`application/json`, the default), text (`text/plain`, stored as a string), or a value of a type of its own (Types); answers are JSON, or as `Accept` asks. Numbers in JSON are stored as integers where they fit, as floats otherwise. -* Every request needs a user from the node's `--htpasswd` (basic authentication). Users - are all alike: each can read and write everything but protocol data (below). +* Every request needs a user from the node's `--htpasswd` (basic authentication), or + named by a reverse proxy in front that authenticated it (`--user-header`; + [`operations.md`](operations.md), Behind a reverse proxy). Users are all alike: each can read and write everything but protocol data (below). * A key with `/` in it, or an empty key, can be written inside a value at its parent, but not named in a path: read it through the parent. @@ -222,7 +223,7 @@ Load balancers can send requests to any ready node: `/ready` is the check. Every change records the node that committed it (as its Automerge author) and, for a write through the API, the user who wrote it (in its message, as a trailer like Git's `Signed-off-by:`: `User: alice`), with when (by the writer's clock). User names are -each node's own (`--htpasswd`), so who wrote it is that user at that node: `alice` on +each node's own (`--htpasswd`, or its proxy's), so who wrote it is that user at that node: `alice` on two nodes is one person only where their operators make it so, which nothing checks. `GET /admin/changes` lists them, newest first, with the paths each touched; `GET /admin/changes/settings` those that diff --git a/docs/dashboard.md b/docs/dashboard.md index 2d55db1..6a75036 100644 --- a/docs/dashboard.md +++ b/docs/dashboard.md @@ -14,10 +14,10 @@ cargo build --release --features mycelium,dashboard ``` It is at `/admin/dashboard` on the node's HTTP API, by default -`http://127.0.0.1:3000/admin/dashboard`, and asks for a user from `--htpasswd`, as the -rest of the API does. The API listens on localhost unless `--bind-addr` says otherwise; +`http://127.0.0.1:3000/admin/dashboard`, and asks for a user from `--htpasswd` (or one +a reverse proxy names, `--user-header`), as the rest of the API does. The API listens on localhost unless `--bind-addr` says otherwise; to look at a node on another machine, forward the port (`ssh -L 3000:localhost:3000 -<machine>`) or put a TLS proxy in front ([`trust.md`](trust.md)). +<machine>`) or put a TLS proxy in front ([`operations.md`](operations.md), Behind a reverse proxy). Every node has its own dashboard, and shows the swarm as that node sees it: what it knows of the others, what they report to it, and its own figures. diff --git a/docs/example.toml b/docs/example.toml index 95b6f52..67f2cfd 100644 --- a/docs/example.toml +++ b/docs/example.toml @@ -51,10 +51,20 @@ node-key-file = "/var/lib/continuum/node.key" # bind-addr = "127.0.0.1:3000" # Users of the HTTP API (an htpasswd file with Argon2 hashes). Required (but with -# relay-only: without it, no HTTP API). +# user-header, or relay-only: without either, no HTTP API). htpasswd = "/etc/continuum/htpasswd" -# HTTP API users (from htpasswd) who may write protocol data under /store/_continuum +# A request header naming the user, set by a reverse proxy in front of the HTTP API that +# authenticated the request itself (e.g. X-Remote-User, from nginx's $remote_user). +# Believed only from trusted-proxies; other requests need htpasswd's basic +# authentication. The proxy must set or clear it on every request. +# user-header = "X-Remote-User" + +# Addresses (an IP address, or a range as ADDRESS/BITS) of the reverse proxies whose +# user-header is believed. +# trusted-proxies = ["127.0.0.1", "::1"] + +# HTTP API users (from htpasswd or user-header) who may write protocol data under /store/_continuum # (remake records, members, forget records), e.g. to repair a stuck remake. Such writes # act on the whole swarm and are logged. Epoch metadata and the swarm ID are never # writable. diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 78912fa..5460a9c 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -7,7 +7,8 @@ info: as JSON by path, that syncs over Mycelium, libp2p or Tor. Every endpoint requires HTTP basic authentication (users from `--htpasswd`, - Argon2 hashes), except `/health`, `/ready`, the Swagger UI (`/swagger-ui`) and + Argon2 hashes), or a user named by a trusted reverse proxy in front + (`--user-header`), except `/health`, `/ready`, the Swagger UI (`/swagger-ui`) and this description (`/openapi.yaml`). `/debug/*` exists only with `--debug-api`. diff --git a/docs/operations.md b/docs/operations.md index fe09537..be185f1 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -103,6 +103,28 @@ 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 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: + +```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; +} +``` + +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)). + ## What to alert on From the metrics (`GET /admin/stats` as Prometheus, [`measurements.md`](measurements.md)): diff --git a/docs/trust.md b/docs/trust.md index 521a175..dc80045 100644 --- a/docs/trust.md +++ b/docs/trust.md @@ -34,7 +34,10 @@ secret like the swarm key: readable by its owner only (a node warns otherwise; a it creates is). **The HTTP API.** Every endpoint but the API description needs a user from -`--htpasswd`. It listens on `127.0.0.1:3000` by default (`--bind-addr`) and speaks +`--htpasswd`, or one a reverse proxy names (`--user-header`): the node believes that +header from `--trusted-proxies` alone, but from those blindly, so whoever can send +requests from such an address (another user of the same host, with the default +loopback addresses) can be any user. It listens on `127.0.0.1:3000` by default (`--bind-addr`) and speaks plain HTTP: to expose it, put a TLS proxy in front. All users may read and write user data and use the admin endpoints (remakes, forgetting peers, exports, log filters). There are no read-only users: whoever may open the dashboard may also edit and delete |
