feat: add configurable MCP transport security settings via gns3_server.conf

Add MCP transport security configuration to gns3_server.conf with permissive
defaults that align with GNS3's design philosophy and VM distribution requirements.

## Changes

### 1. Configuration Schema (gns3server/schemas/config.py)
- Added MCP transport security fields to ServerSettings class:
  - mcp_enable_dns_rebinding_protection (bool, default: True)
  - mcp_allowed_hosts (list[str], default: ["*"])
  - mcp_allowed_origins (list[str], default: ["*"])
- Added field validators to handle comma-separated string input

### 2. MCP Server Initialization (gns3server/api/routes/mcp/__init__.py)
- Import TransportSecuritySettings from mcp.server.transport_security
- Added _create_mcp_server() function to read configuration
- Updated FastMCP instantiation to use configured security settings

### 3. Configuration Sample (gns3server/config_samples/gns3_server.conf)
- Added MCP transport security settings section
- Documented default behavior and security options
- Provided examples for different use cases

## Design Philosophy

**Default: Allow All Hosts** (matches GNS3's 0.0.0.0 binding):
- VM distribution works out-of-the-box
- Users can access from any network location
- Security-conscious users can restrict when needed

**Security: Optional Restriction**:
Users can configure specific hosts for enhanced security:
``ini
mcp_allowed_hosts = 127.0.0.1:*,localhost:*,192.168.1.3:*
mcp_allowed_origins = http://127.0.0.1:*,http://localhost:*,http://192.168.1.3:*
```

## Benefits

- Flexible: Users can configure based on security requirements
- User-friendly: Default matches GNS3's 0.0.0.0 binding philosophy
- Maintainable: No code changes needed for different deployment scenarios
- Secure: DNS rebinding protection remains enabled with configurable hosts

## Related

- Issue #2771
- FastMCP DNS rebinding protection design
- Existing skills configuration in ServerSettings
This commit is contained in:
YueGuobin 2026-06-05 23:06:06 +08:00
parent 68f63597eb
commit bb39238f02
No known key found for this signature in database
3 changed files with 51 additions and 2 deletions

View File

@ -39,6 +39,7 @@ from fastapi.responses import Response
from pydantic import Field
from mcp.server.fastmcp import FastMCP
from mcp.server.transport_security import TransportSecuritySettings
from gns3server.config import Config
from gns3server.services import auth_service
@ -101,7 +102,22 @@ def _server_url() -> str:
# ── FastMCP Server ────────────────────────────────────────────────────
mcp = FastMCP("GNS3 MCP Server")
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,
),
)
return mcp
mcp = _create_mcp_server()
# ── Tool handlers ─────────────────────────────────────────────────────

View File

@ -183,4 +183,17 @@ memory = 2g
; CPU cores per container (e.g., 1.0, 2.0)
cpus = 1.0
; Process limit per container
pids_limit = 1000
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 = *

View File

@ -162,8 +162,28 @@ class ServerSettings(BaseModel):
skills_repo_url: str = "https://github.com/gns3/gns3-skills.git"
skills_repo_branch: str = "main"
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")
model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True)
@field_validator("mcp_allowed_hosts", mode="before")
@classmethod
def split_mcp_allowed_hosts(cls, v):
if v and isinstance(v, str):
return v.split(",")
return list()
@field_validator("mcp_allowed_origins", mode="before")
@classmethod
def split_mcp_allowed_origins(cls, v):
if v and isinstance(v, str):
return v.split(",")
return list()
@field_validator("additional_images_paths", mode="before")
@classmethod
def split_additional_images_paths(cls, v):