fix: configure MCP transport security with env-driven allowed hosts
All checks were successful
ci/woodpecker/push/build Pipeline was successful
All checks were successful
ci/woodpecker/push/build Pipeline was successful
FastMCP auto-enables DNS rebinding protection when host=127.0.0.1 (the default). Production requests from brain.woltje.com were rejected with 421 Invalid Host header because the allowed_hosts list was empty. Added MCP_ALLOWED_HOSTS config field (comma-separated). When set, DNS rebinding protection is enabled with those hosts; when empty, protection is disabled. Set MCP_ALLOWED_HOSTS=brain.woltje.com in Portainer stack env. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,5 +19,10 @@ class Settings(BaseSettings):
|
|||||||
port: int = 8000
|
port: int = 8000
|
||||||
log_level: str = "info"
|
log_level: str = "info"
|
||||||
|
|
||||||
|
# MCP transport security — comma-separated allowed Host header values.
|
||||||
|
# Set to the public hostname (e.g. "brain.woltje.com") in production.
|
||||||
|
# Empty disables DNS rebinding protection.
|
||||||
|
mcp_allowed_hosts: str = ""
|
||||||
|
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import logging
|
|||||||
from fastapi import Depends, FastAPI, HTTPException, Security
|
from fastapi import Depends, FastAPI, HTTPException, Security
|
||||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
from mcp.server.transport_security import TransportSecuritySettings
|
||||||
|
|
||||||
from src import brain, db
|
from src import brain, db
|
||||||
from src.config import settings
|
from src.config import settings
|
||||||
@@ -29,7 +30,12 @@ def require_api_key(credentials: HTTPAuthorizationCredentials = Security(bearer)
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# MCP server
|
# MCP server
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
mcp = FastMCP("openbrain", stateless_http=True)
|
_allowed_hosts = [h.strip() for h in settings.mcp_allowed_hosts.split(",") if h.strip()]
|
||||||
|
_transport_security = TransportSecuritySettings(
|
||||||
|
enable_dns_rebinding_protection=bool(_allowed_hosts),
|
||||||
|
allowed_hosts=_allowed_hosts,
|
||||||
|
)
|
||||||
|
mcp = FastMCP("openbrain", stateless_http=True, transport_security=_transport_security)
|
||||||
|
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
|
|||||||
Reference in New Issue
Block a user