fix: harden gateway security guidance (#2000) (thanks @rhuanssauro)
This commit is contained in:
1 parent
42fe1021ca
commit
09ab5a9b31
14 files changed
+67
-89
No files matched your search
@@ -392,7 +392,7 @@ Save to `~/.clawdbot/clawdbot.json` and you can DM the bot from that number.
|
||||
auth: {
|
||||
mode: "token",
|
||||
token: "gateway-token",
|
||||
allowTailscale: true
|
||||
allowTailscale: false
|
||||
},
|
||||
tailscale: { mode: "serve", resetOnExit: false },
|
||||
remote: { url: "ws://gateway.tailnet:18789", token: "remote-token" },
|
||||
|
||||
@@ -2867,22 +2867,17 @@ Notes:
|
||||
- `gateway.port` controls the single multiplexed port used for WebSocket + HTTP (control UI, hooks, A2UI).
|
||||
- OpenAI Chat Completions endpoint: **disabled by default**; enable with `gateway.http.endpoints.chatCompletions.enabled: true`.
|
||||
- Precedence: `--port` > `CLAWDBOT_GATEWAY_PORT` > `gateway.port` > default `18789`.
|
||||
- Gateway auth is required by default (token/password or Tailscale Serve identity). Non-loopback binds require a shared token/password.
|
||||
- Gateway auth is required by default (token/password). Non-loopback binds require a shared token/password.
|
||||
- The onboarding wizard generates a gateway token by default (even on loopback).
|
||||
- `gateway.remote.token` is **only** for remote CLI calls; it does not enable local gateway auth. `gateway.token` is ignored.
|
||||
|
||||
Auth and Tailscale:
|
||||
- `gateway.auth.mode` sets the handshake requirements (`token` or `password`). When unset, token auth is assumed.
|
||||
- `gateway.auth.token` stores the shared token for token auth (used by the CLI on the same machine).
|
||||
- When `gateway.auth.mode` is set, only that method is accepted (plus optional Tailscale headers).
|
||||
- When `gateway.auth.mode` is set, only that method is accepted.
|
||||
- `gateway.auth.password` can be set here, or via `CLAWDBOT_GATEWAY_PASSWORD` (recommended).
|
||||
- `gateway.auth.allowTailscale` allows Tailscale Serve identity headers
|
||||
(`tailscale-user-login`) to satisfy auth when the request arrives on loopback
|
||||
with `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host`. Clawdbot
|
||||
verifies the identity by resolving the `x-forwarded-for` address via
|
||||
`tailscale whois` before accepting it. When `true`, Serve requests do not need
|
||||
a token/password; set `false` to require explicit credentials. Defaults to
|
||||
`true` when `tailscale.mode = "serve"` and auth mode is not `password`.
|
||||
- `gateway.auth.allowTailscale` is a legacy option; do not use it for auth.
|
||||
Keep it `false` and require token/password even with Tailscale Serve.
|
||||
- `gateway.tailscale.mode: "serve"` uses Tailscale Serve (tailnet only, loopback bind).
|
||||
- `gateway.tailscale.mode: "funnel"` exposes the dashboard publicly; requires auth.
|
||||
- `gateway.tailscale.resetOnExit` resets Serve/Funnel config on shutdown.
|
||||
|
||||
@@ -115,8 +115,8 @@ Short version: **keep the Gateway loopback-only** unless you’re sure you need
|
||||
- **Non-loopback binds** (`lan`/`tailnet`/`custom`, or `auto` when loopback is unavailable) must use auth tokens/passwords.
|
||||
- `gateway.remote.token` is **only** for remote CLI calls — it does **not** enable local auth.
|
||||
- `gateway.remote.tlsFingerprint` pins the remote TLS cert when using `wss://`.
|
||||
- **Tailscale Serve** can authenticate via identity headers when `gateway.auth.allowTailscale: true`.
|
||||
Set it to `false` if you want tokens/passwords instead.
|
||||
- **Tailscale Serve** only provides HTTPS routing; it does not replace auth. Use a
|
||||
token/password and keep `gateway.auth.allowTailscale: false`.
|
||||
- Treat `browser.controlUrl` like an admin API: tailnet-only + token auth.
|
||||
|
||||
Deep dive: [Security](/gateway/security).
|
||||
@@ -327,19 +327,13 @@ Rotation checklist (token/password):
|
||||
3. Update any remote clients (`gateway.remote.token` / `.password` on machines that call into the Gateway).
|
||||
4. Verify you can no longer connect with the old credentials.
|
||||
|
||||
### 0.6) Tailscale Serve identity headers
|
||||
### 0.6) Tailscale Serve auth
|
||||
|
||||
When `gateway.auth.allowTailscale` is `true` (default for Serve), Clawdbot
|
||||
accepts Tailscale Serve identity headers (`tailscale-user-login`) as
|
||||
authentication. Clawdbot verifies the identity by resolving the
|
||||
`x-forwarded-for` address through the local Tailscale daemon (`tailscale whois`)
|
||||
and matching it to the header. This only triggers for requests that hit loopback
|
||||
and include `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host` as
|
||||
injected by Tailscale.
|
||||
|
||||
**Security rule:** do not forward these headers from your own reverse proxy. If
|
||||
you terminate TLS or proxy in front of the gateway, disable
|
||||
`gateway.auth.allowTailscale` and use token/password auth instead.
|
||||
Tailscale Serve/Funnel provide HTTPS and routing; they do **not** replace Gateway
|
||||
auth. Always require a token/password and keep `gateway.auth.allowTailscale: false`
|
||||
(legacy option). If you terminate TLS or proxy in front of the gateway,
|
||||
ensure your proxy overwrites `x-forwarded-*` and is listed in
|
||||
`gateway.trustedProxies`.
|
||||
|
||||
Trusted proxies:
|
||||
- If you terminate TLS in front of the Gateway, set `gateway.trustedProxies` to your proxy IPs.
|
||||
|
||||
@@ -8,7 +8,7 @@ read_when:
|
||||
|
||||
Clawdbot can auto-configure Tailscale **Serve** (tailnet) or **Funnel** (public) for the
|
||||
Gateway dashboard and WebSocket port. This keeps the Gateway bound to loopback while
|
||||
Tailscale provides HTTPS, routing, and (for Serve) identity headers.
|
||||
Tailscale provides HTTPS and routing.
|
||||
|
||||
## Modes
|
||||
|
||||
@@ -23,16 +23,8 @@ Set `gateway.auth.mode` to control the handshake:
|
||||
- `token` (default when `CLAWDBOT_GATEWAY_TOKEN` is set)
|
||||
- `password` (shared secret via `CLAWDBOT_GATEWAY_PASSWORD` or config)
|
||||
|
||||
When `tailscale.mode = "serve"` and `gateway.auth.allowTailscale` is `true`,
|
||||
valid Serve proxy requests can authenticate via Tailscale identity headers
|
||||
(`tailscale-user-login`) without supplying a token/password. Clawdbot verifies
|
||||
the identity by resolving the `x-forwarded-for` address via the local Tailscale
|
||||
daemon (`tailscale whois`) and matching it to the header before accepting it.
|
||||
Clawdbot only treats a request as Serve when it arrives from loopback with
|
||||
Tailscale’s `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-host`
|
||||
headers.
|
||||
To require explicit credentials, set `gateway.auth.allowTailscale: false` or
|
||||
force `gateway.auth.mode: "password"`.
|
||||
Tailscale Serve/Funnel do **not** replace Gateway auth. Always require a token
|
||||
or password, and keep `gateway.auth.allowTailscale: false` (legacy option).
|
||||
|
||||
## Config examples
|
||||
|
||||
@@ -42,7 +34,8 @@ force `gateway.auth.mode: "password"`.
|
||||
{
|
||||
gateway: {
|
||||
bind: "loopback",
|
||||
tailscale: { mode: "serve" }
|
||||
tailscale: { mode: "serve" },
|
||||
auth: { mode: "token", token: "your-token", allowTailscale: false }
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -133,7 +126,7 @@ Avoid Funnel for browser control endpoints unless you explicitly want public exp
|
||||
## Tailscale prerequisites + limits
|
||||
|
||||
- Serve requires HTTPS enabled for your tailnet; the CLI prompts if it is missing.
|
||||
- Serve injects Tailscale identity headers; Funnel does not.
|
||||
- Serve injects Tailscale identity headers; Clawdbot does not use them for auth.
|
||||
- Funnel requires Tailscale v1.38.3+, MagicDNS, HTTPS enabled, and a funnel node attribute.
|
||||
- Funnel only supports ports `443`, `8443`, and `10000` over TLS.
|
||||
- Funnel on macOS requires the open-source Tailscale app variant.
|
||||
|
||||
+2
-2
@@ -327,7 +327,7 @@ The wizard now opens your browser with a tokenized dashboard URL right after onb
|
||||
- The token is the same value as `gateway.auth.token` (or `CLAWDBOT_GATEWAY_TOKEN`) and is stored by the UI after first load.
|
||||
|
||||
**Not on localhost:**
|
||||
- **Tailscale Serve** (recommended): keep bind loopback, run `clawdbot gateway --tailscale serve`, open `https://<magicdns>/`. If `gateway.auth.allowTailscale` is `true`, identity headers satisfy auth (no token).
|
||||
- **Tailscale Serve** (recommended): keep bind loopback, run `clawdbot gateway --tailscale serve`, open `https://<magicdns>/`, and authenticate with your token/password (keep `gateway.auth.allowTailscale: false`).
|
||||
- **Tailnet bind**: run `clawdbot gateway --bind tailnet --token "<token>"`, open `http://<tailscale-ip>:18789/`, paste token in dashboard settings.
|
||||
- **SSH tunnel**: `ssh -N -L 18789:127.0.0.1:18789 user@host` then open `http://127.0.0.1:18789/?token=...` from `clawdbot dashboard`.
|
||||
|
||||
@@ -1434,7 +1434,7 @@ Check the basics:
|
||||
- Channel health: `clawdbot channels status`
|
||||
|
||||
Then verify auth and routing:
|
||||
- If you use Tailscale Serve, make sure `gateway.auth.allowTailscale` is set correctly.
|
||||
- If you use Tailscale Serve, confirm you’re using token/password auth and `gateway.auth.allowTailscale: false`.
|
||||
- If you connect via SSH tunnel, confirm the local tunnel is up and points at the right port.
|
||||
- Confirm your allowlists (DM or group) include your account.
|
||||
|
||||
|
||||
@@ -122,8 +122,8 @@ clawdbot gateway restart
|
||||
Open: `https://<magicdns>/`
|
||||
|
||||
Notes:
|
||||
- Serve keeps the Gateway loopback-only and authenticates via Tailscale identity headers.
|
||||
- To require token/password instead, set `gateway.auth.allowTailscale: false` or use `gateway.auth.mode: "password"`.
|
||||
- Serve keeps the Gateway loopback-only and provides HTTPS routing.
|
||||
- You still need gateway auth (token/password). Keep `gateway.auth.allowTailscale: false` and set `gateway.auth.mode` to `token` or `password`.
|
||||
|
||||
**Option C: Tailnet bind (no Serve)**
|
||||
```bash
|
||||
|
||||
@@ -68,13 +68,9 @@ clawdbot gateway --tailscale serve
|
||||
Open:
|
||||
- `https://<magicdns>/` (or your configured `gateway.controlUi.basePath`)
|
||||
|
||||
By default, Serve requests can authenticate via Tailscale identity headers
|
||||
(`tailscale-user-login`) when `gateway.auth.allowTailscale` is `true`. Clawdbot
|
||||
verifies the identity by resolving the `x-forwarded-for` address with
|
||||
`tailscale whois` and matching it to the header, and only accepts these when the
|
||||
request hits loopback with Tailscale’s `x-forwarded-*` headers. Set
|
||||
`gateway.auth.allowTailscale: false` (or force `gateway.auth.mode: "password"`)
|
||||
if you want to require a token/password even for Serve traffic.
|
||||
Serve does **not** replace Gateway auth. Set a token/password (prefer
|
||||
`CLAWDBOT_GATEWAY_TOKEN` or `CLAWDBOT_GATEWAY_PASSWORD`) and connect with it.
|
||||
Keep `gateway.auth.allowTailscale: false` (legacy option).
|
||||
|
||||
### Bind to tailnet + token
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Authentication is enforced at the WebSocket handshake via `connect.params.auth`
|
||||
|
||||
- **Localhost**: open `http://127.0.0.1:18789/`. If you see “unauthorized,” run `clawdbot dashboard` and use the tokenized link (`?token=...`).
|
||||
- **Token source**: `gateway.auth.token` (or `CLAWDBOT_GATEWAY_TOKEN`); the UI stores it after first load.
|
||||
- **Not localhost**: use Tailscale Serve (tokenless if `gateway.auth.allowTailscale: true`), tailnet bind with a token, or an SSH tunnel. See [Web surfaces](/web).
|
||||
- **Not localhost**: use Tailscale Serve with a token/password, tailnet bind with a token, or an SSH tunnel. See [Web surfaces](/web).
|
||||
|
||||
## If you see “unauthorized” / 1008
|
||||
|
||||
|
||||
+4
-5
@@ -91,14 +91,13 @@ Open:
|
||||
|
||||
## Security notes
|
||||
|
||||
- Gateway auth is required by default (token/password or Tailscale identity headers).
|
||||
- Gateway auth is required by default (token/password).
|
||||
- Non-loopback binds still **require** a shared token/password (`gateway.auth` or env).
|
||||
- The wizard generates a gateway token by default (even on loopback).
|
||||
- The UI sends `connect.params.auth.token` or `connect.params.auth.password`.
|
||||
- With Serve, Tailscale identity headers can satisfy auth when
|
||||
`gateway.auth.allowTailscale` is `true` (no token/password required). Set
|
||||
`gateway.auth.allowTailscale: false` to require explicit credentials. See
|
||||
[Tailscale](/gateway/tailscale) and [Security](/gateway/security).
|
||||
- Tailscale Serve does not bypass auth; use token/password and keep
|
||||
`gateway.auth.allowTailscale: false`. See [Tailscale](/gateway/tailscale) and
|
||||
[Security](/gateway/security).
|
||||
- `gateway.tailscale.mode: "funnel"` requires `gateway.auth.mode: "password"` (shared password).
|
||||
|
||||
## Building the UI
|
||||
|
||||
Reference in new issue
Block a user