feat: KEEP-438 add Cloudflare Access header support#63
Merged
Conversation
…ments Lets the CLI talk to PR previews and staging hosts behind Cloudflare Zero Trust. Reads CF_ACCESS_CLIENT_ID/CF_ACCESS_CLIENT_SECRET (service tokens) and CF_AUTHORIZATION (cookie minted by `cloudflared access login`) from the environment, merged on top of any per-host headers in hosts.yml. Env wins over hosts.yml, matching the KH_API_KEY > hosts.yml precedence model. Also fixes the auth-token validation and doctor health-check paths, which built their own *http.Client and bypassed the shared khhttp.Client header injection -- so without these changes any kh command that lands on those paths would 403 at CF Access even with creds configured.
…ad of clobbering Concatenate `CF_Authorization=<jwt>` onto any pre-existing `Cookie:` value in hosts.yml.headers using the `;` separator from RFC 6265, instead of overwriting it. Adds tests for the append path and for the combined service-token + cookie env case. Caught in PR review.
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.
Summary
khcan talk to PR previews (app-pr-<N>.keeperhub.com) and staging behind Cloudflare Zero Trust.CF_ACCESS_CLIENT_ID+CF_ACCESS_CLIENT_SECRET(service-token pair, sent asCF-Access-Client-Id/CF-Access-Client-Secret) andCF_AUTHORIZATION(cookie minted bycloudflared access login, sent asCookie: CF_Authorization=...). Env wins overhosts.yml, matching the existingKH_API_KEY > hosts.ymlprecedence.*http.Clientand bypassed the sharedkhhttp.Clientheader injection:internal/auth/token.go(fetchSessionInfo,fetchAPIKeyInfo,fetchOrgDetails) andcmd/doctor/doctor.go. Without this,kh auth statusandkh doctorwould 403 at CF Access even with creds configured.Cookie:value inhosts.yml.headers(RFC 6265;separator) instead of clobbering it.Notes
headers:map inhosts.ymlalready supported arbitrary headers via the shared client. This PR is just the env-var convenience layer plus the bypass-path fixes.internal/auth/device.go(browser login flow) loops overhosts.ymlheaders but does not pick up env vars. Left as follow-up since service tokens are CI-only and the affected envs are reached with API keys, not the device flow.fetchAPIKeyInfoaccepts any HTTP 200 (so a CF Access HTML login page falsely validates). Independent issue, flagged but not fixed here.Test plan
go build ./...cleango vet ./...cleango test ./...passes (unit tests forMergeCloudflareAccessEnvcover no-env / service-token / partial-creds-ignored / cookie / cookie-appends-to-existing / env-precedence / service-token+cookie combined / no-mutation)app-staging.keeperhub.comwith a realCF_AUTHORIZATIONcookie:kh chain list->decoding chains response: invalid character '<'(request lands on the CF Access HTML login page)kh doctoragainst the same host: chains check goes from "reachable" (HTML 200) to "19 chains available" (real JSON) once cookie is setapp-pr-1162.keeperhub.comusinghosts.ymlonly (no env):kh chain list,kh workflow list,kh workflow create,kh workflow get,kh auth status,kh doctorall working through CF AccessUsage
One-time setup per gated environment
Either path works; pick one.
A)
hosts.yml(recommended for everyday work)Then just run
kh --host app-pr-1234.keeperhub.com workflow list(orexport KH_HOST=app-pr-1234.keeperhub.com).B) Environment variables (recommended for CI / one-offs)
Env vars override
hosts.ymlon key collision; non-CF headers inhosts.ymlare preserved.Cookie auth via
cloudflared(when service tokens aren't authorized for the env)When
khagainst the env starts returninginvalid character '<'(response is the CF Access HTML login page), the JWT has expired - re-runcloudflared access login.Precedence
Same model as
KH_API_KEY > hosts.yml:CF_ACCESS_CLIENT_ID/CF_ACCESS_CLIENT_SECRET,CF_AUTHORIZATION) win on key collision.hosts.ymlheaders:entries that don't collide are preserved.Authorization: Bearer <kh_token>is set independently viatoken:/KH_API_KEYand is never overwritten by header merging.Caveats
service_token_status: falsein the redirect JWT meta) by per-PR CF Access apps even though the headers reach CF.cloudflared access tokenreturns the cached JWT unconditionally and does not auto-refresh; once expired, you must re-runaccess login.Cookieenv merge appends to any existingCookie:inhosts.yml.headersrather than overwriting, so users mixing CF auth with other cookies don't lose them.