Claude DesktopのSSHでMatch hostが端末と食い違う原因と対処
端末のsshでは効かないMatch hostのブロックが、Claude DesktopのSSHでだけ効くことがあります。HostNameの渡し方の違い、ssh -Gでの比較、!originalhostの足し方、転送のオフを扱います。
端末で ssh devbox と打つと繋がるのに、Claude DesktopのSSHセッションでは別の設定が混ざる。~/.ssh/config に Match host のブロックがあるなら、原因はたいていここです。Desktopは HostName の値をコマンドラインで ssh に渡すため、Match host の判定相手が端末のときと変わります。
直し方は Match 行に !originalhost <Host名> を足す1行です。先に、食い違いを ssh -G で目に見える形にして確かめます。
なぜ端末とDesktopで判定が変わるのか
端末で ssh devbox を実行すると、ssh は設定ファイルを上から読みます。HostName 行より上にある Match host のブロックは、まだ HostName が反映されていない devbox と比べられます。
Desktopは違います。エントリの HostName の値をコマンドラインで ssh に渡すので、同じブロックが devbox.internal.example.com のような実ホスト名と比べられます。
この挙動が当てはまるのは、手元のPCがmacOSかLinuxの場合です。Desktopは手元のOpenSSHクライアントを実行して ~/.ssh/config を読むので、判定は ssh の仕様そのものに従います。必要なのはClaude Desktop v1.37937.0以降と、OpenSSH 7.6以降です。クライアントの版は手元の端末で ssh -V を実行すると分かります。
もう一つ、接続先のエントリに RemoteCommand を書いていても、Desktopはそれを実行しません。端末では効いていた RemoteCommand がDesktopで効かないときは、Match host と別の原因です。
ssh_configのmanページでも、host は Hostname などで置き換えた後の接続先名と照合し、originalhost はコマンドラインで指定したままの名前と照合すると書かれています。この差が、端末では素通りするブロックをDesktopだけで有効にします。
| 条件 | 端末の ssh devbox | Desktopの接続 |
|---|---|---|
Match host の比較相手 | 端末の ssh devboxdevbox | Desktopの接続HostName の値 |
Match host *.internal.example.com | 端末の ssh devbox一致しない | Desktopの接続一致する |
Match originalhost の比較相手 | 端末の ssh devboxdevbox | Desktopの接続devbox |
たとえば次のような設定だと、ProxyCommand を持つブロックがDesktopにだけ適用されます。
Match host *.internal.example.com
ProxyCommand ssh bastion.example.com -W %h:%p
Host devbox
HostName devbox.internal.example.com
User aliceこの例は、公式の説明に沿って組んだ説明用の設定です。端末では devbox が *.internal.example.com に当たらないので、ProxyCommand は使われません。
ssh -Gで食い違いを確かめる
疑いを確かめるには、Desktopと同じ形で ssh -G を実行して、端末の通常の結果と並べます。どちらも端末で実行します。
ssh -G devbox
ssh -G -o HostName=devbox.internal.example.com -l alice devbox2本目の HostName と User には、エントリに書いた値を入れます。迷うときは1本目の出力にある hostname と user の行の値をそのまま使えます。値を入れ間違えると、Desktopと違う条件で比べてしまうので注意してください。1本目と2本目で出力を見比べ、proxycommand のような行が2本目にしか出ないなら、そのブロックはDesktopでだけ効いています。
差分を取りたいときは、シェルのプロセス置換が使えます。
diff <(ssh -G devbox) \
<(ssh -G -o HostName=devbox.internal.example.com -l alice devbox)Claude Codeに調べさせる場合は、この2コマンドを実行させて差分の行だけを報告させると速く済みます。たとえば次のように頼みます。
~/.ssh/config の devbox エントリについて、端末の ssh -G devbox と
HostName・User を -o / -l で渡した ssh -G の出力を比べてください。
2本目にだけ出る行を、どの Match ブロック由来かまで挙げてください。差分の行が proxycommand なら接続経路が、forwardagent なら転送設定が、別の Match から混ざっています。
Match行に!originalhostを足して揃える
Desktopにもそのブロックを飛ばさせるには、Match 行に !originalhost <Host名> を足します。
Match host *.internal.example.com !originalhost devboxoriginalhost はコマンドラインで指定した名前、つまり devbox と比べられます。端末でもDesktopでも devbox なので、否定条件が両方で同じに働き、ブロックが外れます。devbox 以外の宛先には、これまでどおり *.internal.example.com のブロックが効きます。
足したら、2本目のコマンドをもう一度実行します。ブロックの設定が出力から消えていれば揃っています。
Desktopの接続では転送設定がオフになる
食い違いの調べ方は、転送設定にも使えます。Desktopはリモートマシンへの接続で、次の6つを、SSH設定で有効にしていてもオフにします。
ForwardAgentForwardX11LocalForwardRemoteForwardDynamicForwardTunnel
端末ペインや ! コマンドでは、転送が効くことも効かないこともあります。Desktopがそこで別の ssh を起動する場合があり、その ssh は設定に従うためです。
だから「端末のsshではエージェント転送が効いていたのに、Desktopのセッションでは git push が通らない」といった差が出ます。対処は転送に頼らない形に寄せることです。
- リモートのポートを開きたいときは、自分で
ssh -N -L 3000:localhost:3000 devboxのようにポートを転送する - 自分の鍵をリモートで使いたいときは、
git push用などの認証情報をリモートマシン側に用意する
ポート転送のコマンドは、接続にSSH portやSSH keyを入力していれば -p、-i で同じ値を足します。open failed: administratively prohibited と出たら、サーバー側の sshd_config で AllowTcpForwarding が許可されているかを管理者に確認してもらいます。
端末ペインや!コマンドで転送を効かせないために
前の節のとおりDesktop本体の接続は転送をオフにしますが、端末ペインや ! コマンドでDesktopが別の ssh を起動したときは、その ssh が設定に従って転送を有効にします。そこで転送を効かせたくない場合も、2本目の ssh -G が役に立ちます。端末の1本目では Match に隠れて見えない forwardagent や localforward が、2本目にだけ出ることがあるためです。
1本目と2本目の両方の出力で、見る行は次の2種類です。
forwardagentの値がno以外の行localforward、remoteforward、dynamicforwardで始まる行
見つかったら、その設定を ~/.ssh/config から外します。ProxyCommand が中で ssh を呼んで別の踏み台に入る構成なら、その踏み台のエントリも同じ手順で調べます。
踏み台はエントリに直接書く方法もある
Match host で踏み台を足す書き方をやめて、エントリに ProxyJump を直接書けば、判定の食い違いは起きません。Desktopの接続でも、エントリの HostName、User、IdentityFile、CertificateFile、ProxyJump、ProxyCommand が使われます。
Host devbox
HostName devbox.internal.example.com
User alice
ProxyJump bastion.example.comSSH hostの欄には devbox と入れます。SSH portの欄を埋めると、エントリの Port より入力値が優先されます。SSH keyの欄に選んだ鍵は、エントリの IdentityFile やSSHエージェントの鍵に加えて試されます。どちらも空のままで構いません。
接続を追加する前に、手元の端末で ssh devbox が通ることを確かめておくと、Desktop側の失敗とエントリ側の失敗を切り分けられます。
端末と差が出るほかの設定
Match host のほかにも、端末とDesktopで同じ ~/.ssh/config が違う結果になる場所があります。切り分けの手は同じで、端末で実際に走らせて差を見ます。
端末との差が出やすい3か所
Match execやProxyCommandの環境変数
自分の環境変数を
ProxyCommandやMatch execのコマンドで使うなら、~/.zshrcなどのシェル起動ファイルでexportしてからDesktopを終了し、開き直します。Desktopは起動時にログインシェルがexportした値を読み、終了するまで保ちます。Claude Desktop v2.37337.0以降と、-0を受け付けるenvコマンドが必要です。AWSの一時認証情報
シェルが
AWS_SESSION_TOKEN、AWS_SECURITY_TOKEN、AWS_CREDENTIAL_EXPIRATIONをexportしていると、DesktopのsshとProxyCommandにはシェルのAWS認証情報、AWS_REGION、AWS_PROFILEが渡りません。AWS CLIが~/.aws/configのプロファイルを読めるようにし、ProxyCommandに--profileを足します。ConnectTimeoutの既定値
設定に
ConnectTimeoutが無いホストには、Desktopが30秒を与えます。値を足すときは、最初に見つかった値が使われるので、Host *より上のエントリに書きます。確認はssh -G devboxのconnecttimeoutの行です。値を変えたら、Desktopを終了して開き直します。リモートマシン自体がスマホでの承認を待つ場合は、
ConnectTimeoutをいくつにしても45秒以内に承認する必要があります。DesktopがServerAliveIntervalとServerAliveCountMaxを自分で設定しているため、この上限は引き上げられません。待っているのが踏み台やProxyCommandなら、リモートマシンのエントリのConnectTimeoutを延ばせば間に合います。
どれも「端末のsshでは通るのに」という症状から入るので、ssh -G の出力を見る癖があると原因に早く届きます。
接続先の許可リストを管理者が運用している環境では、sshHostAllowlist が見るのは ssh -G で解決した後の HostName です。別名や ProxyCommand のエントリも、解決後の HostName が許可されていれば通ります。許可リスト側の設計はsshHostAllowlistの記事、接続自体を配る方法はsshConfigsの記事にまとめています。
接続の後でworktreeの置き場所がずれる場合は、設定の優先順位が別の話になります。Worktreeの置き場所の記事を参照してください。
RemoteCommandでコンテナに入る構成はDesktopでは動かない
端末では RemoteCommand でコンテナに入る構成にしていると、Desktopのセッションは期待した場所で動きません。Desktopは接続先のエントリの RemoteCommand を実行しないので、セッションはコンテナではなく、そのエントリがログインした先のマシンで走ります。
SSHポートを公開していないコンテナにDesktopから入りたいときは、RemoteCommand ではなく ProxyCommand でコンテナまで届かせます。この ProxyCommand を足すエントリは、Host * のエントリを持っているなら、その直前に置きます。ssh は最初に見つかった値を使うので、位置が ConnectTimeout などの効き方を左右します。
直らないときに見る場所
!originalhost を足しても2本目の出力にブロックの設定が残るなら、別の Match ブロックが同じ設定を出していないかを ssh -G の出力の該当行から探します。ProxyCommand の先で別の ssh が呼ばれる構成では、その踏み台のエントリも同じ手順で調べます。接続先のエントリの RemoteCommand が効かないのは、Desktopが実行しない仕様です。ConnectTimeoutを変えた場合は、Desktopを終了して開き直してから試します。
まとめ
Match host を使うエントリが残るなら、originalhost を基準にした書き方へ揃えておく方が、端末とDesktopのどちらでも結果を予測しやすくなります。HostName の置き換え後の名前で判定するブロックは、Desktopだけで効く側に回りやすいためです。
転送の6設定がDesktopの接続でオフになることも、「端末では動くのにDesktopでは動かない」ときの切り分けの出発点になります。