Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ A running MCPProxy core watches `mcp_config.json` and hot-reloads external edits

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `enable_socket` | boolean | `true` | Enable Unix socket (macOS/Linux) or named pipe (Windows) for secure local IPC between tray and core |
| `enable_socket` | boolean | `true` | Enable the Unix socket (macOS/Linux) or named pipe (Windows) the tray uses to talk to the core. **Connections over it are trusted without the API key**, and that includes every stdio server the proxy itself spawns under `none` or `sandbox` isolation: such a server can call the admin API and mint agent tokens with no credential. The tray is the only socket client, so **set `false` on headless installs, and whenever a stdio server you do not fully trust runs outside `docker` isolation**. `mcpproxy doctor` warns about the combination. See [What the sandbox does not hide](features/sandbox-isolation.md#what-the-sandbox-does-not-hide). |
| `tray_endpoint` | string | `""` | Override socket/pipe path (advanced, usually not needed) |

### Search & Tool Limits
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ MCPProxy uses a JSON configuration file located at `~/.mcpproxy/mcp_config.json`
| `trusted_proxies` | string[] | `[]` (trust nobody) | CIDRs or IP addresses whose `X-Forwarded-For`, `X-Real-IP`, `X-Forwarded-Proto` and `X-Forwarded-Host` headers are believed. Headers from any other peer are ignored and the direct `RemoteAddr` is used. Env `MCPPROXY_TRUSTED_PROXIES` (comma list). Live (hot-reload, no restart). Validation: `trusted_proxies[N] "value" is not a valid CIDR or IP address` — refused identically at boot, `PATCH /api/v1/config` and `/config/apply`. See [Reverse Proxy Deployment](/operations/reverse-proxy#trusted_proxies-forwarded-headers) |
| `require_mcp_auth` | boolean | `false` | Require an API key on the `/mcp` endpoint (off by default for client compatibility). Enable when exposing MCPProxy beyond localhost. **Server edition:** forced to `true` whenever `server_edition.enabled` is `true` — an explicit `false` is not an error, but boot logs `require_mcp_auth: false is overridden to true because server_edition.enabled is true` and `mcpproxy doctor` reports the same finding |
| `mcp_reject_query_apikey` | boolean | `false` | Refuse a `?apikey=` query parameter on every MCP endpoint (`/mcp*` and the legacy `/v1/tool_code` aliases) with a 401, forcing clients onto `Authorization: Bearer <token>` or `X-API-Key` so tokens stop appearing in access logs, proxy logs and browser history (a URL already sent is still logged in transit; the option makes the client fail until it is migrated). MCP endpoints only: the REST API, the Web UI and `/events` keep accepting the query form. Clients connected by the Connect wizard with `?apikey=` in the URL (Codex, the generic fallback) must be switched to a header carrier first. Live, no restart |
| `enable_socket` | boolean | `true` | Enable Unix socket/named pipe for local communication |
| `enable_socket` | boolean | `true` | Enable the Unix socket/named pipe the tray uses. Socket connections are trusted **without the API key**, including stdio servers spawned under `none` or `sandbox` isolation, which can then mint agent tokens. Set `false` on headless installs and whenever untrusted stdio servers run outside `docker` isolation. |
| `forward_client_headers` | boolean | `true` | Master switch for [client header forwarding](/configuration/upstream-servers#client-header-forwarding). Nothing is forwarded until a server lists names in `forward_headers`. Env `MCPPROXY_FORWARD_CLIENT_HEADERS=false` disables it for the process (never persisted). |

### `audit_log` (edition-neutral JSONL audit record)
Expand Down
2 changes: 1 addition & 1 deletion docs/docker-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ Your options on such a host:
- **No uid/gid drop.** Dropping to an unprivileged uid/gid requires `CAP_SETUID`/`CAP_SETGID` (i.e. running as root). When mcpproxy runs unprivileged, the uid/gid drop is **best-effort and typically a no-op** — the sandboxed process keeps the launching user's identity. Landlock (filesystem) and `setrlimit` (resource caps) still apply. Docker mode does drop to a container user. This is an honest trade-off, not a bug.
- **Linux-only.** Landlock is a Linux 5.13+ feature. On older kernels the launcher degrades best-effort (fewer access-right bits enforced on ABI 1). On macOS/Windows `sandbox` is a documented **no-op** and behaves like `none`.
- **Filesystem + resources only.** Landlock confines the filesystem write-allowlist; it does not provide network namespacing. Pair with care for network-sensitive servers, or use `docker` mode with `network_mode: none`.
- **`none` and `sandbox` do not hide the data directory or the Unix socket.** A stdio server runs as the proxy's user, so it can read `mcp_config.json` (holds `api_key`) and `config.db` (holds the upstream OAuth tokens), and while `enable_socket` is on it can connect to `<data-dir>/mcpproxy.sock` without a key — a sandboxed child minted an agent token over it ([#1704](https://github.com/smart-mcp-proxy/mcpproxy-go/issues/1704)). Landlock does block `/proc/<pid>/environ`, so the key is safe in the environment but not on disk or over the socket. Set `enable_socket: false` for headless installs and use `docker` isolation for servers you do not trust; `mcpproxy doctor` warns when an enabled stdio server resolves to `none` or `sandbox` with the socket on. Verification table: [What the sandbox does not hide](features/sandbox-isolation.md#what-the-sandbox-does-not-hide).
- **`none` and `sandbox` do not hide the data directory or the Unix socket.** A stdio server runs as the proxy's user, so it can read `mcp_config.json` (holds `api_key`) and `config.db` (holds the upstream OAuth tokens), and while `enable_socket` is on it can connect to `<data-dir>/mcpproxy.sock` without a key — a sandboxed child minted an agent token over it ([#1704](https://github.com/smart-mcp-proxy/mcpproxy-go/issues/1704)). Landlock does block `/proc/<pid>/environ`, so the key is safe in the environment but not on disk or over the socket. **`enable_socket: false` is the setting that matters** when a stdio server you do not fully trust runs outside `docker` isolation (headless installs lose nothing: the tray is the only socket client); use `docker` isolation for such servers where you can; `mcpproxy doctor` warns when an enabled stdio server resolves to `none` or `sandbox` with the socket on. Verification table: [What the sandbox does not hide](features/sandbox-isolation.md#what-the-sandbox-does-not-hide).
- **Docker-based scanners do not run under `sandbox`/`none`.** They are skipped and the skip is surfaced via the informational `deep_scan` descriptor — it never downgrades the baseline verdict (Spec 077 FR-008). A native scanner runtime is a future enhancement (D3 option a).

## Platform support matrix
Expand Down
2 changes: 1 addition & 1 deletion docs/features/docker-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ See [Shutdown Behavior](/operations/shutdown-behavior) for detailed subprocess l
- **No uid/gid drop.** Dropping to an unprivileged uid/gid requires `CAP_SETUID`/`CAP_SETGID` (i.e. running as root). When mcpproxy runs unprivileged, the uid/gid drop is **best-effort and typically a no-op** — the sandboxed process keeps the launching user's identity. Landlock (filesystem) and `setrlimit` (resource caps) still apply. Docker mode does drop to a container user. This is an honest trade-off, not a bug.
- **Linux-only.** Landlock is a Linux 5.13+ feature. On older kernels the launcher degrades best-effort (fewer access-right bits enforced). On macOS/Windows `sandbox` is a documented **no-op** and behaves like `none`.
- **Filesystem + resources only.** Landlock confines the filesystem write-allowlist; it does not provide network namespacing. For network-sensitive servers, use `docker` mode with `network_mode: none`.
- **`none` and `sandbox` do not hide the data directory or the Unix socket.** A stdio server runs as the proxy's user, so it can read `mcp_config.json` (holds `api_key`) and `config.db` (holds the upstream OAuth tokens), and while `enable_socket` is on it can connect to `<data-dir>/mcpproxy.sock` without a key — a sandboxed child minted an agent token over it ([#1704](https://github.com/smart-mcp-proxy/mcpproxy-go/issues/1704)). Landlock does block `/proc/<pid>/environ`, so the key is safe in the environment but not on disk or over the socket. Set `enable_socket: false` for headless installs and use `docker` isolation for servers you do not trust; `mcpproxy doctor` warns when an enabled stdio server resolves to `none` or `sandbox` with the socket on. Verification table: [What the sandbox does not hide](/features/sandbox-isolation#what-the-sandbox-does-not-hide).
- **`none` and `sandbox` do not hide the data directory or the Unix socket.** A stdio server runs as the proxy's user, so it can read `mcp_config.json` (holds `api_key`) and `config.db` (holds the upstream OAuth tokens), and while `enable_socket` is on it can connect to `<data-dir>/mcpproxy.sock` without a key — a sandboxed child minted an agent token over it ([#1704](https://github.com/smart-mcp-proxy/mcpproxy-go/issues/1704)). Landlock does block `/proc/<pid>/environ`, so the key is safe in the environment but not on disk or over the socket. **`enable_socket: false` is the setting that matters** when a stdio server you do not fully trust runs outside `docker` isolation (headless installs lose nothing: the tray is the only socket client); use `docker` isolation for such servers where you can; `mcpproxy doctor` warns when an enabled stdio server resolves to `none` or `sandbox` with the socket on. Verification table: [What the sandbox does not hide](/features/sandbox-isolation#what-the-sandbox-does-not-hide).
- **Docker-based scanners do not run under `sandbox`/`none`.** They are skipped and the skip is surfaced via the informational `deep_scan` descriptor — it never downgrades the baseline verdict (Spec 077 FR-008). A native scanner runtime is a future enhancement.

## Platform support matrix
Expand Down
14 changes: 12 additions & 2 deletions docs/features/sandbox-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ sandboxes, Landlock does **not** require unprivileged user namespaces, so it is
not blocked by `kernel.apparmor_restrict_unprivileged_userns=1` (default on
Ubuntu 23.10+).

:::caution The sandbox confines writes, not identity
A stdio server under `sandbox` (or `none`) still runs as the proxy's user. It can
read `mcp_config.json` and `config.db`, and while `enable_socket` is on it can
connect to the admin socket **without a key and mint itself an agent token**, in
both modes. If you run stdio servers you do not fully trust outside `docker`
isolation, **`enable_socket: false` is the setting that matters**. Details and
the verified matrix: [What the sandbox does not hide](#what-the-sandbox-does-not-hide).
:::

## Enabling

Set the isolation **mode** to `sandbox`, globally or per server:
Expand Down Expand Up @@ -111,8 +120,9 @@ readable in `none` and `sandbox`.

What to do:

- **Headless installs** (no tray): set `enable_socket: false`. The tray is the
only socket client, so a headless service loses nothing.
- **Set `enable_socket: false`** unless you use the tray. This is the one
setting that closes the token-minting path in both `none` and `sandbox`; the
tray is the only socket client, so a headless service loses nothing.
- **Servers you do not trust**: use `docker` isolation
(`docker_isolation.mode: "docker"`, or per-server `isolation.mode`). The
container does not mount the data directory, so neither the files nor the
Expand Down
1 change: 1 addition & 0 deletions docs/features/security-quarantine.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ What it does not do:
- **It is not proof of safety.** Review is only as good as the reviewer, and the offline scanners can miss attacks. A clean scan means the checks found nothing in the captured definitions; it is not evidence that a server or tool is safe. Manual review, scanner-assisted review and agent-assisted inspection (`inspect_quarantined`) are inputs to your decision. None of them approves anything by itself.
- **It does not see behavior outside the definition.** The hash covers name, description, schemas and the safety-relevant annotation hints. A server whose implementation or backend changes while its definitions stay identical is not detected, and neither is a malicious tool result returned at call time.
- **Trust modes change the guarantee.** `scan` (the default since spec 119) admits a new server, a new tool and a changed tool contract automatically when the offline scan is clean, so a detector miss — including a paraphrased rug pull that scans clean — is admitted at that point; findings, uncovered tools and safer-direction hint flips are held. `manual` holds every new server and every tool contract change until it is approved: server release needs a person, while tool approval can also come from an admin-access MCP client. `auto` admits without scanning or holding at all and trusts tool changes (rug-pull risk). Server admission and tool-change review are separate controls: `"quarantined": false` on a server entry only releases the server-level quarantine, and under `manual` trust a new or changed tool on that server is still held after its baseline. Tool-change holds are removed by `trust_mode: auto`, the legacy `auto_approve_tool_changes: true` (which an explicit `trust_mode` overrides), or `quarantine_enabled: false` globally. See [Trust modes](#trust-modes-auto--scan--manual).
- **It gates definitions, not processes.** Quarantine decides which tool definitions agents may see and call. A stdio server the proxy spawns under `none` or `sandbox` isolation still runs as the proxy's user: it can read the data directory and, while `enable_socket` is on, mint agent tokens over the admin socket with no key, whatever its quarantine state. For such servers `enable_socket: false` or `docker` isolation is the control, not approval. See [What the sandbox does not hide](./sandbox-isolation.md#what-the-sandbox-does-not-hide).
- **It is not data-loss prevention.** See [Sensitive Data Detection](./sensitive-data-detection.md) and [Output Sanitisation](./output-sanitisation.md) for what is flagged and what, if anything, is blocked.

## Managing Quarantine
Expand Down
Loading