Serving on a port: the Host check needs an exact match, so the README's proxy example and --host 0.0.0.0 refuse every client #13
Labels
No labels
bug
documentation
enhancement
security
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-mcp#13
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo-mcp at
0b29e70b9, readingserving.pyagainst the MCP SDK'sTransportSecurityMiddleware(mcp 2.1.1, as locked).Related to #3.
Listening.securityhands the SDK a list of allowedHostvalues, and the SDK accepts a header only on an exact match or on an entry ending in:*. The list is built fromhost:portstrings, so the headers ordinary clients send never match in two common set-ups, and every request is refused with 421 "Invalid Host header" even when it carries the right bearer secret.src/postulo_mcp/serving.py:117-133(Listening.security:hosts = [self.address, *self.allowed_hosts], origins built asf"{scheme}://{host}"),README.md:136-139,src/postulo_mcp/__main__.py:60-69and:115-120;tests/test_server.py:444-457, which asserts the derivedhttps://postulo.internal:443origin.--allowed-host proxy.example:443. A client reachinghttps://proxy.examplesendsHost: proxy.example, without the default port, and Caddy and Traefik pass the Host header through unchanged by default, so the server seesproxy.example, which is not in the list. The derived originhttps://proxy.example:443likewise never equals a browser'sOrigin: https://proxy.example.--host 0.0.0.0(or::), the only non-loopback entry is0.0.0.0:8000, which no client sends; a LAN client sends192.168.1.5:8000and is refused. Start-up meanwhile prints "Listening on 0.0.0.0:8000 … Every request must carry the bearer token", which implies it will work.--allowed-hostwithout a port, allow the bare name andname:*; with a port, also allow the bare name when the port is 80 or 443. Build origins without a default port (https://name). When--hostis a wildcard address, refuse to start unless at least one--allowed-hostis given, and say why. Change the README example to--allowed-host proxy.example.Done when
tests/test_server.pybuildsListening.securitywith--allowed-host proxy.exampleand checks thatTransportSecurityMiddlewareacceptsHost: proxy.exampleandOrigin: https://proxy.example.tests/test_server.pyshows thatserving.planwithhost="0.0.0.0"and no--allowed-hoststops with a sentence.