feat(local): support accept-invalid-certificate saved connections#53
Merged
Conversation
clickhouse-client connections carrying <accept-invalid-certificate>1</accept-invalid-certificate> now surface a cert-trust step in the login picker. A browser can't bypass TLS validation from fetch(), so the SPA can't honour the flag on its own — instead it walks the user through trusting the cert once. - build/local.py: read accept-invalid-certificate; emit `insecure` on basic and oauth host descriptors in config.json. - src/net/oauth-config.js: carry `insecure` through normalizeHost (it was silently dropped — the picker never saw the flag). - src/ui/login.js: insecure host → show a cert-trust panel (open-cluster link + guidance that the post-handshake redirect to an auth gateway/login page is expected and unrelated). For oauth, hold the SSO redirect behind a Continue button so the cert is trusted before any post-login query hits the cluster. - styles.css: panel styling. README + docstring: document the flow. - tests: login picker (basic/oauth/clear) + oauth-config insecure pass-through. Verified end-to-end via `python3 build/local.py` against the `audit` support connection (support-a.tenant-a.dev.altinity.cloud): the cluster root 302-redirects to acm.altinity.cloud/login after the TLS handshake, which is the "wrong redirect" the panel now explains. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUc6UU9qs5J3u7qoy5YoNN
A saved connection without an explicit http_port built a portless URL, so secure hosts resolved to :443. On managed Altinity Cloud endpoints :443 is an auth gateway (a browser GET 302s to acm.altinity.cloud/login) — the SPA never reached ClickHouse, and the :443 cert is valid so "accept the certificate" trusted nothing relevant. The real HTTPS interface (with the self-signed cert) is :8443. Default secure→8443, plain→8123 (ClickHouse's own HTTP-interface defaults), mirroring the SPA's resolveTarget for a bare host. Verified all configured clusters answer /ping on 8443; support-a only works there. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUc6UU9qs5J3u7qoy5YoNN
- login.js: pickOAuth now guards `if (busy) return` (it became reachable from the cert panel's Continue button, not just the picker's onchange, so a fast double-click could race two OAuth flows and corrupt the PKCE verifier/state). The Continue button also disables itself on click, mirroring doSso. (#1) - build/local.py: don't append a default port when <hostname> already carries one (host:port → was becoming host:port:8443). (#2) - build/local.py: document that an explicit <http_port> overrides the 8443/8123 default (e.g. 443 for a proxy that fronts the HTTP interface with no gateway). (#3) - test: cover the Continue double-submit guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUc6UU9qs5J3u7qoy5YoNN
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.
What
npm run localnow supportsclickhouse-clientconnections carrying<accept-invalid-certificate>1</accept-invalid-certificate>(self-signed /wrong-host TLS certs, common on dev tenants).
A browser can't bypass TLS validation from
fetch(), so the SPA can't honourthe flag on its own. Instead the login picker flags such a connection and walks
the user through trusting the cert once, then connects normally.
Changes
build/local.py— readaccept-invalid-certificate→ emitinsecureonbasic & oauth host descriptors in
config.json.src/net/oauth-config.js— carryinsecurethroughnormalizeHost(itwas being silently dropped, so the picker never saw the flag).
src/ui/login.js— insecure host → cert-trust panel (open-cluster link +guidance that the post-handshake redirect to an auth gateway/login page is
expected). For oauth, the SSO redirect is held behind a Continue button so
the cert is trusted before any post-login query hits the cluster.
build/local.py(port fix) — default to ClickHouse's HTTP-interface ports8443 (TLS) / 8123 (plain), not 443/80. Managed Altinity Cloud endpoints
park an auth gateway on 443 (a browser GET 302s to
acm.altinity.cloud/login),so 443 never reached ClickHouse. Mirrors the SPA's
resolveTarget. An explicit<http_port>still overrides; a port already embedded in<hostname>is keptas-is.
insecurepass-through + the Continue double-submit guard.
Testing
npm test— 952 passing, per-file coverage gate green.python3 build/local.pyagainst a realaccept-invalid-certificatecluster (agent Chrome): cert panel renders, linkopens
:8443(the direct HTTPS interface), Connect succeeds after the cert istrusted. Probing confirmed
:443is the ACM gateway (302 → login) and:8443is the real ClickHouse interface.
Review
Ran an internal review (medium); fixed the findings it surfaced: a
pickOAuthre-entrancy gap (Continue button double-submit racing the PKCE state), the
embedded-port double-append, and documented the
http_portoverride.🤖 Generated with Claude Code
https://claude.ai/code/session_01MUc6UU9qs5J3u7qoy5YoNN