Skip to content

refactor(robonix-api): make MCP DNS-rebinding opt-out explicit (follow-up to #86)#87

Closed
enkerewpo wants to merge 1 commit into
devfrom
fix/cross-host-mcp-421-and-atlas-env
Closed

refactor(robonix-api): make MCP DNS-rebinding opt-out explicit (follow-up to #86)#87
enkerewpo wants to merge 1 commit into
devfrom
fix/cross-host-mcp-421-and-atlas-env

Conversation

@enkerewpo

@enkerewpo enkerewpo commented Jun 17, 2026

Copy link
Copy Markdown
Member

Optional follow-up to the fix merged in #86, which used FastMCP(self.id, host="0.0.0.0"). No behavior change — this only makes the intent explicit and responds to the automated security review on the commit.

Background

  • Passing host="0.0.0.0" to FastMCP does not change the listen socket. The socket is bound by _start_mcp_server's uvicorn host="0.0.0.0" (capability.py:750), and the lifecycle gRPC has always bound 0.0.0.0 (:688). The host arg only matters when transport_security is unset and the host is loopback, where the SDK adds a localhost-only allowed_hosts (mcp/server/fastmcp/server.py:178). So "0.0.0.0" was just a side-effect way of skipping that auto-allowlist, and reads misleadingly as if it changed the bind address.
  • The 421 is therefore also unrelated to the /mcp/ trailing slash: the 307 redirect is incidental, and a direct POST /mcp 421s under the old code too. The cause is the default host=127.0.0.1 triggering the localhost-only Host-header allowlist.

Change

host="0.0.0.0" → explicit transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False), with a clarifying comment.

Security note (responding to the automated review)

  • The 0.0.0.0 listen socket pre-dates this change (both uvicorn and the Driver gRPC). This change only relaxes the application-layer Host-header allowlist.
  • DNS-rebinding protection defends a browser attack surface (a malicious page driving a victim browser at a loopback service). Robonix's executor→provider call is server-to-server and does not go through a browser, so the control does not apply to this threat model — leaving it on only blocks legitimate cross-host calls.
  • The genuine residual risk is unauthenticated access to hardware-control endpoints on a routable network, which is a property of the entire v0.1 wire (atlas / Driver gRPC / MCP are all unauthenticated) and the trusted-LAN/VPN deployment posture — not something this single MCP change should carry. End-to-end authentication (token / mTLS) is a separate, wire-wide hardening item worth tracking in its own issue.

Does not touch the v0.1 frozen surface; purely an internal robonix-api expression change.

Replace FastMCP(self.id, host="0.0.0.0") with an explicit
TransportSecuritySettings(enable_dns_rebinding_protection=False). Same
runtime behavior — cross-host Host headers are accepted and the listen
socket is bound 0.0.0.0 by uvicorn (_start_mcp_server) regardless — but the
intent is now explicit rather than a side-effect of the host arg dodging
FastMCP's host-in-loopback auto-allowlist branch (server.py:178). The host
arg never affected the bind address, so "0.0.0.0" there was misleading.

Clarifies the security-review note: the 0.0.0.0 listen socket pre-dates this
change; only the app-layer Host-header allowlist is relaxed. DNS-rebinding
protection defends a browser attack vector, not the server-to-server
executor->provider call. Endpoint auth remains a separate wire-wide item.
@enkerewpo enkerewpo closed this Jun 17, 2026
@enkerewpo enkerewpo deleted the fix/cross-host-mcp-421-and-atlas-env branch June 17, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant