Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add per-host api_host override for custom API endpoint routing - #13760

Closed
dziemba wants to merge 1 commit into
cli:trunkfrom
dziemba:api-host-transport-rewrite
Closed

Add per-host api_host override for custom API endpoint routing#13760
dziemba wants to merge 1 commit into
cli:trunkfrom
dziemba:api-host-transport-rewrite

Conversation

@dziemba

@dziemba dziemba commented Jun 30, 2026

Copy link
Copy Markdown

Allow routing API traffic to a different endpoint per host, via the api_host key in hosts.yml or the GH_API_HOST_<host> environment variable. This supports API gateways, reverse proxies, and GHEC data-residency tenants where the organization has no control over the api.* hostname.

The implementation uses an HTTP transport-layer rewriter that swaps the request destination while preserving the original hostname for TLS SNI, the Host header, and — critically — token resolution. The transport stacking order (auth → rewriter → base) ensures auth always sees the original hostname, so credentials are looked up correctly before the request is routed to the gateway.

The resolver also handles the api.* prefix that go-gh prepends to github.com and *.ghe.com hostnames when constructing API URLs, so a user-level config for example.ghe.com correctly matches requests addressed to api.example.ghe.com.

Other approaches tried and abandoned:

  • Modifying ghinstance URL construction (RESTPrefix/GraphQLEndpoint) — go-gh reconstructs URLs independently, so changes don't propagate
  • Patching AuthConfig.ActiveToken with reverse api_host lookup — requires changes at every auth call site and breaks encapsulation
  • Wrapping the go-gh HTTP client at the api.Client level — too late in the chain, misses direct http.Client usage

Fixes #13717

Allow routing API traffic to a different endpoint per host, via the
api_host key in hosts.yml or the GH_API_HOST_<host> environment
variable. This supports API gateways, reverse proxies, and GHEC
data-residency tenants where the organization has no control over
the api.* hostname.

The implementation uses an HTTP transport-layer rewriter that swaps
the request destination while preserving the original hostname for
TLS SNI, the Host header, and — critically — token resolution. The
transport stacking order (auth → rewriter → base) ensures auth
always sees the original hostname, so credentials are looked up
correctly before the request is routed to the gateway.

The resolver also handles the api.* prefix that go-gh prepends to
github.com and *.ghe.com hostnames when constructing API URLs, so a
user-level config for example.ghe.com correctly matches requests
addressed to api.example.ghe.com.

Other approaches tried and abandoned:
- Modifying ghinstance URL construction (RESTPrefix/GraphQLEndpoint)
  — go-gh reconstructs URLs independently, so changes don't propagate
- Patching AuthConfig.ActiveToken with reverse api_host lookup —
  requires changes at every auth call site and breaks encapsulation
- Wrapping the go-gh HTTP client at the api.Client level — too late
  in the chain, misses direct http.Client usage

Refs cli#13717
@dziemba
dziemba requested a review from a team as a code owner June 30, 2026 14:43
@dziemba
dziemba requested a review from babakks June 30, 2026 14:43
@github-actions github-actions Bot added external pull request originating outside of the CLI core team unmet-requirements needs-triage needs to be reviewed labels Jun 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your pull request! This is a large change (303 lines across 14 files) that doesn't reference a help wanted issue.

Large feature PRs require prior discussion in an issue before implementation — this helps the team assess whether the feature aligns with the project's direction before significant effort is invested.

Please open an issue to discuss this feature first. This PR will be automatically closed in 2 days if requirements are not met.

Full contribution requirements
  1. Include a detailed description of what this PR does
  2. Link to an issue with the help wanted label (use Fixes #123 or Closes #123)

@williammartin williammartin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi @dziemba, thanks for getting this moving with your PR.

The transport rewriting implementation here is clever, but it introduces some complexity we'd like to avoid and misses a number of important cases. The list below reflects our understanding today. Some of these are small and you might think "well that's easy to fix", which is fair; the point however, is that it's deceptive and there is unfortunately a lot of complexity under the surface that we'd prefer to try and find a holistic fix for.

What doesn't work in this PR

To the best of our knowledge, these are the current issues:

  1. The gh api command constructs an http client distinct from the factory which other commands do, so it won't hit the host rewriting transport.
  2. Any command that paginates will fail to authenticate on the 2nd and onwards pages. gh follows the absolute URL in the previous response's Link header, that URL carries the gateway's hostname, and gh won't be able to lookup a token to include in the authorization header. This covers gh api --paginate, gh secret list, gh variable list, gh cache list, gh status, and more.
  3. Any function where the API endpoint is derived from the response body will bypass the configurable API host. gh secret list and gh variable list follow selected_repositories_url, gh release download and gh run download follow asset and artifact URLs, and gh extension install follows a release asset url.
  4. Since the implementation lives in cli/cli it also prevents extensions from benefiting from the feature, which we know other users want.
  5. The implementation also bypasses another client used by gh auth login. This only makes one true API request, so may not be a large issue, but is demonstrative of the breadth of this change and small issues to address across the codebase.

Points 2 and 3 are relevant for the URL rewriting you mentioned in #13717 (comment). The issue as you know is that this PR requires the host to be canonical at the time it gets to AddAuthTokenHeader. I created a test here that I believe demonstrates that issue on this branch.

Security

The implementation bypasses a key part of gh's security model, hiding the bypass in the transport layer. Through the existing AddAuthTokenHeader transport, we look up a token for the hostname of the request, and the approach in this PR involves rewriting the host after the lookup, which is opaque and spreads the security model across disparate components.

We understand the trusted host scope needs to expand to support this feature, but we would prefer to see the logic consolidated.

Maintainability

Similar to the previous point, as per your PR description, there is a certain fragility in this approach, with request authentication depending on a set of carefully ordered transports. Of particular concern is that the functionality of these features are split across the two transports but a reader of either one wouldn't understand. Points 2 and 3 above are examples of that fragility.

Where this leaves us

To align this feature with our existing security model, we're exploring what it would take to add this as a first-class input to our API client. What this means is that we transparently augment the security features that exist rather than add a hidden bypass. As a feature that is attractive to enterprises using gh, it's important we deliver something that is easy to maintain for all future integrators, and something that integrates clearly and safely with our existing critical security logic.

This proper implementation includes more effort because it involves changes to the construction of this API client at command invocation time, and many call sites that need updating. It's important to point out that some level of effort remains the same - as this touches critical gh authentication infrastructure no matter the implementation, it will require a review and discussion by GitHub's security specialists team. There is typically a lead time of weeks for a review of things at this severity level.

In short, the extra code to review and architectural design needed to implement this transparently adds time to the delivery schedule, and our security review process adds time regardless of the path.

What's currently in flight?

Currently, in parallel we are:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external pull request originating outside of the CLI core team ready-for-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow configuring a custom API endpoint per host

3 participants