FastMCP enables DNS rebinding protection inside __init__ when it is built with
a loopback bind address, and create_server() takes the default 127.0.0.1:
if transport_security is None and host in ("127.0.0.1", "localhost", "::1"):
transport_security = TransportSecuritySettings(
enable_dns_rebinding_protection=True,
allowed_hosts=["127.0.0.1:*", "localhost:*", "[::1]:*"],
)
main() reassigns settings.host afterwards, which never revisits that decision,
so the allow-list stayed loopback-only. Any client reaching the server under a
real hostname got 421 Misdirected Request with "Invalid Host header". It reads
as a hang rather than a rejection, because clients retry until they time out --
bifrost reported fin as simply disconnected after exhausting its retries.
Rebuild the settings after the host is known. Protection stays on; the loopback
patterns are kept and FIN_MCP_ALLOWED_HOSTS adds to them (comma separated,
"host:*" allows any port). Unlisted hosts are still refused.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RTuguQqazhoWUsh2GkPg9a
51 lines
1.4 KiB
YAML
51 lines
1.4 KiB
YAML
services:
|
|
api:
|
|
image: harbor.cloud.traunseenet.com/library/fin-api:latest
|
|
build: ./api
|
|
volumes:
|
|
- sqlite_data:/data
|
|
environment:
|
|
- DATABASE_URL=sqlite:////data/fin.db
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
|
|
mcp:
|
|
image: harbor.cloud.traunseenet.com/library/fin-mcp:latest
|
|
build: ./cli
|
|
environment:
|
|
- FIN_API_URL=http://api:8000
|
|
- FIN_MCP_TRANSPORT=streamable-http
|
|
- FIN_MCP_HOST=0.0.0.0
|
|
- FIN_MCP_PORT=8000
|
|
- FIN_MCP_PATH=/mcp
|
|
- FIN_MCP_ALLOW_WRITES=1
|
|
# Clients reach this service as "mcp" on the compose network, and FastMCP
|
|
# rejects any Host it was not told to trust with a 421.
|
|
- FIN_MCP_ALLOWED_HOSTS=mcp:*
|
|
# No ports mapping on purpose: the API has no auth, so the MCP surface
|
|
# stays reachable only from inside the compose network. Add a mapping
|
|
# only if you also set FIN_API_TOKEN.
|
|
depends_on:
|
|
api:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
|
|
web:
|
|
image: harbor.cloud.traunseenet.com/library/fin-web:latest
|
|
build: ./web
|
|
ports:
|
|
- "80:80"
|
|
environment:
|
|
- API_UPSTREAM=api:8000
|
|
depends_on:
|
|
api:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
sqlite_data:
|