From a73924f77e7e4352311ce5092d1b2338e224816c Mon Sep 17 00:00:00 2001 From: chengyongru <2755839590@qq.com> Date: Sun, 7 Jun 2026 21:29:38 +0800 Subject: [PATCH] docs: document MCP SSRF allowlist behavior Maintainer edit: explain that HTTP/SSE MCP now uses the shared SSRF guard before connecting and before following redirects, so local or private HTTP MCP endpoints require an explicit tools.ssrfWhitelist entry. --- .agent/security.md | 4 +++- docs/configuration.md | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.agent/security.md b/.agent/security.md index cdbc79b50..8dfc4abe7 100644 --- a/.agent/security.md +++ b/.agent/security.md @@ -12,10 +12,12 @@ Shell execution (`ExecTool`, `agent/tools/shell.py`) also respects `restrict_to_ ## SSRF Protection -All outbound HTTP requests from agent tools must pass through `validate_url_target` (`security/network.py`). By default it blocks RFC1918 private addresses, link-local ranges, and cloud metadata endpoints (including `169.254.169.254`). +All outbound HTTP requests from agent tools must pass through `validate_url_target` (`security/network.py`). By default it blocks loopback, RFC1918 private addresses, CGNAT ranges, link-local ranges, and cloud metadata endpoints (including `169.254.169.254`). The only escape hatch is `configure_ssrf_whitelist(cidrs)`, which reads from `config.tools.ssrf_whitelist` at load time. +HTTP/SSE MCP transports are part of this boundary: validate configured MCP URLs before probing or constructing clients, and validate each outgoing HTTP request before redirects are followed. Local/private HTTP MCP endpoints are allowed only through the explicit SSRF whitelist. Stdio MCP servers are not part of the HTTP SSRF path. + **Rule**: Do not add direct `httpx.get` / `requests.get` calls in tools. Route through the existing web fetch utilities or replicate the `validate_url_target` check. ## Shell Sandbox diff --git a/docs/configuration.md b/docs/configuration.md index fa6c02e1f..3a583a1a1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1184,7 +1184,7 @@ If you want to disable them, which removes both `web_search` and `web_fetch` fro } ``` -If you need to allow trusted private ranges such as Tailscale / CGNAT addresses, you can explicitly exempt them from SSRF blocking with `tools.ssrfWhitelist`: +nanobot uses a shared SSRF guard for built-in web fetches and HTTP/SSE MCP connections. By default it blocks loopback, RFC1918/private ranges, CGNAT/Tailscale ranges, link-local addresses, and cloud metadata endpoints. If you need to allow trusted private ranges, explicitly exempt them from SSRF blocking with `tools.ssrfWhitelist`: ```json { @@ -1194,6 +1194,8 @@ If you need to allow trusted private ranges such as Tailscale / CGNAT addresses, } ``` +Keep whitelist entries as narrow as possible, such as a single host CIDR (`192.168.1.50/32`). The whitelist is global for the shared SSRF guard; it is not limited to one tool or one MCP server. + > [!TIP] > Use `proxy` in `tools.web` to route all web requests (search + fetch) through a proxy: > ```json @@ -1423,6 +1425,9 @@ Two transport modes are supported: | **Stdio** | `command` + `args` | Local process via `npx` / `uvx` | | **HTTP** | `url` + `headers` (optional) | Remote endpoint (`https://mcp.example.com/sse`) | +> [!IMPORTANT] +> HTTP/SSE MCP URLs are validated before probing or connecting, and every outgoing MCP HTTP request is validated again before redirects are followed. `localhost`, `127.0.0.1`, RFC1918/private IPs, CGNAT/Tailscale ranges, link-local addresses, and cloud metadata endpoints are blocked by default. This can break previously working local or private HTTP MCP configs until the endpoint is explicitly allowed with `tools.ssrfWhitelist`, preferably with a single-host CIDR such as `127.0.0.1/32`, `::1/128`, or `192.168.1.50/32`. Stdio MCP servers are not affected. + Use `toolTimeout` to override the default 30s per-call timeout for slow servers: ```json @@ -1479,6 +1484,7 @@ For API keys, tokens, and other secrets, see [Environment Variables for Secrets] | `tools.exec.enable` | `true` | When `false`, the shell `exec` tool is not registered at all. Use this to completely disable shell command execution. | | `tools.exec.timeout` | `60` | Default hard timeout in seconds for shell commands. Config values may exceed the per-call tool cap; set `0` to disable the hard timeout for trusted long-running commands. | | `tools.exec.pathAppend` | `""` | Extra directories to append to `PATH` when running shell commands (e.g. `/usr/sbin` for `ufw`). | +| `tools.ssrfWhitelist` | `[]` | CIDR ranges exempted from the shared SSRF guard used by web fetches and HTTP/SSE MCP connections. Prefer exact host CIDRs such as `192.168.1.50/32`; broad ranges increase SSRF exposure. | | `channels.*.allowFrom` | omitted | Access control per channel. Omit to use pairing-only mode; set `["*"]` to allow everyone; or list specific user IDs. See [Pairing](#pairing) for details. | **Docker security**: The official Docker image runs as a non-root user (`nanobot`, UID 1000) with bubblewrap pre-installed. When using `docker-compose.yml`, the container drops all Linux capabilities except `SYS_ADMIN` (required for bwrap's namespace isolation).