anthropics / anthropics/claude-code

[BUG] Desktop SSH connections fail when ~/.ssh is an iCloud Drive symlink — spawned ssh/ssh-keygen can't read known_hosts or config

オープン
#88,835 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:desktop bug has repro platform:macos
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

### Preflight Checklist

- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code

### What's Wrong?

The desktop app's "Add SSH connection" always fails with `Host denied (verification failed)` and:

> "" is not in ~/.ssh/known_hosts yet, or its host key has changed. If this is your first connection to it, run ssh to this host once in a terminal and accept the host key, then test again. If you have connected before and the machine was not reinstalled, do not connect.

Command-line `ssh` to the same host from the same Mac succeeds, and the host key **is** in `~/.ssh/known_hosts` and current (verified by comparing fingerprints against `/etc/ssh/ssh_host_*_key.pub` read over an already-trusted session). Following the dialog's advice therefore changes nothing.

Entering an alias from `~/.ssh/config` in the SSH Host field fails earlier, at `getaddrinfo ENOTFOUND`, even though the field's helper text offers "a host from ~/.ssh/config".

## Root cause

On this machine `~/.ssh` is a symlink into iCloud Drive (`~/Library/Mobile Documents/com~apple~CloudDocs/...`), a TCC-protected location, so one keystore can be shared between two Macs.

The app shells out to `ssh -G` and `ssh-keygen -F`. Those children get the correct `HOME` and resolve to `/usr/bin/ssh` and `/usr/bin/ssh-keygen`, but they do not inherit Claude.app's `kTCCServiceFileProviderDomain` grant, so `~/.ssh` is effectively invisible to them. The app's own Node `fs` reads are unaffected, which is what makes this hard to diagnose.

## Evidence (from `~/Library/Logs/Claude/ssh.log`)

1. `identityFiles: 5` — real `ssh -G ` returns **1** identity file for this host. 5 is exactly what `ssh -F none -G ` returns, i.e. the user config was never read.
2. An alias defined in `~/.ssh/config` is logged as `Host is not in known_hosts` — the raw string was used as the hostname, so `ssh -G` output was discarded.
3. The rejection is always the "lookup returned nothing" branch (`host is not in known_hosts`), never the "presented key does not match" branch. No key comparison ever happens.
4. `keys: 1[ssh-rsa]` — an identity file read **in-process** from that same iCloud-backed directory (the agent holds 3 keys, so this is the file from the dialog, not the agent).

Host key type is not involved: adding every key type the server offers, in any order, changes nothing, because the file is never read.

Related but distinct: #72735 reports the same error string caused by a genuine host key rotation.

### What Should Happen?

Two things:

1. **The connection should succeed.** The host key is present and current in `~/.ssh/known_hosts`, and command-line `ssh` accepts it. Reading `known_hosts` and `ssh_config` in-process — the way identity files are already read — would remove the dependency on TCC grants that spawned OpenSSH binaries do not inherit.

2. **The error should distinguish "the known_hosts lookup could not run" from "the presented key does not match."** The current message asserts the host is unknown or re-keyed and warns "do not connect", when in fact nothing was compared. Only a genuine mismatch warrants the MITM warning; as written, it sends users off to re-record a host key that is already correct, which cannot help.

### Error Messages/Logs

```shell
~/Library/Logs/Claude/ssh.log (host/user redacted):

[info] [SSH2Connection] Resolved @ -> @:22 (identityFiles: 5)
[warn] [SSH2Connection] Host is not in known_hosts
[info] [SSH2Connection] Connecting to @:22 (agent: env:/var/run/com.apple.launchd.XXXXX/Listeners, keys: 1[ssh-rsa], proxy: false, keyboard: true)
[warn] [SSH2Connection] Rejecting host key for - host is not in known_hosts
[error] [SSH2Connection] Connection error: Host denied (verification failed)
[error] [RemoteServerController] Connection failed (166ms, trigger: send_message, attempt 1): Host denied (verification failed)
[warn] [RemoteServerController] Auto-reconnect aborted: terminal error class "hostkey" (Host denied (verification failed))

With a ~/.ssh/config alias in the SSH Host field, the alias is never resolved:

[warn] [SSH2Connection] Host is not in known_hosts

UI, when an alias is entered:

Couldn't find the host "". Check the spelling in SSH Host, or connect to your VPN if it is an internal name.
getaddrinfo ENOTFOUND

Same shell, same user, same HOME - the lookups the app relies on all succeed:

$ ssh-keygen -F
# Host found: line 51
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5...

$ ssh -G | grep -c '^identityfile'
1
$ ssh -F none -G | grep -c '^identityfile'
5 # <- the number the app logs

$ ssh
# alias resolves fine via ~/.ssh/config
```

