mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:20:43 +00:00
feat(gateway): auto-approve trusted CIDR node pairing (#61004) (thanks @sahilsatralkar)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
d885c14dea2c361123a97a0f6c854f6dbae8592f39daa211173ef7f1fe7d554a config-baseline.json
|
||||
c991bb527d8efffb5c9a2c5e502113260a2873923d469289c82f7029257fddaf config-baseline.core.json
|
||||
3b8ff208a31b04ea61391182444bd744357577872eac279136bbc284c3dc064a config-baseline.json
|
||||
4dfeadeb814fb205f5a17d797cbbe3c07685009821fe8dbf8771ea428ed5b4dd config-baseline.core.json
|
||||
d72032762ab46b99480b57deb81130a0ab5b1401189cfbaf4f7fef4a063a7f6c config-baseline.channel.json
|
||||
0d5ba81f0030bd39b7ae285096276cc18b150836c2252fd2217329fc6154e80e config-baseline.plugin.json
|
||||
|
||||
@@ -104,6 +104,28 @@ existing approval as-is and creates a fresh pending upgrade request. Use
|
||||
`openclaw devices list` to compare the currently approved access with the newly
|
||||
requested access before you approve.
|
||||
|
||||
### Optional trusted-CIDR node auto-approve
|
||||
|
||||
Device pairing remains manual by default. For tightly controlled node networks,
|
||||
you can opt in to first-time node auto-approval with explicit CIDRs or exact IPs:
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
pairing: {
|
||||
autoApproveCidrs: ["192.168.1.0/24"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This only applies to fresh `role: node` pairing requests with no requested
|
||||
scopes. Operator, browser, Control UI, and WebChat clients still require manual
|
||||
approval. Role, scope, metadata, and public-key changes still require manual
|
||||
approval.
|
||||
|
||||
### Node pairing state storage
|
||||
|
||||
Stored under `~/.openclaw/devices/`:
|
||||
|
||||
@@ -297,6 +297,14 @@ See [Plugins](/tools/plugin).
|
||||
trustedProxies: ["10.0.0.1"],
|
||||
// Optional. Default false.
|
||||
allowRealIpFallback: false,
|
||||
nodes: {
|
||||
pairing: {
|
||||
// Optional. Default unset/disabled.
|
||||
autoApproveCidrs: ["192.168.1.0/24", "fd00:1234:5678::/64"],
|
||||
},
|
||||
allowCommands: ["canvas.navigate"],
|
||||
denyCommands: ["system.run"],
|
||||
},
|
||||
tools: {
|
||||
// Additional /tools/invoke HTTP denies
|
||||
deny: ["browser"],
|
||||
@@ -359,6 +367,8 @@ See [Plugins](/tools/plugin).
|
||||
- If `gateway.auth.token` / `gateway.auth.password` is explicitly configured via SecretRef and unresolved, resolution fails closed (no remote fallback masking).
|
||||
- `trustedProxies`: reverse proxy IPs that terminate TLS or inject forwarded-client headers. Only list proxies you control. Loopback entries are still valid for same-host proxy/local-detection setups (for example Tailscale Serve or a local reverse proxy), but they do **not** make loopback requests eligible for `gateway.auth.mode: "trusted-proxy"`.
|
||||
- `allowRealIpFallback`: when `true`, the gateway accepts `X-Real-IP` if `X-Forwarded-For` is missing. Default `false` for fail-closed behavior.
|
||||
- `gateway.nodes.pairing.autoApproveCidrs`: optional CIDR/IP allowlist for auto-approving first-time node device pairing with no requested scopes. It is disabled when unset. This does not auto-approve operator/browser/Control UI/WebChat pairing, and it does not auto-approve role, scope, metadata, or public-key upgrades.
|
||||
- `gateway.nodes.allowCommands` / `gateway.nodes.denyCommands`: global allow/deny shaping for declared node commands after pairing and allowlist evaluation.
|
||||
- `gateway.tools.deny`: extra tool names blocked for HTTP `POST /tools/invoke` (extends default deny list).
|
||||
- `gateway.tools.allow`: remove tool names from the default HTTP deny list.
|
||||
|
||||
|
||||
@@ -115,6 +115,34 @@ The macOS app can optionally attempt a **silent approval** when:
|
||||
|
||||
If silent approval fails, it falls back to the normal “Approve/Reject” prompt.
|
||||
|
||||
## Trusted-CIDR device auto-approval
|
||||
|
||||
WS device pairing for `role: node` remains manual by default. For private
|
||||
node networks where the Gateway already trusts the network path, operators can
|
||||
opt in with explicit CIDRs or exact IPs:
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
pairing: {
|
||||
autoApproveCidrs: ["192.168.1.0/24"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Security boundary:
|
||||
|
||||
- Disabled when `gateway.nodes.pairing.autoApproveCidrs` is unset.
|
||||
- No blanket LAN or private-network auto-approve mode exists.
|
||||
- Only fresh `role: node` device pairing with no requested scopes is eligible.
|
||||
- Operator, browser, Control UI, and WebChat clients stay manual.
|
||||
- Role, scope, metadata, and public-key upgrades stay manual.
|
||||
- Same-host loopback trusted-proxy header paths are not eligible because that
|
||||
path can be spoofed by local callers.
|
||||
|
||||
## Metadata-upgrade auto-approval
|
||||
|
||||
When an already paired device reconnects with only non-sensitive metadata
|
||||
|
||||
@@ -111,6 +111,7 @@ Use this as the quick model when triaging risk:
|
||||
| `canvas.eval` / browser evaluate | Intentional operator capability when enabled | "Any JS eval primitive is automatically a vuln in this trust model" |
|
||||
| Local TUI `!` shell | Explicit operator-triggered local execution | "Local shell convenience command is remote injection" |
|
||||
| Node pairing and node commands | Operator-level remote execution on paired devices | "Remote device control should be treated as untrusted user access by default" |
|
||||
| `gateway.nodes.pairing.autoApproveCidrs` | Opt-in trusted-network node enrollment policy | "A disabled-by-default allowlist is an automatic pairing vulnerability" |
|
||||
|
||||
## Not vulnerabilities by design
|
||||
|
||||
@@ -133,6 +134,12 @@ a real boundary bypass is demonstrated:
|
||||
approval layer for `system.run`, when the real execution boundary is still
|
||||
the gateway's global node command policy plus the node's own exec
|
||||
approvals.
|
||||
- Reports that treat configured `gateway.nodes.pairing.autoApproveCidrs` as a
|
||||
vulnerability by itself. This setting is disabled by default, requires
|
||||
explicit CIDR/IP entries, only applies to first-time `role: node` pairing with
|
||||
no requested scopes, and does not auto-approve operator/browser/Control UI,
|
||||
WebChat, role upgrades, scope upgrades, metadata changes, public-key changes,
|
||||
or same-host loopback trusted-proxy header paths.
|
||||
- "Missing per-user authorization" findings that treat `sessionKey` as an
|
||||
auth token.
|
||||
|
||||
@@ -353,6 +360,12 @@ gateway:
|
||||
|
||||
When `trustedProxies` is configured, the Gateway uses `X-Forwarded-For` to determine the client IP. `X-Real-IP` is ignored by default unless `gateway.allowRealIpFallback: true` is explicitly set.
|
||||
|
||||
Trusted proxy headers do not make node device pairing automatically trusted.
|
||||
`gateway.nodes.pairing.autoApproveCidrs` is a separate, disabled-by-default
|
||||
operator policy. Even when enabled, loopback-source trusted-proxy header paths
|
||||
are excluded from node auto-approval because local callers can forge those
|
||||
headers.
|
||||
|
||||
Good reverse proxy behavior (overwrite incoming forwarding headers):
|
||||
|
||||
```nginx
|
||||
|
||||
@@ -117,6 +117,25 @@ openclaw devices reject <requestId>
|
||||
|
||||
Pairing details: [Pairing](/channels/pairing).
|
||||
|
||||
Optional: if the Android node always connects from a tightly controlled subnet,
|
||||
you can opt in to first-time node auto-approval with explicit CIDRs or exact IPs:
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
pairing: {
|
||||
autoApproveCidrs: ["192.168.1.0/24"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This is disabled by default. It applies only to fresh `role: node` pairing with
|
||||
no requested scopes. Operator/browser pairing and any role, scope, metadata, or
|
||||
public-key change still require manual approval.
|
||||
|
||||
### 5) Verify the node is connected
|
||||
|
||||
- Via nodes status:
|
||||
|
||||
@@ -44,6 +44,25 @@ If the app retries pairing with changed auth details (role/scopes/public key),
|
||||
the previous pending request is superseded and a new `requestId` is created.
|
||||
Run `openclaw devices list` again before approval.
|
||||
|
||||
Optional: if the iOS node always connects from a tightly controlled subnet, you
|
||||
can opt in to first-time node auto-approval with explicit CIDRs or exact IPs:
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
pairing: {
|
||||
autoApproveCidrs: ["192.168.1.0/24"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This is disabled by default. It applies only to fresh `role: node` pairing with
|
||||
no requested scopes. Operator/browser pairing and any role, scope, metadata, or
|
||||
public-key change still require manual approval.
|
||||
|
||||
4. Verify connection:
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user