diff --git a/README.md b/README.md index d5005ed6..b6f91c7b 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,44 @@ Postgres MCP Pro supports multiple *access modes* to give you control over the o To use restricted mode, replace `--access-mode=unrestricted` with `--access-mode=restricted` in the configuration examples above. +##### Transport Security Configuration + +Postgres MCP Pro includes DNS rebinding protection to secure the server against certain types of attacks. +By default, the server allows connections from common local and Docker hostnames. +You can customize this behavior using environment variables: + +- **`MCP_ENABLE_DNS_REBINDING_PROTECTION`**: Controls whether DNS rebinding protection is enabled. Set to `false` to disable. Default: `true`. +- **`MCP_ALLOWED_HOSTS`**: Comma-separated list of allowed host patterns. Default: `localhost:*,127.0.0.1:*,0.0.0.0:*,postgres-mcp-server:*,host.docker.internal:*`. +- **`MCP_ALLOWED_ORIGINS`**: Comma-separated list of allowed origins. Default: empty (allows any origin). + +For example, to restrict allowed hosts in your configuration: + +```json +{ + "mcpServers": { + "postgres": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "DATABASE_URI", + "-e", + "MCP_ALLOWED_HOSTS", + "crystaldba/postgres-mcp", + "--access-mode=unrestricted" + ], + "env": { + "DATABASE_URI": "postgresql://username:password@localhost:5432/dbname", + "MCP_ALLOWED_HOSTS": "localhost:*,myapp.example.com:*" + } + } + } +} +``` + + #### Other MCP Clients Many MCP clients have similar configuration files to Claude Desktop, and you can adapt the examples above to work with the client of your choice. diff --git a/src/postgres_mcp/server.py b/src/postgres_mcp/server.py index f3ba8f8b..cc2b9a19 100644 --- a/src/postgres_mcp/server.py +++ b/src/postgres_mcp/server.py @@ -13,6 +13,7 @@ import mcp.types as types from mcp.server.fastmcp import FastMCP +from mcp.server.transport_security import TransportSecuritySettings from mcp.types import ToolAnnotations from pydantic import Field from pydantic import validate_call @@ -34,8 +35,29 @@ from .sql import obfuscate_password from .top_queries import TopQueriesCalc -# Initialize FastMCP with default settings -mcp = FastMCP("postgres-mcp") +# Initialize FastMCP with transport security settings +# Configure to allow requests from localhost, 127.0.0.1, and Docker container names +# These can be customized via environment variables: +# - MCP_ENABLE_DNS_REBINDING_PROTECTION: Set to "false" to disable (default: "true") +# - MCP_ALLOWED_HOSTS: Comma-separated list of allowed hosts (default: localhost:*,127.0.0.1:*,0.0.0.0:*,postgres-mcp-server:*,host.docker.internal:*) +# - MCP_ALLOWED_ORIGINS: Comma-separated list of allowed origins (default: empty, allows any origin) + +dns_rebinding_protection = os.environ.get("MCP_ENABLE_DNS_REBINDING_PROTECTION", "true").lower() == "true" + +default_allowed_hosts = "localhost:*,127.0.0.1:*,0.0.0.0:*,postgres-mcp-server:*,host.docker.internal:*" +allowed_hosts_str = os.environ.get("MCP_ALLOWED_HOSTS", default_allowed_hosts) +allowed_hosts = [host.strip() for host in allowed_hosts_str.split(",") if host.strip()] + +allowed_origins_str = os.environ.get("MCP_ALLOWED_ORIGINS", "") +allowed_origins = [origin.strip() for origin in allowed_origins_str.split(",") if origin.strip()] + +transport_security = TransportSecuritySettings( + enable_dns_rebinding_protection=dns_rebinding_protection, + allowed_hosts=allowed_hosts, + allowed_origins=allowed_origins, +) + +mcp = FastMCP("postgres-mcp", transport_security=transport_security) # Constants PG_STAT_STATEMENTS = "pg_stat_statements"