SSH sessions ignore `GSSAPIAuthentication` from `~/.ssh/config`, then prompt for a password the server never offered

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 14, 2026

Environment

| | |
|---|---|
| Claude Desktop | 1.30096.1 |
| OS | macOS 26.5, arm64 |
| Local OpenSSH | OpenSSH_10.2p1, LibreSSL 3.3.6 |
| Remote sshd | OpenSSH_9.2, OpenSSL 3.0.20 (Linux) |
| Feature | Code tab → SSH session |

Summary

Desktop's SSH sessions parse ~/.ssh/config but silently ignore the GSSAPIAuthentication yes directive in it, then fall back to prompting for a password that the server never advertised as acceptable. The connection cannot succeed by construction, and the reported error names none of this.

The same host connects with zero interaction from the system ssh client, on the same machine, with the same config file.

Steps to reproduce

  1. Remote host with a Kerberos-only sshd — the standard setup for corporate dev machines:

``
PubkeyAuthentication no
PasswordAuthentication no
GSSAPIAuthentication yes
``

  1. ~/.ssh/config on the client:

``
Host dev
HostName dev.example.com
User myuser
Host *
GSSAPIAuthentication yes
``

  1. Run kinit; confirm a valid TGT with klist.
  2. Confirm the system client needs no interaction at all:

``
$ ssh -o BatchMode=yes dev 'echo OK'
OK
``

  1. In Desktop, add an SSH connection to the same host and start a session.

Expected: connects, same as the system client.

Actual: fails after ~93s, and the app prompts for a password. Any password fails — the server does not offer password or keyboard-interactive either.

Evidence

From ~/Library/Logs/Claude/ssh.log (host, user and socket ID redacted):

[RemoteServerController] Ensuring server is ready on myuser@dev.example.com (trigger: warm_up)
[SSH2Connection] Resolved myuser@dev.example.com -> myuser@dev.example.com:22 (identityFiles: 5)
[SSH2Connection] Connecting to myuser@dev.example.com:22 (agent: env:/var/run/com.apple.launchd.XXXX/Listeners, keys: 3[ssh-rsa,ecdsa-sha2-nistp256,ssh-ed25519], proxy: false, keyboard: true)
[SSH2Connection] ssh2: Outbound: Sending USERAUTH_REQUEST (none)
[SSH2Connection] ssh2: Inbound: Received USERAUTH_FAILURE (gssapi-with-mic)
[SSH2Connection] ssh2: Client: none auth failed
[SSH2Connection] ssh2: Outbound: Sending USERAUTH_REQUEST (publickey -- check)
[SSH2Connection] ssh2: Inbound: Received USERAUTH_FAILURE (gssapi-with-mic)
[SSH2Connection] ssh2: Client: publickey auth failed
[SSH2Connection] ssh2: Client: agent auth failed
[error] [SSH2Connection] Connection error: All configured authentication methods failed
[SSH2Connection] Auth failed with ~/.ssh/id_rsa, retrying with ~/.ssh/id_ecdsa
... (identical handshake and failure repeated for every identity file)
[error] [RemoteServerController] Connection failed (93474ms, trigger: warm_up): All configured authentication methods failed

The server's USERAUTH_FAILURE payload lists exactly one acceptable method: gssapi-with-mic. The client attempts only none, publickey and agent.

Four distinct defects here

1. ~/.ssh/config is parsed, but GSSAPIAuthentication is silently dropped.
identityFiles: 5 in the log proves the config file was read and honored for IdentityFile. The docs state SSH Host accepts "a host defined in ~/.ssh/config". Honoring some directives from that file while silently ignoring others is the core bug: the user has correctly configured the connection and gets no indication that part of their configuration was discarded. At minimum, ignoring a directive should be visible.

2. The app prompts for a password the server never offered.
keyboard: true in the log, and a password prompt in the UI — but the server's advertised method list contains neither password nor keyboard-interactive. The prompt is unreachable by construction and sends users hunting for a credential problem that does not exist. The advertised list is right there in the handshake.

3. The error text hides the diagnosis.
All configured authentication methods failed omits the one fact that explains everything. It should read: "Server accepts only: gssapi-with-mic. Claude Desktop supports: publickey, agent, password." That single line would have saved hours of debugging here.

4. Pointless retry amplification.
The server never advertised publickey, yet the client reruns the entire handshake once per identity file — 5 full round trips, 93 seconds, all provably futile after the first USERAUTH_FAILURE. Once the advertised method list is known, stop.

Defects 1–4 are all fixable without adding GSSAPI support. That said, the underlying capability gap — the ssh2 Node library has no GSSAPI implementation — is what makes this host unreachable at all, so the two are worth considering together.

Suggested fix

Preferred: use the system ssh binary for session transport, multiplexed via ControlMaster/ControlPath.

This resolves all four defects structurally and inherits every authentication method and config directive OpenSSH supports, permanently:

  • GSSAPI/Kerberos (this issue)
  • SSH certificates, PKCS#11 / smartcards, FIDO keys (sk-ssh-ed25519)
  • ProxyJump / ProxyCommand, Match blocks, Include directives, and every other ssh_config feature a reimplementation will otherwise keep chasing

ControlMaster auto over a persistent control socket provides cheap multiplexed channels, which is presumably what ssh2 was chosen for. Note the app already shells out to the system binary elsewhere — probing and git fetch use ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new ... — so "the command line works" and "the app connects" are currently two independent code paths with different capabilities. Consolidating them removes that divergence.

Alternative: add GSSAPI to the ssh2 path via the kerberos npm package (GSS-API on macOS/Linux, SSPI on Windows). This closes one gap and leaves the reimplementation treadmill in place.

Independently of either: fix defects 2, 3 and 4. They are cheap, they are correct regardless of which transport is chosen, and #3 alone turns this from a multi-hour investigation into a one-line diagnosis.

Why this matters

Kerberos-only sshd is the default posture for managed development machines at most large companies — tickets are revocable and auditable, static public keys are not. For those users, Desktop's SSH feature is currently unusable, and the failure mode gives them no way to find out why.

Current workaround, for anyone finding this issue

Run the Claude Code CLI inside a normal ssh session on the remote host; that path uses the system client and works. Tunneling around the problem is also possible — a user-space sshd on the remote host bound to 127.0.0.1, forwarded over a GSSAPI-authenticated ssh -N -L, with Desktop pointed at 127.0.0.1 — but it means a resident process on the dev machine, a resident local tunnel, a reconnect on every Kerberos ticket expiry, and on a managed corporate machine it may violate the very policy that made sshd Kerberos-only. It is a workaround, not a fix.

View original on GitHub ↗