oauth: H-1 — refuse to start when gating + cluster_secret without require_email_verified#105
Open
BorisTyshkevich wants to merge 1 commit intofeature/oauth-spec-compliance-hardeningfrom
Conversation
6 tasks
88b1135 to
21195ed
Compare
…uire_email_verified In gating + cluster_secret mode, GetClickHouseClientWithOAuth uses `oauthClaims.Email` verbatim as the ClickHouse `initial_user` field (server_client.go:303-311). ClickHouse trusts that impersonation because the cluster_secret authenticates the peer; the username is just metadata. So without `require_email_verified=true`, any IdP- issued token with `email_verified=false` (e.g. an Auth0 Database Connection that forgot to require verification, a self-hosted OIDC, a federated partner IdP) lets the bearer impersonate any provisioned ClickHouse user just by typing their email at registration. Reproducer: Auth0 tenant with both Google Social + a Database Connection that doesn't verify email. Attacker registers `admin@altinity.com` on the Database Connection (no verification sent / required), logs in via that connection, gets an id_token with `email=admin@altinity.com, email_verified=false`. MCP validates signature/iss/aud/exp — all pass. MCP routes the bearer to gating-mode CH using cluster_secret with `initial_user= admin@altinity.com`. CH looks up admin, applies admin's grants. Attacker now runs queries as admin. `allowed_email_domains` alone doesn't fix this — domain restriction allows `admin@altinity.com` regardless of `email_verified`. The actual protection is `require_email_verified=true`. Fix: validateOAuthRuntimeConfig now refuses to start when: - OAuth is enabled in gating mode AND - ClickHouse cluster_secret is set AND - require_email_verified is false Forces operators to opt in explicitly. Auth0 + Google Social deployments can flip the flag with one config line; deployments that intentionally federate with non-verifying IdPs need to redesign the impersonation path (don't use cluster_secret with unverified-email-source IdPs). Tests: 4 new sub-tests in TestValidateOAuthRuntimeConfig covering the unsafe combo, the safe-with-flag combo, the no-cluster-secret case (unaffected), and the forward-mode case (unaffected because forward never uses Email as initial_user). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
804528d to
5302328
Compare
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
H-1 from the OAuth security review: refuse to start in gating mode when
clickhouse.cluster_secretis set butoauth.require_email_verifiedis false.This is the second of a 3-PR stack:
Threat model
In gating mode with
clickhouse.cluster_secretconfigured, the path atpkg/server/server_client.go:303-311feedsoauthClaims.Emailverbatim into ClickHouse'sinitial_user. ClickHouse trusts the impersonation because the cluster_secret authenticates the peer, not the end user.Without
oauth.require_email_verified=true, any IdP-issued token withemail_verified=falselets the bearer impersonate any ClickHouse user just by typing their email at registration:Auth0's hosted social-login providers (Google, GitHub) emit
email_verified=truereliably, but a misconfigured custom DB connection or a permissive partner IdP can issueemail_verified=falsetokens.Mitigation
validateOAuthRuntimeConfigincmd/altinity-mcp/main.gorefuses to boot whenIsGatingMode() && cluster_secret != "" && !RequireEmailVerified. Operators must explicitly opt in to the verified-email check, and the binary won't start without it. Auth0 + Google Social emitemail_verified=trueon every token, so this is safe to enable in our demo deployments.The
RequireEmailVerifiedfield already existed inOAuthConfig(used byvalidateOAuthIdentityPolicy); this PR only adds the startup check that makes it mandatory under the dangerous combination.Live deployment
Already running on
otel-mcp.demo.altinity.cloudsince the H-1 deploy (imagefeature-strict-schema-14da408).currentUser()returnsbtyshkevich@altinity.comconfirming the gating-mode + cluster_secret + email-verified chain works correctly with verified Auth0 + Google Social tokens.Test plan
cmd/altinity-mcp/oauth_server_test.goexercisesvalidateOAuthRuntimeConfigrejection with the dangerous combination🤖 Generated with Claude Code