### Steps to Reproduce

On macOS, with `~/.ssh` relocated into iCloud Drive (a common setup for sharing one keystore between two Macs):

1. Move `~/.ssh` into iCloud Drive and symlink it back:
```
mv ~/.ssh "$HOME/Library/Mobile Documents/com~apple~CloudDocs/ssh-store"
ln -s "$HOME/Library/Mobile Documents/com~apple~CloudDocs/ssh-store" ~/.ssh
```
2. Confirm OpenSSH is happy from a terminal - both of these must succeed:
```
ssh @ # connects
ssh-keygen -F # prints the recorded host key
```
3. In the desktop app, add an SSH connection: SSH Host `@`, port empty, Identity File set to a key in `~/.ssh`.
4. Click Test connection.

Observed: `Connection failed: Host key not trusted` / `Host denied (verification failed)`, with the dialog claiming the host is not in `~/.ssh/known_hosts` or has been re-keyed.

5. Open `~/Library/Logs/Claude/ssh.log`. It shows `Host is not in known_hosts` and `identityFiles: 5` - the latter being the count you get only when `~/.ssh/config` is not read (`ssh -F none -G`).

Also reproducible in step 3 by entering a `~/.ssh/config` alias instead of `@`: it fails at `getaddrinfo ENOTFOUND` because the alias is never resolved through the config.

A machine with a normal, local `~/.ssh` does not reproduce this.

### Claude Model

None

### Is this a regression?

No, this never worked

### Last Working Version

_No response_

### Claude Code Version

2.1.237 (Claude Code), in Claude desktop 1.34493.1

### Platform

Anthropic API

### Operating System

macOS

### Terminal/Shell

Terminal.app (macOS)

### Additional Information

**Environment**

- Claude desktop 1.34493.1 / Claude Code 2.1.237
- macOS 26.5.2, Apple Silicon
- OpenSSH from `/usr/bin` (both `ssh` and `ssh-keygen` resolve there on the login-shell PATH)

**Ruled out while diagnosing**

- Missing or stale host key entries. All host key types the server offers were added, verified against `/etc/ssh/ssh_host_*_key.pub` over an already-trusted session, and reordered; no effect. The file is never read.
- Malformed `known_hosts`. 63 lines, no CRs, no markers, no blank or short lines, only `ssh-ed25519` / `ecdsa-sha2-nistp256` / `ssh-rsa`.
- Wrong `HOME` in spawned children. `ps -Eww` on a process the app spawned shows `HOME=/Users/`.
- `ssh-keygen` not on PATH, or a slow/failing login-shell PATH probe. It resolves to `/usr/bin/ssh-keygen`, and `zsh -lc` returns in ~25 ms.
- TCC being denied to the app itself. Both `com.anthropic.claude-code` and `com.anthropic.claudefordesktop` hold `kTCCServiceFileProviderDomain` (allowed), and the app reads an identity file from the same directory in-process. Only the spawned OpenSSH binaries are affected.

**Workarounds**

None that keep the setup intact. Moving `config` and `known_hosts` back to real local files outside iCloud Drive would fix it, at the cost of the shared keystore. Granting Full Disk Access to `/usr/bin/ssh` and `/usr/bin/ssh-keygen` may also work, but that is a broad grant to system binaries and I did not test it.

Note: this reproduces on both Macs sharing the keystore, so the app cannot SSH out from either.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

Start with the desktop SSH connection path that spawns ssh -G and ssh-keygen -F, using ~/Library/Logs/Claude/ssh.log and the identityFiles: 5 and alias-resolution failures as evidence. Reproduce with a symlinked iCloud ~/.ssh and a local ~/.ssh; done means config and known_hosts are read reliably, aliases resolve, and lookup failures are distinguished from host-key mismatches.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
macos, node.js
領域
desktop, security
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
45/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。