Skip to content

fix(ssh): every tunnel strips the client certificate, so mutual TLS never works through one - #3198

Merged
datlechin merged 6 commits into
mainfrom
fix/tunnel-transport-gaps
Sep 30, 2026
Merged

datlechin merged 6 commits into
mainfrom
fix/tunnel-transport-gaps

Conversation

@datlechin

Copy link
Copy Markdown
Member

Summary

  • A server that enforces mutual TLS refused every connect through an SSH, Cloudflare, SOCKS, Tunnel Command or Cloud SQL tunnel. The shared TCP-forward rewrite cleared the client certificate and key along with the CA path, although only hostname verification depends on the host dialed.
  • A SOCKS proxy dialed the hidden Host field instead of the first host-list entry for Redis Cluster, Redis Sentinel and Kafka. The SOCKS path read host and port directly, where SSH and Tunnel Command already read tunnelForwardEndpoint.
  • A Password-auth SSH tunnel with no saved password reported "SSH password rejected". The nil-password guard threw the same reason as a server refusal, before any password reached the server; it now throws a new passwordMissing reason with its own message.
  • The cloudflared and cloud-sql-proxy not-found labels, the SSH config token errors, the remote command failure and the dump tool install hints showed literal backticks. Each string was written in Markdown but reaches a surface that renders plain text (Label(String), Text(verbatim:), result sheets). The unlocalized "SSH username not set" message also became a localized SSHTunnelError.usernameMissing case.
  • The Tunnel Command preview showed port 0 when Port was blank, and the hidden Host for a host-list connection. The preview read the raw form fields, while the connect resolves the default port and the first host-list entry; it now builds the connection the way Test Connection does and reads tunnelForwardEndpoint.
  • The SSH tab's host-list warning said "Replica set failover is not available" for Redis and Kafka, hid itself for a single Sentinel node or a single Cluster seed, and implied Sentinel works through a tunnel. The caption was MongoDB's sentence gated on a comma for every engine; it is now worded per host-list field. The Redis Cluster caption describes the behavior after fix(plugin-redis): Cluster over SSH fails with wrong advice and typed SCAN drops its cursor #3190, so merge this after it.

Tests

  • DatabaseManagerTunnelTests: tcpForwardKeepsClientIdentity and tcpForwardRelaxesVerificationAndKeepsClientIdentity fail without the fix; socketForwardClearsClientIdentity guards the Unix-socket branch.
  • SOCKSProxyTargetTests: hostListConnectionDialsFirstListedNode reads the CONNECT request a fake SOCKS5 server receives and fails without the fix (it dialed stale.example:6379); singleHostConnectionDialsHostAndPort guards the single-host case.
  • BuildAuthenticatorTests: passwordWithoutCredentialThrows now expects passwordMissing (a deliberate spec change) and missingPasswordIsNotReportedAsRejected is new; neither compiles without the fix. AuthFailureReasonTests covers the new case's message through allCases.
  • CloudflareModelTests.binaryNotFoundIsPlainText, CloudSQLProxyModelTests.binaryNotFoundIsPlainText, SSHConfigTokensTests.expansionFailuresArePlainText, SSHTunnelErrorTests.remoteCommandFailedIsPlainText, NativeDumpRegistryTests.installHintsArePlainText and PostgreSQLDumpToolCompatibilityTests.refusalsArePlainText fail without the fix. CloudflareTunnelPaneViewModelTests and CloudSQLProxyPaneViewModelTests (binaryNotFoundMessageIsPlainText) and SSHTunnelErrorTests.usernameMissingDescription do not compile without it.
  • TunnelCommandPreviewTests: clearedPortPreviewsDefaultPort and hostListPreviewsFirstListedNode fail without the fix ({port}:0 and localhost:0).
  • NetworkPaneTunnelHostListCaptionTests: the Sentinel (two nodes and one node), Cluster, single Cluster seed and Kafka cases fail without the fix; the MongoDB and no-caption cases guard the existing behavior.
  • The 29 covering suites pass: 298 tests in the result bundle, 0 failed. swiftlint lint --strict over the 32 changed Swift files reports 0 violations.

Docs

The docs rewrite branch covers the user-facing text these changes affect: the tunneled TLS paragraph in docs/connections/ssl.mdx, the SOCKS host-list note, the new missing-password message, the Tunnel Command preview step and the Redis tunnel host-list wording.

@datlechin
datlechin merged commit 8f8e270 into main Sep 30, 2026
13 checks passed
@datlechin
datlechin deleted the fix/tunnel-transport-gaps branch October 2, 2026 04:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant