Altinity / Altinity/altinity-oauth-helper

ADR: Separate ClickHouse edge protection from OAuth authorization

Open
#17 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

adr
Dominant language
Go
Stars
5
Forks
0
Avg merge
7h 12m
Merged PRs (30d)
31

Description

Status

Proposed

Context

OAuth support for Altinity Stable ClickHouse 24.8 requires two distinct concerns:

  1. Identity/authentication compatibility between OAuth/OIDC credentials and ClickHouse's authentication mechanisms.
  2. Edge/admission protection for ClickHouse's HTTP and native TCP interfaces against abusive, pathological, or resource-exhausting clients.

These concerns have different trust boundaries and failure modes and should not be combined into one authorization proxy.

The companion ADR proposes adding ch-oauth-ldap to Altinity/altinity-oauth-helper. ClickHouse uses that helper through its LDAP external user directory to authenticate OAuth identities and derive externally assigned ClickHouse roles. ClickHouse remains authoritative for RBAC.

A separate front proxy may be implemented in another repository, potentially as part of CHGuard if that codebase is a good fit after review.

Target clients already support OAuth and are responsible for obtaining and refreshing access tokens.

Decision

Keep the front ClickHouse proxy separate from altinity-oauth-helper and define its primary purpose as connection admission, protocol protection, rate limiting, and DoS/resource-abuse defense.

The front proxy may validate OAuth tokens to establish an admission principal and reject invalid traffic early, but it MUST NOT become the source of ClickHouse authorization truth.

The architectural boundary is:

OAuth-capable client
        |
        | OAuth token
        v
+-------------------------+
| ClickHouse edge proxy   |
|                         |
| admission authentication|
| rate/connection limits  |
| protocol protections    |
| TCP session lifetime    |
| observability           |
+------------+------------+
             |
             | token / CH-compatible credential envelope
             v
+-------------------------+
| ClickHouse              |
|                         |
| LDAP external directory |
| ClickHouse RBAC         |
+------------+------------+
             |
             | LDAP bind + role search
             v
+-------------------------+
| altinity-oauth-helper   |
| ch-oauth-ldap           |
|                         |
| authoritative token     |
| validation for CH auth  |
| identity + groups       |
+-------------------------+

Responsibility split

Front edge proxy

The proxy is responsible for protecting ClickHouse before expensive or long-lived work reaches the server.

Expected responsibilities include:

  • terminate/validate TLS as required by deployment;
  • validate OAuth tokens sufficiently to reject invalid/expired/wrong-issuer/wrong-audience traffic before opening or retaining expensive ClickHouse resources;
  • derive an authenticated admission principal for rate limiting and connection policy;
  • limit concurrent connections globally and per principal/source;
  • limit connection-establishment rate;
  • rate-limit HTTP requests;
  • enforce native TCP idle timeouts;
  • enforce maximum native TCP session lifetime;
  • ensure OAuth-authenticated native sessions do not survive past the credential lifetime used to establish them;
  • defend against slow handshakes / slowloris behavior;
  • enforce bounded handshake/header/message sizes where safe for ClickHouse protocols;
  • protect upstream ClickHouse connection capacity;
  • circuit-break or shed load when ClickHouse is overloaded/unavailable;
  • collect admission/security telemetry without logging bearer tokens;
  • optionally provide protocol translation required for OAuth-aware clients to reach older ClickHouse authentication interfaces.

The proxy MAY use the OAuth subject/issuer/username as dimensions for admission controls.

ClickHouse + altinity-oauth-helper

These remain responsible for database authentication/authorization semantics:

  • validate the token for ClickHouse login through ch-oauth-ldap;
  • bind the requested ClickHouse identity to the authenticated OAuth principal;
  • derive external group/role membership;
  • create/use the ephemeral external ClickHouse user representation;
  • map external groups to existing ClickHouse roles;
  • enforce role grants, row policies, settings profiles, and quotas;
  • propagate externally granted roles to remote ClickHouse nodes.

Explicit non-goals for the edge proxy

The proxy MUST NOT:

  • create/drop ClickHouse users;
  • execute GRANT or REVOKE to synchronize IdP memberships;
  • decide whether a principal may SELECT, INSERT, ALTER, etc. on a ClickHouse object;
  • convert IdP groups directly into SQL grants;
  • hold or use the ClickHouse interserver cluster secret for user impersonation;
  • assert arbitrary external_roles as a trusted ClickHouse cluster peer;
  • use EXECUTE AS or a service-account impersonation model as the normal OAuth path;
  • require ClickHouse administrative credentials;
  • replace ClickHouse RBAC with proxy-side ACLs.

A useful rule is:

The edge proxy authenticates for admission. ClickHouse plus altinity-oauth-helper authenticate for ClickHouse identity and ClickHouse authorizes through RBAC.

Double token validation

The same OAuth token should normally be validated independently at two boundaries:

client
  |
  | JWT
  v
edge proxy
  |
  | validation #1: should this client/session be admitted?
  v
ClickHouse
  |
  | LDAP bind(username, JWT)
  v
ch-oauth-ldap
  |
  | validation #2: does this token authenticate this CH identity,
  | and which external groups apply?
  v
ClickHouse RBAC

This duplication is intentional.

The edge proxy's successful validation MUST NOT be sufficient proof for ClickHouse authorization. ch-oauth-ldap should independently validate the credential rather than trusting proxy-generated identity/group headers or equivalent assertions.

The components may share the same validation libraries/configuration conventions, but not authentication state.

Security consequence

Compromising the edge proxy alone should not allow an attacker to invent arbitrary ClickHouse identities or roles. The attacker still needs credentials accepted by the ClickHouse authentication path.

Conversely, the edge proxy protects ch-oauth-ldap and ClickHouse from high-volume invalid traffic by rejecting obvious failures early.

HTTP behavior

For an OAuth-aware client that sends:

Authorization: Bearer <token>

the proxy may:

  1. validate the token for admission;
  2. establish the canonical/expected ClickHouse username from configured identity policy;
  3. apply principal/IP rate limits;
  4. translate the credential into a form Altinity Stable 24.8 can pass to its configured authentication backend;
  5. forward the request without making any database authorization decision.

The exact ClickHouse-facing credential envelope is an implementation detail and should be tested for token leakage in logs, query-visible HTTP headers, and diagnostics.

Native TCP behavior

For native TCP, the edge proxy may need limited ClickHouse protocol awareness around connection setup in order to:

  • obtain/validate the OAuth credential supplied by an OAuth-aware client;
  • establish the canonical username/credential presented to ClickHouse;
  • enforce handshake limits and admission policy;
  • track the credential expiry associated with the authenticated session.

After authentication, the preferred implementation should be as close to transparent relay as practical rather than parsing/authorizing every query.

A native OAuth-authenticated connection must have a deadline no later than the token/session credential expiration:

connection_deadline = min(configured_max_session_lifetime, credential_expiry)

When that deadline is reached, the proxy closes the client connection. The client obtains/refreshes a token and reconnects, causing ClickHouse/LDAP authentication and dynamic role lookup to happen again.

This bounds stale authorization for long-lived native connections without requiring the edge proxy to understand ClickHouse RBAC.

Authorization freshness

The edge proxy is not responsible for group/role revocation propagation.

For a JWT whose groups are embedded in the token, the normal security boundary remains the access-token lifetime. Group changes are reflected when the client authenticates with a newly issued token, subject to any stricter group source configured in ch-oauth-ldap.

The native session-lifetime rule ensures an old connection cannot outlive the credential that established it.

Trust boundaries

Edge proxy compromised

Potential impact:

  • traffic admission policy can be bypassed;
  • requests/connections can be dropped, altered, or observed according to deployment TLS topology;
  • DoS protections can be disabled.

Intended limitation:

  • the proxy alone cannot invent a ClickHouse user/role accepted by the LDAP OAuth authentication path because ClickHouse still causes ch-oauth-ldap to validate the credential independently.
OAuth helper compromised

Potential impact:

  • OAuth-managed ClickHouse identities/group mappings may be forged.

Mitigation boundary:

  • local ClickHouse administrative/break-glass users are kept outside the OAuth external directory;
  • actual SQL privileges remain constrained by the set of pre-existing ClickHouse roles.
IdP compromised

Potential impact:

  • OAuth identities and group claims can be forged by the IdP.

Mitigation boundary:

  • IdP groups select only allowlisted ClickHouse roles;
  • ClickHouse remains authoritative for the privileges those roles actually carry.

Deployment relationship

The components may be colocated in the same Kubernetes environment, but they have different logical positions:

untrusted/client network
        |
        v
edge proxy
        |
        v
ClickHouse
        |
        v
ch-oauth-ldap / altinity-oauth-helper

altinity-oauth-helper should preferably remain on a trusted/internal interface reachable by ClickHouse, not exposed as the public ClickHouse endpoint.

Repository boundary

Altinity/altinity-oauth-helper owns OAuth-to-ClickHouse authentication helpers such as:

cmd/ch-jwt-verify
cmd/ch-oauth-ldap
shared OAuth/JWT/identity/group code

The edge proxy should be implemented in a separate repository/component. CHGuard is a possible home if its architecture can support HTTP + native TCP proxying, OAuth admission, session-lifetime enforcement, and rate/connection controls without mixing in ClickHouse authorization. This ADR does not decide the concrete repository.

Alternatives considered

Put all OAuth logic and authorization in the front proxy

Rejected. It creates a second authorization engine, requires the proxy to stay synchronized with ClickHouse privileges, and makes proxy compromise equivalent to database authorization compromise.

Trust proxy-signed identity/group headers downstream

Rejected as the default model. It removes independent ClickHouse-side credential validation and makes the public-facing proxy the authentication/authorization trust root.

Use the ClickHouse interserver cluster secret from the proxy

Rejected. A public-facing/general-purpose proxy should not be elevated to a trusted ClickHouse cluster peer able to assert externally granted roles or impersonated users.

No front proxy

Potentially valid for tightly controlled deployments, because ch-oauth-ldap plus ClickHouse can provide authentication/authorization. Rejected as the product's general exposure architecture because it gives up centralized HTTP/TCP admission controls, token-expiry enforcement for long-lived TCP sessions, and DoS protection.

Consequences

Positive
  • clear separation between admission/security controls and database authorization;
  • ClickHouse RBAC remains the single authorization source of truth;
  • no ClickHouse admin credentials or cluster secret in the edge proxy;
  • invalid OAuth traffic can be rejected before consuming ClickHouse resources;
  • native session lifetime can be bounded by OAuth token lifetime;
  • front-proxy security features can evolve independently of identity/group mapping.
Negative / risks
  • tokens are normally validated twice;
  • two network components must share compatible issuer/audience/identity configuration;
  • native TCP protection requires careful ClickHouse protocol handling;
  • token forwarding/translation must avoid credential exposure in logs and diagnostics;
  • duplicate validation caches must not accidentally extend credential validity beyond token expiry.

Validation criteria

Before this architecture is considered implemented, integration/security tests should demonstrate:

  1. Invalid/expired/wrong-audience tokens are rejected by the edge proxy before a ClickHouse session is established.
  2. Bypassing or compromising the proxy's identity assertion is insufficient to authenticate a different ClickHouse user without a valid credential accepted by ch-oauth-ldap.
  3. HTTP requests can be rate-limited by principal and source without changing ClickHouse authorization semantics.
  4. Native connection count and connection-establishment rate can be bounded globally and per principal/source.
  5. Slow/partial handshakes do not hold unbounded proxy or ClickHouse resources.
  6. A native session is closed no later than the OAuth credential expiration / configured maximum lifetime.
  7. A reconnect with a refreshed token causes a new LDAP authentication and observes updated external groups/roles.
  8. Distributed ClickHouse authorization continues to be enforced by ClickHouse, not by proxy ACLs.
  9. Local administrative ClickHouse users remain outside the OAuth-helper trust path.
  10. OAuth bearer credentials are redacted from normal proxy, ClickHouse, and helper logs/metrics/errors.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the proposed ADR and the mentioned entry points, cmd/ch-jwt-verify, cmd/ch-oauth-ldap, and shared OAuth/JWT/identity/group code. Confirm that the repository boundary and responsibility split are documented consistently, then verify that the completed ADR clearly records the separation from any future edge-proxy implementation.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, kubernetes
Domain
authentication, backend-api-design, documentation, security
Issue type
Documentation
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.