From 0e6db9a7b6e0d7e1e11fc2aef0028a66cada559d Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Fri, 5 Jun 2026 23:20:57 +0800 Subject: [PATCH] fix: correct MCP transport security config to actually allow all hosts by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MCP library's TransportSecurityMiddleware only supports exact host matches or "host:*" port wildcards. It does NOT support a standalone "*" wildcard to mean "allow all hosts" — setting allowed_hosts=["*"] would reject every connection because no Host header equals "*". Worse, when transport_security=None was passed to FastMCP while its default host is "127.0.0.1", FastMCP would auto-enable protection with strict localhost-only rules, overriding GNS3's intent to allow all hosts. Root cause analysis: - FastMCP auto-enables DNS rebinding protection when host is localhost and no explicit TransportSecuritySettings is provided - GNS3 was passing transport_security=None (indirectly via FastMCP's default) when protection was disabled, triggering the auto-enable - The TransportSecuritySettings "allowed_hosts" list does NOT support "*" as a catch-all wildcard This fix: 1. Always pass an explicit TransportSecuritySettings to FastMCP - Disabled: TransportSecuritySettings(enable_dns_rebinding_protection=False) - Enabled: TransportSecuritySettings(enable_dns_rebinding_protection=True, ...) 2. Restore mcp_allowed_hosts and mcp_allowed_origins config fields 3. Set mcp_enable_dns_rebinding_protection default to False (allow all hosts) Behaviour: - Default (no config change): all hosts can connect to MCP server - With mcp_enable_dns_rebinding_protection=true: only configured hosts - Aligns with GNS3 server's 0.0.0.0 binding policy --- gns3server/api/routes/mcp/__init__.py | 22 ++++++++++++++-------- gns3server/config_samples/gns3_server.conf | 19 +++++++------------ gns3server/schemas/config.py | 10 +++++++--- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/gns3server/api/routes/mcp/__init__.py b/gns3server/api/routes/mcp/__init__.py index b3752dd70..00a2770fb 100644 --- a/gns3server/api/routes/mcp/__init__.py +++ b/gns3server/api/routes/mcp/__init__.py @@ -106,14 +106,20 @@ def _create_mcp_server() -> FastMCP: """Create MCP server with security settings from configuration.""" cfg = Config.instance().settings.Server - mcp = FastMCP( - "GNS3 MCP Server", - transport_security=TransportSecuritySettings( - enable_dns_rebinding_protection=cfg.mcp_enable_dns_rebinding_protection, - allowed_hosts=cfg.mcp_allowed_hosts, - allowed_origins=cfg.mcp_allowed_origins, - ), - ) + # Always pass an explicit TransportSecuritySettings to prevent FastMCP + # from auto-enabling protection when host is localhost (its default). + if cfg.mcp_enable_dns_rebinding_protection: + transport_security = TransportSecuritySettings( + enable_dns_rebinding_protection=True, + allowed_hosts=cfg.mcp_allowed_hosts or ["127.0.0.1:*", "localhost:*"], + allowed_origins=cfg.mcp_allowed_origins or ["http://127.0.0.1:*", "http://localhost:*"], + ) + else: + transport_security = TransportSecuritySettings( + enable_dns_rebinding_protection=False, + ) + + mcp = FastMCP("GNS3 MCP Server", transport_security=transport_security) return mcp diff --git a/gns3server/config_samples/gns3_server.conf b/gns3server/config_samples/gns3_server.conf index 36588378a..248e18fcc 100644 --- a/gns3server/config_samples/gns3_server.conf +++ b/gns3server/config_samples/gns3_server.conf @@ -185,15 +185,10 @@ cpus = 1.0 ; Process limit per container pids_limit = 1000 ; MCP (Model Context Protocol) transport security settings -; Enable DNS rebinding protection for MCP server -mcp_enable_dns_rebinding_protection = True - -; Allowed hosts for MCP connections (comma-separated, use * for wildcard) -; Default: * (allow all hosts - recommended for GNS3 VM distribution) -; For enhanced security, restrict to specific hosts: 127.0.0.1:*,localhost:*,192.168.1.3:* -mcp_allowed_hosts = * - -; Allowed origins for MCP connections (comma-separated) -; Default: * (allow all origins) -; For enhanced security: http://127.0.0.1:*,http://localhost:*,http://192.168.1.3:* -mcp_allowed_origins = * +; Disabled by default — allows connections from any host (matches GNS3 +; server's 0.0.0.0 binding). Enable and configure allowed hosts below +; for enhanced security against DNS rebinding attacks. +; Note: Only "host:*" port wildcards are supported (e.g., "127.0.0.1:*"). +;mcp_enable_dns_rebinding_protection = true +;mcp_allowed_hosts = 127.0.0.1:*,localhost:* +;mcp_allowed_origins = http://127.0.0.1:*,http://localhost:* diff --git a/gns3server/schemas/config.py b/gns3server/schemas/config.py index bb32e48fd..9e328b2a9 100644 --- a/gns3server/schemas/config.py +++ b/gns3server/schemas/config.py @@ -164,9 +164,13 @@ class ServerSettings(BaseModel): skills_auto_update: bool = True # MCP (Model Context Protocol) transport security settings - mcp_enable_dns_rebinding_protection: bool = True - mcp_allowed_hosts: list[str] = Field(default=["*"], description="Allowed Host header values for MCP server") - mcp_allowed_origins: list[str] = Field(default=["*"], description="Allowed Origin header values for MCP server") + # DNS rebinding protection is disabled by default to allow connections + # from any host (aligns with GNS3 server's 0.0.0.0 binding). + # Users with security requirements can enable protection and specify + # allowed hosts using "host:*" port wildcard patterns. + mcp_enable_dns_rebinding_protection: bool = False + mcp_allowed_hosts: list[str] = Field(default_factory=list) + mcp_allowed_origins: list[str] = Field(default_factory=list) model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True)