File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -110,6 +110,9 @@ def _build_transport_security() -> TransportSecuritySettings:
110110 host_pattern = host if ":" in host else f"{ host } :*"
111111 allowed_hosts .add (host_pattern )
112112 allowed_origins .update ({f"http://{ host_pattern } " , f"https://{ host_pattern } " })
113+ bare = host .split (":" , 1 )[0 ]
114+ allowed_hosts .add (bare )
115+ allowed_origins .update ({f"http://{ bare } " , f"https://{ bare } " })
113116
114117 return TransportSecuritySettings (
115118 enable_dns_rebinding_protection = True ,
Original file line number Diff line number Diff line change @@ -180,6 +180,17 @@ def run_server() -> None:
180180 if settings .API_TLS_ENABLED :
181181 ssl_kwargs ["ssl_certfile" ] = settings .SSL_CERT_FILE
182182 ssl_kwargs ["ssl_keyfile" ] = settings .SSL_KEY_FILE
183+ # uvicorn defaults ssl_ciphers to "TLSv1", which restricts TLS 1.2 to legacy
184+ # CBC/SHA-1 suites with no AEAD. Hardened TLS clients (e.g. the Databricks
185+ # Unity Catalog HTTP-connection gateway) negotiate TLS 1.2 with an AEAD-only
186+ # policy and find no shared cipher, so the handshake fails before any request
187+ # is sent. Offer a modern AEAD cipher set (Mozilla intermediate).
188+ ssl_kwargs ["ssl_ciphers" ] = (
189+ "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:"
190+ "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:"
191+ "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:"
192+ "DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
193+ )
183194
184195 LOG .info (
185196 "Starting server on %s:%s (TLS: %s, MCP: %s)" ,
Original file line number Diff line number Diff line change @@ -634,7 +634,8 @@ def _default_ui_base_url() -> str:
634634Extra Host header values accepted by MCP DNS rebinding protection (comma-separated).
635635BASE_URL's hostname and loopback are always allowed; this adds more for multi-domain
636636deployments or reverse proxies that rewrite Host. Entries without a port (`tg.example.com`)
637- get an automatic `:*` wildcard; entries with a port are matched literally
637+ are allowed both with an automatic `:*` port wildcard and bare, so gateways that send an
638+ Origin with no port are accepted; entries with a port are matched literally
638639(`tg.example.com:8080`) or with explicit wildcard (`tg.example.com:*`).
639640Only affects MCP routes — the parent FastAPI app does not validate Host headers.
640641
Original file line number Diff line number Diff line change @@ -30,14 +30,20 @@ def test_loopback_and_base_url_always_present():
3030 assert "https://localhost:*" in settings .allowed_origins
3131
3232
33- def test_extra_host_without_port_gets_wildcard ():
34- """An extras entry without `:` gets `:*` automatically appended."""
33+ def test_extra_host_without_port_gets_wildcard_and_bare ():
34+ """An extras entry without `:` is allowed both with a `:*` port wildcard and bare.
35+
36+ The bare (port-less) form is required because some MCP gateways (e.g. Databricks)
37+ send an Origin with no port, which the `:*` wildcard does not match.
38+ """
3539 settings = _build_with ("http://localhost:8530" , extras = ["tg.example.com" ])
3640
3741 assert "tg.example.com:*" in settings .allowed_hosts
38- assert "tg.example.com" not in settings .allowed_hosts # bare entry should NOT be present
42+ assert "tg.example.com" in settings .allowed_hosts
3943 assert "http://tg.example.com:*" in settings .allowed_origins
4044 assert "https://tg.example.com:*" in settings .allowed_origins
45+ assert "http://tg.example.com" in settings .allowed_origins
46+ assert "https://tg.example.com" in settings .allowed_origins
4147
4248
4349def test_extra_host_with_explicit_port_preserved_literally ():
You can’t perform that action at this time.
0 commit comments