Skip to content

Commit 2633b14

Browse files
feat(security): support operator-managed network proxy routing (#70044)
* feat: support operator-managed proxy routing * docs: add network proxy changelog entry * fix(proxy): restrict gateway bypass to loopback IPs * fix(cli): harden container proxy URL checks * docs(proxy): clarify gateway bypass scope * docs: remove proxy changelog entry * fix(proxy): clear startup CI guard failures * fix(proxy): harden gateway proxy policy parsing * fix(proxy): honor update shorthand proxy policy * fix(cli): redact proxy URL suffixes * test(proxy): keep gateway help off proxy startup * fix(proxy): keep overlapping lifecycle active * docs: add proxy changelog entry --------- Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
1 parent 025081d commit 2633b14

36 files changed

Lines changed: 2737 additions & 96 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
77
### Changes
88

99
- Gateway/chat: accept non-image attachments through `chat.send` by staging them as agent-readable media paths, while keeping unsupported RPC attachment paths explicit instead of silently dropping files. Fixes #48123. (#67572) Thanks @samzong.
10+
- Security/networking: add opt-in operator-managed outbound proxy routing (proxy.enabled + proxy.proxyUrl/OPENCLAW_PROXY_URL) with strict http:// forward-proxy validation, loopback-only Gateway bypass, and cleanup of proxy env/dispatcher state on exit. (#70044) Thanks @jesse-merhi and @joshavant.
1011

1112
### Fixes
1213

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
eaa444149224b6cd634d034003c07f3c430d4e680e58aaa158e0038c1b03398e config-baseline.json
2-
6aa2d317b20d73fba7b5f0d36dffc3d0c33796147b544434654d9fe4c1885c5f config-baseline.core.json
1+
b80df5537b3569826a23b8176910476493ae569b65f9b4c2fa9e0ad415eb4a2b config-baseline.json
2+
8530c8fd54e04a2ab7f6704195f9959311e289ae122ebd8e27af236de435fef9 config-baseline.core.json
33
c4f07c228d4f07e7afafa5b600b4a80f5b26aaed7267c7287a64d04a527be8e8 config-baseline.channel.json
44
1f5592bfd141ba1e982ce31763a253c10afb080ab4ea2b6538299b114e29cee1 config-baseline.plugin.json

docs/docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,7 @@
15211521
{
15221522
"group": "Security",
15231523
"pages": [
1524+
"security/network-proxy",
15241525
"security/formal-verification",
15251526
"security/THREAT-MODEL-ATLAS",
15261527
"security/CONTRIBUTING-THREAT-MODEL"

docs/security/network-proxy.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
summary: "How to route OpenClaw runtime HTTP and WebSocket traffic through an operator-managed filtering proxy"
3+
title: "Network proxy"
4+
read_when:
5+
- You want defense-in-depth against SSRF and DNS rebinding attacks
6+
- Configuring an external forward proxy for OpenClaw runtime traffic
7+
---
8+
9+
# Network Proxy
10+
11+
OpenClaw can route runtime HTTP and WebSocket traffic through an operator-managed forward proxy. This is optional defense in depth for deployments that want central egress control, stronger SSRF protection, and better network auditability.
12+
13+
OpenClaw does not ship, download, start, configure, or certify a proxy. You run the proxy technology that fits your environment, and OpenClaw routes normal process-local HTTP and WebSocket clients through it.
14+
15+
## Why Use a Proxy?
16+
17+
A proxy gives operators one network control point for outbound HTTP and WebSocket traffic. That can be useful even outside SSRF hardening:
18+
19+
- Central policy: maintain one egress policy instead of relying on every application HTTP call site to get network rules right.
20+
- Connect-time checks: evaluate the destination after DNS resolution and immediately before the proxy opens the upstream connection.
21+
- DNS rebinding defense: reduce the gap between an application-level DNS check and the actual outbound connection.
22+
- Broader JavaScript coverage: route ordinary `fetch`, `node:http`, `node:https`, WebSocket, axios, got, node-fetch, and similar clients through the same path.
23+
- Auditability: log allowed and denied destinations at the egress boundary.
24+
- Operational control: enforce destination rules, network segmentation, rate limits, or outbound allowlists without rebuilding OpenClaw.
25+
26+
OpenClaw still keeps application-level SSRF guards such as `fetchWithSsrFGuard`. Proxy routing is an additional process-level guardrail for normal HTTP and WebSocket egress, not a replacement for guarded fetches or an OS-level network sandbox.
27+
28+
## How OpenClaw Routes Traffic
29+
30+
When `proxy.enabled=true` and a proxy URL is configured, protected runtime processes such as `openclaw gateway run`, `openclaw node run`, and `openclaw agent --local` route normal HTTP and WebSocket egress through the configured proxy:
31+
32+
```text
33+
OpenClaw process
34+
fetch -> operator-managed filtering proxy -> public internet
35+
node:http and https -> operator-managed filtering proxy -> public internet
36+
WebSocket clients -> operator-managed filtering proxy -> public internet
37+
```
38+
39+
The public contract is the routing behavior, not the internal Node hooks used to implement it. OpenClaw Gateway control-plane WebSocket clients use a narrow direct path for local loopback Gateway RPC traffic when the Gateway URL uses a literal loopback IP such as `127.0.0.1` or `[::1]`. That control-plane path must be able to reach loopback Gateways even when the operator proxy blocks loopback destinations. Normal runtime HTTP and WebSocket requests still use the configured proxy.
40+
41+
The proxy URL itself must use `http://`. HTTPS destinations are still supported through the proxy with HTTP `CONNECT`; this only means OpenClaw expects a plain HTTP forward-proxy listener such as `http://127.0.0.1:3128`.
42+
43+
While the proxy is active, OpenClaw clears `no_proxy`, `NO_PROXY`, and `GLOBAL_AGENT_NO_PROXY`. Those bypass lists are destination-based, so leaving `localhost` or `127.0.0.1` there would let high-risk SSRF targets skip the filtering proxy.
44+
45+
On shutdown, OpenClaw restores the previous proxy environment and resets cached process routing state.
46+
47+
## Configuration
48+
49+
```yaml
50+
proxy:
51+
enabled: true
52+
proxyUrl: http://127.0.0.1:3128
53+
```
54+
55+
You can also provide the URL through the environment, while keeping `proxy.enabled=true` in config:
56+
57+
```bash
58+
OPENCLAW_PROXY_URL=http://127.0.0.1:3128 openclaw gateway run
59+
```
60+
61+
`proxy.proxyUrl` takes precedence over `OPENCLAW_PROXY_URL`.
62+
63+
If `enabled=true` but no valid proxy URL is configured, protected commands fail startup instead of falling back to direct network access.
64+
65+
For managed gateway services started with `openclaw gateway start`, prefer storing the URL in config:
66+
67+
```bash
68+
openclaw config set proxy.enabled true
69+
openclaw config set proxy.proxyUrl http://127.0.0.1:3128
70+
openclaw gateway install --force
71+
openclaw gateway start
72+
```
73+
74+
The environment fallback is best for foreground runs. If you use it with an installed service, put `OPENCLAW_PROXY_URL` in the service durable environment, such as `$OPENCLAW_STATE_DIR/.env` or `~/.openclaw/.env`, then reinstall the service so launchd, systemd, or Scheduled Tasks starts the gateway with that value.
75+
76+
For `openclaw --container ...` commands, OpenClaw forwards `OPENCLAW_PROXY_URL` into the container-targeted child CLI when it is set. The URL must be reachable from inside the container; `127.0.0.1` refers to the container itself, not the host. OpenClaw rejects loopback proxy URLs for container-targeted commands unless you explicitly override that safety check.
77+
78+
## Proxy Requirements
79+
80+
The proxy policy is the security boundary. OpenClaw cannot verify that the proxy blocks the right targets.
81+
82+
Configure the proxy to:
83+
84+
- Bind only to loopback or a private trusted interface.
85+
- Restrict access so only the OpenClaw process, host, container, or service account can use it.
86+
- Resolve destinations itself and block destination IPs after DNS resolution.
87+
- Apply policy at connect time for both plain HTTP requests and HTTPS `CONNECT` tunnels.
88+
- Reject destination-based bypasses for loopback, private, link-local, metadata, multicast, reserved, or documentation ranges.
89+
- Avoid hostname allowlists unless you fully trust the DNS resolution path.
90+
- Log destination, decision, status, and reason without logging request bodies, authorization headers, cookies, or other secrets.
91+
- Keep proxy policy under version control and review changes like security-sensitive configuration.
92+
93+
## Recommended Blocked Destinations
94+
95+
Use this denylist as the starting point for any forward proxy, firewall, or egress policy.
96+
97+
OpenClaw application-level classifier logic lives in `src/infra/net/ssrf.ts` and `src/shared/net/ip.ts`. The relevant parity hooks are `BLOCKED_HOSTNAMES`, `BLOCKED_IPV4_SPECIAL_USE_RANGES`, `BLOCKED_IPV6_SPECIAL_USE_RANGES`, `RFC2544_BENCHMARK_PREFIX`, and the embedded IPv4 sentinel handling for NAT64, 6to4, Teredo, ISATAP, and IPv4-mapped forms. Those files are useful references when maintaining an external proxy policy, but OpenClaw does not automatically export or enforce those rules in your proxy.
98+
99+
| Range or host | Why to block |
100+
| ------------------------------------------------------------------------------------ | ---------------------------------------------------- |
101+
| `127.0.0.0/8`, `localhost`, `localhost.localdomain` | IPv4 loopback |
102+
| `::1/128` | IPv6 loopback |
103+
| `0.0.0.0/8`, `::/128` | Unspecified and this-network addresses |
104+
| `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` | RFC1918 private networks |
105+
| `169.254.0.0/16`, `fe80::/10` | Link-local addresses and common cloud metadata paths |
106+
| `169.254.169.254`, `metadata.google.internal` | Cloud metadata services |
107+
| `100.64.0.0/10` | Carrier-grade NAT shared address space |
108+
| `198.18.0.0/15`, `2001:2::/48` | Benchmarking ranges |
109+
| `192.0.0.0/24`, `192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`, `2001:db8::/32` | Special-use and documentation ranges |
110+
| `224.0.0.0/4`, `ff00::/8` | Multicast |
111+
| `240.0.0.0/4` | Reserved IPv4 |
112+
| `fc00::/7`, `fec0::/10` | IPv6 local/private ranges |
113+
| `100::/64`, `2001:20::/28` | IPv6 discard and ORCHIDv2 ranges |
114+
| `64:ff9b::/96`, `64:ff9b:1::/48` | NAT64 prefixes with embedded IPv4 |
115+
| `2002::/16`, `2001::/32` | 6to4 and Teredo with embedded IPv4 |
116+
| `::/96`, `::ffff:0:0/96` | IPv4-compatible and IPv4-mapped IPv6 |
117+
118+
If your cloud provider or network platform documents additional metadata hosts or reserved ranges, add those too.
119+
120+
## Validation
121+
122+
Validate the proxy from the same host, container, or service account that runs OpenClaw:
123+
124+
```bash
125+
curl -x http://127.0.0.1:3128 https://example.com/
126+
curl -x http://127.0.0.1:3128 http://127.0.0.1/
127+
curl -x http://127.0.0.1:3128 http://169.254.169.254/
128+
```
129+
130+
The public request should succeed. The loopback and metadata requests should fail at the proxy.
131+
132+
Then enable OpenClaw proxy routing:
133+
134+
```bash
135+
openclaw config set proxy.enabled true
136+
openclaw config set proxy.proxyUrl http://127.0.0.1:3128
137+
openclaw gateway run
138+
```
139+
140+
or set:
141+
142+
```yaml
143+
proxy:
144+
enabled: true
145+
proxyUrl: http://127.0.0.1:3128
146+
```
147+
148+
## Limits
149+
150+
- The proxy improves coverage for process-local JavaScript HTTP and WebSocket clients, but it does not replace application-level `fetchWithSsrFGuard`.
151+
- Raw `net`, `tls`, and `http2` sockets, native addons, and child processes may bypass Node-level proxy routing unless they inherit and respect proxy environment variables.
152+
- User local WebUIs and local model servers should be allowlisted in the operator proxy policy when needed; OpenClaw does not expose a general local-network bypass for them.
153+
- Gateway control-plane proxy bypass is intentionally limited to literal loopback IP URLs. Use `ws://127.0.0.1:18789` or `ws://[::1]:18789` for local direct Gateway control-plane connections; `localhost` hostnames route like ordinary hostname-based traffic.
154+
- OpenClaw does not inspect, test, or certify your proxy policy.
155+
- Treat proxy policy changes as security-sensitive operational changes.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,7 @@
16011601
"croner": "^10.0.1",
16021602
"dotenv": "^17.4.2",
16031603
"file-type": "22.0.1",
1604+
"global-agent": "^4.1.3",
16041605
"https-proxy-agent": "^9.0.0",
16051606
"ipaddr.js": "^2.3.0",
16061607
"jiti": "^2.6.1",

pnpm-lock.yaml

Lines changed: 84 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)