fix!: only trust x-forwarded-host from configured trusted proxies by geokat · Pull Request #26204 · coder/coder · GitHub
Skip to content

fix!: only trust x-forwarded-host from configured trusted proxies#26204

Merged
geokat merged 3 commits into
mainfrom
george/plat-259/only-trust-x-forwarded-host-from-trusted-proxies
Jun 11, 2026
Merged

fix!: only trust x-forwarded-host from configured trusted proxies#26204
geokat merged 3 commits into
mainfrom
george/plat-259/only-trust-x-forwarded-host-from-trusted-proxies

Conversation

@geokat

@geokat geokat commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Problem

Subdomain app routing resolved the app identity from httpapi.RequestHost, which
returns the client-supplied X-Forwarded-Host header verbatim, and no middleware
validated or stripped it. Because the application_connect cookie is scoped to
the wildcard apps domain, JavaScript running in a share=authenticated app could
fetch() with a forged X-Forwarded-Host pointing at a victim's owner-only app.
coderd would route and authorize the request as the victim and return the private
app's response same-origin to the attacker's page, allowing the attacker to read
the victim's private app output.

Fix

Remove httpapi.RequestHost and resolve the host through a new
httpmw.EffectiveHost, which honors X-Forwarded-Host only when the original
socket peer is a configured trusted origin, otherwise falling back to the received
Host header. This ties host trust to the same RealIPConfig model already used
for X-Forwarded-For and X-Forwarded-Proto. EffectiveHost is wired into
HandleSubdomain for both coderd and wsproxy. Request logs now record both the
effective host and the raw received_host.

The trust check uses the original socket peer rather than the post-ExtractRealIP
(spoofable) forwarded client IP, with unit tests pinning that behavior, plus a
HandleSubdomain regression test confirming a forged X-Forwarded-Host from an
untrusted peer never reaches token resolution.

Compatibility note

Deployments that rely on reverse proxies rewriting Host and forwarding the
original host in X-Forwarded-Host now need CODER_PROXY_TRUSTED_ORIGINS
configured for those proxies. Otherwise subdomain app routing will ignore
X-Forwarded-Host.

Refs: https://linear.app/codercom/issue/PLAT-259

@linear-code

linear-code Bot commented Jun 10, 2026

Copy link
Copy Markdown

@geokat geokat force-pushed the george/plat-259/only-trust-x-forwarded-host-from-trusted-proxies branch from 350bd43 to 3cf4d6c Compare June 10, 2026 17:01
Subdomain app routing derived the app identity from
httpapi.RequestHost, which returned the client-supplied
X-Forwarded-Host header verbatim. No middleware validated or stripped
that header, so a request from an untrusted peer could forge it. Since
the application_connect cookie is scoped to the wildcard apps domain,
JavaScript in a share=authenticated app could fetch() with a forged
X-Forwarded-Host pointing at a victim's owner-only app; coderd routed
and authorized the request as the victim and returned the private app
response same-origin to the attacker.

Replace RequestHost with httpmw.EffectiveHost, which honors
X-Forwarded-Host only when the original socket peer is a configured
trusted origin, otherwise falling back to the received Host header.
This ties host trust to the same RealIPConfig model already used for
X-Forwarded-For and -Proto. Wire it into HandleSubdomain for both
coderd and wsproxy, and log both the effective host and the raw
received_host.

Add coverage: EffectiveHost unit tests assert the trust decision uses
the socket peer rather than the spoofable forwarded client IP, and a
HandleSubdomain test confirms a forged X-Forwarded-Host from an
untrusted peer never reaches token resolution.

Refs: https://linear.app/codercom/issue/PLAT-259
@geokat geokat force-pushed the george/plat-259/only-trust-x-forwarded-host-from-trusted-proxies branch from 3cf4d6c to 83fd965 Compare June 10, 2026 17:08
Comment on lines +367 to +369
loggermw.Logger(s.Logger, func(r *http.Request) string {
return httpmw.EffectiveHost(s.Options.RealIPConfig, r)
}),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This callback is not ideal, but removing it cleanly would require a broader refactor to avoid the import cycle. I kept the callback here to avoid adding extra churn to a security fix.

@geokat geokat marked this pull request as ready for review June 10, 2026 17:23
@geokat geokat requested a review from johnstcn June 10, 2026 17:23
@coder-tasks

coder-tasks Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Documentation Check

Updates Needed

  • codersdk/deployment.go (generates docs/reference/cli/server.md) - The --proxy-trusted-origins description now includes "and X-Forwarded-Host for subdomain app routing."
  • docs/admin/networking/wildcard-access-url.md - Added a note that X-Forwarded-Host is only honored from trusted proxy origins, with a link to CODER_PROXY_TRUSTED_ORIGINS. (Originally suggested for docs/admin/networking/index.md; the wildcard access URL page is a better fit.)

Automated review via Coder Agents

@github-actions

Copy link
Copy Markdown

@geokat geokat added the release/breaking This label is applied to PRs to detect breaking changes as part of the release process label Jun 10, 2026
@github-actions github-actions Bot changed the title fix: only trust x-forwarded-host from configured trusted proxies fix!: only trust x-forwarded-host from configured trusted proxies Jun 10, 2026
@geokat geokat force-pushed the george/plat-259/only-trust-x-forwarded-host-from-trusted-proxies branch from fa0a412 to ed2a41c Compare June 10, 2026 18:40
Comment thread agent/agentchat/log_test.go
Comment thread coderd/httpmw/realip_test.go
@geokat geokat merged commit b5ef700 into main Jun 11, 2026
51 of 54 checks passed
@geokat geokat deleted the george/plat-259/only-trust-x-forwarded-host-from-trusted-proxies branch June 11, 2026 17:55
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

backport release/breaking This label is applied to PRs to detect breaking changes as part of the release process

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants