Allow the public host in MCP DNS-rebinding protection - #8
Merged
Conversation
mcp 0.23.0 added DNS-rebinding protection to StreamableHTTPTransport: any Host header other than loopback is rejected with 403 "Forbidden: Invalid Host header" unless allow-listed via the new allowed_hosts: option. Production installs this gem directly (`gem install mail_mcp -v X`), which bypasses Gemfile.lock and resolves dependencies from the gemspec at image build time. `mcp "~> 0.14"` therefore floated up to 0.25.0 on a rebuild and started 403ing every authenticated /mcp call, with no code change. Both reported symptoms share this cause. resolve_mcp_context halts with 401 before the transport is constructed, so unauthenticated discovery still worked and the OAuth flow completed; the connector's first authenticated initialize call then hit the 403 and surfaced it as "Failed to complete OAuth authorization". Allow-list the BASE_URL host, and pin mcp to a single minor — since the gemspec constraints act as production pins here, a floor alone would leave the next mcp minor free to break this the same way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Users could not authenticate. Two symptoms, one cause:
{"error": "Authorization failed (403 Forbidden). Forbidden: Invalid Host header..."}Root cause
No code change caused this — a transitive dependency did.
mcp 0.23.0added DNS-rebinding protection toStreamableHTTPTransport: anyHostheader other than loopback (127.0.0.1,::1,localhost) is rejected with 403 unless allow-listed via the newallowed_hosts:option.Production installs this gem directly (
gem install -N mail_mcp -v 1.0.2), which bypassesGemfile.lockand resolves dependencies from the gemspec at image-build time.mcp "~> 0.14"floated up to 0.25.0 on a rebuild, and every authenticated/mcpcall started 403ing.The OAuth symptom is the same bug wearing a different hat:
resolve_mcp_contexthalts with 401 before the transport is constructed, so unauthenticated discovery still worked — login form rendered, IMAP/SMTP validated, code exchanged, token issued. The connector's first authenticatedinitializecall then hit the 403 and reported it as an authorization failure.Changes
lib/mail_mcp/app.rb— passallowed_hosts:(derived fromBASE_URL) to the transportmail_mcp.gemspec— pinmcpto~> 0.25.0. Not just a floor: because prod installs the gem directly, gemspec constraints are the production pins, and~> 0.23would leave the next mcp minor free to break this the same wayGemfile.lock—bundle update mcp→ 0.25.0, so local/CI match prodspec/mail_mcp/app_spec.rb—/mcpspecs send the realHost, plus coverage for host-with-port, a rebound host (403), and an end-to-endtools/callVerification
Reproduced the exact production 403 in rspec first (rack-test defaults to
Host: example.org), then confirmed against the real artifact: built the gem, installed it into a cleanGEM_HOME(resolves mcp 0.25.0), ranmail_mcp serverover HTTP.GET /healthPOST /mcpprobeWWW-Authenticateinitialize, public Hosttools/list, rebound Host84 specs pass, rubocop clean.
Deploying
Merging is not enough — the deploy pins an exact gem version. After merge: bump
lib/mail_mcp/version.rbto 1.0.3, push to RubyGems, and update the deploy Dockerfile togem install -N mail_mcp -v 1.0.3.Follow-up (not in this PR)
/.well-known/oauth-authorization-serveromitstoken_endpoint_auth_methods_supported, whose RFC 8414 default isclient_secret_basic— butPOST /oauth/tokenonly readsparams[:client_secret], never theAuthorizationheader. Deliberately left out so it doesn't muddy this diagnosis. Worth checking if the connect flow still fails after release.🤖 Generated with Claude Code