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)