Motivation
使用頻度の高いGit BashからのsshとVSCode Remote Developmentでssh_configの設定を共通化したい。
最初にまとめ
- Git BashとWindows標準のsshで ~/.ssh/config でのパスの書き方が違う。
- VSCodeのRemote Developmentでは標準でWindowsのsshが使われる。
- ~/.ssh/config を以下のように書くと、Git BashとWindows標準どちらのsshからもtargetに接続できる。
Host target
Hostname target.example.com
User ******
IdentityFile ~/.ssh/id_rsa
ProxyCommand C:/PROGRA~1/Git/mingw64/bin/connect.exe -S socks.example.com:1080 %h %p
connect.exe のパスにスペースが入らないように、 Program Files
の短いファイル名 PROGRA~1
を使うこと、ディレクトリ区切りに\
ではなく/
を使うことがポイント
構成:
環境
- Windows 10 64bit (1909)
- Git for Windows 64bit (2.22.0)
- Visual Studio Code 1.60.1
経緯と詳細
- Windows で SOKCKサーバー経由のSSH接続を行うにはGit for Windows付属の connect.exe を使う。
- Git for Windows ならみんなもうインストール済み
- ssh.exe のパスは
C:\Program Files\Git\usr\bin\ssh.exe
- connect.exe のパスは
C:\Program Files\Git\mingw64\bin\connect.exe
ここはPATHの通っていないディレクトリなので、connect.exeを使うにはフルパスで指定が必要
- 最近のWindowsにはssh.exeが付属してきており、そのパスは
C:\Windows\System32\OpenSSH\ssh.exe
- VSCode の Remote Development においては標準でこちらのssh.exeが使われる
Git BashからProxy経由でsshのssh_config
Host target
Hostname target.example.com
User ******
IdentityFile ~/.ssh/id_rsa
ProxyCommand "C:\Program Files\Git\mingw64\bin\connect.exe" -S socks.example.com:1080 %h %p
C:\Program Files\Git\mingw64\bin\connect.exe
を一つの実行ファイルと認識させるためにこのパスをダブルクォーテーションで囲んでいる。
PowerShellからProxy経由でsshのssh_config
Host target
Hostname target.example.com
User ******
IdentityFile ~/.ssh/id_rsa
ProxyCommand C:\Program Files\Git\mingw64\bin\connect.exe -S socks.example.com:1080 %h %p
PowerShellのCLI、cmd.exe (コマンドプロンプト)、 Visual Studio Code などWindowsネイティブでのパスの指定方法はこちら。
C:\Program Files\
のように途中にスペースがあってもなぜか C:\Program Files\Git\mingw64\bin\connect.exe
までが最初の実行ファイルの指定だとわかり、connect.exe を起動して残りをconnect.exeへのパラメータとして扱ってくれる。
Git BashとPowerShellからの~/.ssh/config設定を共通にしたい
違いは ProxyCommand の connect.exe のパスの書き方だけなのでなんとか共通化したい。
ProxyCommand C:\Program Files\Git\mingw64\bin\connect.exe
でGit Bashからsshすると、エラー
$ ssh target
/usr/bin/bash: line 0: exec: C:Program: not found
kex_exchange_identification: Connection closed by remote host
\
がエスケープ文字と見なされて消えているのと、 Program Files
のスペースで区切られてしまい、 Program という実行ファイルを探してしまっている。
エスケープ
ProxyCommand C:\\Program\ Files\\Git\\mingw64\\bin\\connect.exe
でGit Bashからsshすると、つながる。
$ ssh target
Last login: Sun Sep 19 09:37:09 2021 from xxx.xxx.xxx.xxx
この状態でPowerShellからsshするとエラー。
PS C:\Users\umorigu> ssh target
CreateProcessW failed error:2
posix_spawn: No such file or directory
逆にGit Bashで繋がっていた ProxyCommand "C:\Program Files\Git\mingw64\bin\connect.exe"
でPowerShellからsshすると、これもエラー。
PS C:\Users\umorigu> ssh target
CreateProcessW failed error:87
posix_spawn: Unknown error
シングルクォートで囲んでみても ProxyCommand 'C:\Program Files\Git\mingw64\bin\connect.exe'
メッセージが変わるが引き続きエラーになる。
PS C:\Users\umorigu> ssh target
CreateProcessW failed error:2
posix_spawn: No such file or directory
ssh_exchange_identification: Connection closed by remote host
Windows native のsshは、パス前後に引用符 "
, '
が付いたり、パスの途中に \
が入ったりするともうつながらなくなってしまう。
ただし複数の \
は一つの \
と同一視される。(Windows一般ルール)
引用符を使わずにパス途中のスペースに対応する方法は? と考えて、短いファイル名を思い出した。
> dir /x C:\
...
2021/09/07 14:45 <DIR> PROGRA~1 Program Files
2021/06/29 10:49 <DIR> PROGRA~2 Program Files (x86)
...
ProxyCommand C:\PROGRA~1\Git\mingw64\bin\connect.exe
を試すと
PowerShellではつながる、Git Bashでエラー
$ ssh target
/usr/bin/bash: line 0: exec: C:PROGRA~1Gitmingw64binconnect.exe: not found
kex_exchange_identification: Connection closed by remote host
\
がエスケープ文字と解釈されて消えてしまっている。
\
をエスケープするために\\
に変えた ProxyCommand C:\\PROGRA~1\\Git\\mingw64\\bin\\connect.exe
を試すと
PowerShellではつながる。Git Bashでもつながる!
次にバックスラッシュ2つを/
に変えた ProxyCommand C:/PROGRA~1/Git/mingw64/bin/connect.exe
を試すと、これも
PoewrShellではつながる。Git Bashでもつながる!
以上により、 ProxyCommand の記述として正しいのは次の2パターン。
ProxyCommand C:\\PROGRA~1\\Git\\mingw64\\bin\\connect.exe -S socks.example.com:1080 %h %p
ProxyCommand C:/PROGRA~1/Git/mingw64/bin/connect.exe -S socks.example.com:1080 %h %p
どちらでも動作するがLinuxでのssh_configや IdentityFile ~/.ssh/id_rsa
のような典型的な記述を意識して今回は /
を採用する。
確定したパスを使ってconnect.exeのバージョンを確認する
C:/PROGRA~1/Git/mingw64/bin/connect.exe -V
を実行すると connect.exe のバージョンを確認できる。 (Git Bash, PowerShell, コマンドプロンプトすべて同じでOK)
$ C:/PROGRA~1/Git/mingw64/bin/connect.exe -V
connect --- simple relaying command via proxy.
Version 1.105
PS C:\Users\umorigu> C:/PROGRA~1/Git/mingw64/bin/connect.exe -V
connect --- simple relaying command via proxy.
Version 1.105
得られた知見
- Git BashとWindows標準のsshで実行バイナリが異なり、 ~/.ssh/config でのパスの書き方が違う。
- VSCodeのRemote Developmentでは標準でWindowsのsshが使われる。
- Windows標準sshは、パスを
"
や'
で囲むと動かなくなる - Git Bash付属sshはShell的な解釈を行い、
"
や'
で囲まれていないパスの途中にスペースがあると動かない。 - 短いファイル名 (
C:\Program Files
に対してC:\PROGRA~1
) を使うことでスペースも引用符もなしにGit BashとWindows標準のsshに共通のパス設定(実行ファイル指定)を行うことができる。 - パス区切り文字列は
\\
とするか/
とするか、どちらでも有効。
別解
- Git for Windows を Program Files でなくスペースの入らないディレクトリにインストールする。
- これでもOK。ただ、そんなことをする人はほとんどいない。
- VSCode の Remote.SSH:Path に
C:\Program Files\Git\usr\bin\ssh.exe
を設定する。- これでもOK。ただ、できるだけVSCode側のデフォルト設定は変えずにおきたい。
応用
踏み台サーバー経由で別のサーバーにログインする
踏み台サーバー(bastion)を経由してtarget-internalにログインしたい場合の設定は以下のようになる。
構成:
Host bastion
Hostname bastion.example.com
User ******
IdentityFile ~/.ssh/id_rsa
ProxyCommand C:/PROGRA~1/Git/mingw64/bin/connect.exe -S socks.example.com:1080 %h %p
Host target-internal
Hostname target-internal.example.com
User ******
IdentityFile ~/.ssh/id_rsa
ProxyCommand ssh.exe -CW %h:%p bastion
こうしておくと
$ ssh target-internal
でSSHログインできる。
HTTP Proxy経由でSSH接続
HTTP Proxyの場合は、 connect.exe に指定するオプションを -S
(SOCKS) から -H
に変える。
Host target
Hostname target.example.com
User ******
IdentityFile ~/.ssh/id_rsa
ProxyCommand C:/PROGRA~1/Git/mingw64/bin/connect.exe -H proxy.example.com:10080 %h %p
感想
2021年になってショートファイル名 PROGRA~1 を使うことになるとは思わなかった。