どうしてこうなった。
何の話?
WindowsでのSSH-AGENTとSSHの話です。
この記事での用語: SSHとssh
, SSH-AGENTとssh-agent
この記事では、SSH-AGENTと書いたときにはカテゴリとしてのSSHエージェントを意味します。
ssh-agent
と書いたときには、実行プログラムとしてのssh-agent
コマンドを意味します。
同様に、SSHと書いたときにはカテゴリとしてのSSHクライアントを意味します。
ssh
と書いたときには、実行プログラムとしてのsshコマンドを意味します。
SSH-AGENTって?
SSH-AGENTは、秘密鍵での署名を代行1してくれるツールです。
- SSH-AGENT に秘密鍵をロードしてしまえば、あとはパスワード(パスフレーズ)入力なしでSSH認証できる
- agent forward機能を使うことで、SSHした先でさらにSSHするときでも、パスワード(パスフレーズ)入力なしでSSH認証できる
1は、(セキュリティのことに目をつぶると)パスフレーズなしの秘密鍵ファイルを使うことで代用できるのですが、SSH-AGENTなしで2を実現するにはリモートサーバにパスフレーズなしの秘密鍵ファイルを置いてしまうことになります。セキュリティ的にかなりオススメできません。
現代社会のインフラエンジニアに欠かせない git や ansible ではSSHを多用します。
そんなわけで、SSH-AGENTは欠かせないツールであり、agent forwardは欠かせないテクニックです。
SSH-AGENTとSSHの間の通信
SSHクライアントは、必要に応じてSSH-AGENTに署名を依頼することになるので、SSH-AGENT と SSHの間にはセキュアな通信チャネルが必要です。
UNIX系のOSだと、通信チャネルは Unix domain socket2 の一択です。
- WindowsのWin32 NamedPipe3
- MSYS2 の Unix domain socket
- WSLの Unix domain socket
- WSL2 の Unix domain socket
-
pageant.exe
互換の共有メモリ
複数の通信チャネルと、それらを相互変換するプロキシの組み合わせにより、理解の難易度が爆発しています。
パターン分け
実行形式のパターン分け
まず実行形式ファイル、つまり .exe
ファイルのパターン分けです。
WindowsでのSSHを語る上では、実行形式に関するいくつかのグループ、種族を知らなければなりません。
Windowsバイナリ
普通のWindowsのプログラムです。32bitか64bit。
実行形式のフォーマットは PE4 (Portable Executable)です。
WSL (Windows Subsystem for Linux) バイナリ
Windows上のLinux環境であるWSL1またはWSL2で実行するプログラムです。
詳しいことはWSL1やWSL2関係の記事を読んでください。
バイナリファイル自体にWSL1とWSL2用の違いはないみたいです。
実行形式のフォーマットはELFです。
$file /bin/ls
/bin/ls: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2f15ad836be3339dec0e2e6a3c637e08e48aacbd, for GNU/Linux 3.2.0, stripped
MSYS2 バイナリ
MSYS25 は cygwin6 の余計な部分をそぎ落としてミニマル化したものです。
cygwin は Windows 上にUNIX的な環境を再現する、歴史あるプロジェクトです。
詳しいことはMSYS2関係の記事を読んでください。
2021年時点では、MSYS2はMSYS2自体での利用というよりも、Git for Windows7のベースとして広く利用されています(主観)。Git for Windows 7 は、gitのWindows移植版です。MSYS2をベースにして移植されています。以降、「GFW」と表記します。
MSYS2ベースのプログラムは、MSYS2ベースのプログラムの間で通じる共通のエミュレーションレイヤを持っています。実行形式のフォーマットとしては、Windows標準のPEです。
GFWはMSYS2をベースにしていますが、本家MSYS2からはフォークしてしまっているので、GFWとMSYS2は「混ぜるな危険」だそうです。
名前は同じだけど互換性の無いDLLをロードしてしまってクラッシュする、最近はすっかり見かけなくなったDLL地獄を体験できるのかもしれません。
その他
MSYS2の前身の MSYSとか、MinGW とか cygwin とかあるけど、ここでは考えないことにします。
通信チャネルの種類
次に、SSHとSSH-AGENT間の通信チャネルの分類です。
pageant.exe
方式
以降、pageant_shm
(pageant shared memory)と表記します。
Windowsで普及しているSSHクライアント、SSHスイートである putty に付属している pageant.exe
8 の方式です。
Win32のメッセージング(WM_COPYDATA
等)や、共有メモリ機構を使っているそうです。
2015年ぐらいまで、Windows ではこの pageant 方式がSSHとSSH-AGENTの通信方式として、ほぼ唯一のものでした。(諸説あり)
Win32 NamedPipe
以降 NamedPipe
と表記します。
WindowsのNamedPipe機構を使う方式です。
NamedPipe
の識別子の例
\\.\pipe\openssh-ssh-agent
WSL1 Unix domain socket
以降、UDS/WSL1
と表記します。
WSL1の世界での、Unix domain socketを使う方式です。
UDS/WSL1
の識別子の例
/tmp/ssh-qe810tCXteV2/agent.16960
WSL2 Unix domain socket
以降、UDS/WSL2
と表記します。
WSL2の世界での、Unix domain socketを使う方式です。
WSL1のUnix domain socketはWSL2のUnix domain socketと互換性がありません。つまり相互通信できません。
UDS/WSL2
の識別子の例
/tmp/ssh-UdWUYum9bXkJ/agent.16975
MSYS2 Unix domain socket
以降、UDS/MSYS2
と表記します。
MSYS2の枠組みでの、Unix domain socketを使う方式です。
UDS/MSYS2
の識別子の例
/c/tmp/ssh-UdWUYum9bXkJ/agent.16975
WindowsのSSH実装
実行形式のパターン分けと、通信チャネルのパターン分けが分かったところで、WindowsのSSHクライアントの実装を見てみます。
Win32-OpenSSH
Microsoftによる、OpenSSHの移植版です。64bit版もWin32-OpenSSHです。64ビット版のファイル名は OpenSSH-Win64.zip
になっています。
https://github.com/PowerShell/Win32-OpenSSH
SSH-AGENTとの通信は NamedPipe
です。
pageant 互換のSSHクライアント
putty9, RLogin10, TeraTerm11, FileZilla12, WinSCP12 などです。
SSH-AGENTとの通信は pageant_shm
です。
GFW内蔵SSH
Git for Windowsに同梱されるSSHです。GFWがMSYS2ベースなので、GFWに内蔵されるSSHもMSYS2ベースです。
SSH-AGENTとの通信は UDS/MSYS2
です。
WSL SSH
WSL1/2でインストールされる SSH です。
他のディストリビューションでも違いはないと思いますが、ストアでインストールできるUbuntu 20.04 LTSの前提で書いています。
WSL1環境で実行する場合、SSH-AGENTとの通信は UDS/WSL1
です。
WSL2環境で実行する場合、SSH-AGENTとの通信は UDS/WSL2
です。
WindowsのSSH-AGENT実装
次にSSH-AGENTの実装です。
Win32-OpenSSH の ssh-agent.exe
Win32-OpenSSHに同梱される ssh-agent.exe
です。
SSHとの通信は NamedPipe
です。
Git For Windows の ssh-agent.exe
Git for Windows に同梱される ssh-agent.exe
です。
SSHとの通信は UDS/MSYS2
です。
Win32-OpenSSHのssh-agent.exe
とは同じ名前ですが、動作は別物です。
putty の pageant.exe
puttyに同梱される本家 pageant.exe
です。
SSHとの通信は pageant_shm
です。
嬉しいことに、バージョン 0.77 から、NamedPipe
がサポートされました。
Windows's own port of OpenSSH uses the same mechanism as Pageant to talk to its SSH agent (Windows named pipes).
-
pageant.exe
もGUIのウィンドウと、実体としてのSSH-AGENT("its SSH agent") はNamedPipe
で通信している。 - そのSSH-AGENTと通信するためのNamed pipeを、OpenSSHから使えるようにするよ。
ということのようです。詳しくは後述します。
pageant.exe
互換SSH-AGENT
pageant.exe
の互換 SSH-AGENTです。
本家 pageant.exe
が NamedPipe
もサポートするので、pageant.exe
互換 SSH-AGENTを使う意味は無いと思います。
古い内容。一応文章は残しておきます。
pageant.exe
互換 SSH-AGENT である pageant+
13 は、複数の通信チャネルに対応しています。
https://github.com/zmatsuo/pageant-
-
UDS/MSYS2
- pageant+ のサイトの表記だと、Git for Windows, OpenSSH(msys2)
-
UDS/WSL1
- pageant+ のサイトの表記だと、OpenSSH(WSL)
- cygwin方式
- pageant+ のサイトの表記だと、OpenSSH(Cygwin)
-
NamedPipe
- pageant+ のサイトの表記だと、OpenSSH(Microsoft版)
-
pageant_shm
- 当然ながら、普通の pageant.exe と同じ方式も動作します。
マルチプロトコルに対応しているという点で貴重なSSH-AGENTなのですが、2021年3月時点では残念ながら開発が停止しているようです。
動作が不安定なところがあります。
- KeePassXC 等から、APIで秘密鍵を追加するときにクラッシュすることがある。
- ansibleで複数台に処理するときのように、多数のリクエストが同時に発生すると、取りこぼす時がある。
WSL1/WSL2 の ssh-agent
WSLのopenssh に同梱される ssh-agent
です。
SSH-AGENTプロキシ
基本的には、SSHクライアントは同族のSSH-AGENTとしか通信できません。
例) WSL2のssh
は、WSL2のssh-agent
としか通信できない。
しかし、それだと使うクライアントの種類の数だけSSH-AGENTが必要になってしまいます。
もともとの、「SSH秘密鍵が必要になる署名処理を1か所にまとめる」という利便性がスポイルされます。
SSHクライアントが同族のSSH-AGENTとしか通信できない、という問題を解決するために、SSH-AGENTプロキシが開発されています。
異なる通信チャネル間をプロキシ接続することができます。
例) WSL2のssh
(UDS/WSL2
) と pageant.exe
(pageant_shm
) の間を接続することで、WSL2のssh
から pageant.exe
にロードした秘密鍵を使用できる。
wsl-ssh-ageant
- 実行形式: Windowsバイナリ
- 受け入れるクライアント
- クライアントとの通信チャネル:
UDS/WSL1
- クライアントの例: WSL1 の SSH
- クライアントとの通信チャネル:
- 接続先SSH-AGENT
- SSH-AGENTとの通信チャネル:
NamedPipe
- SSH-AGENTの例: OpenSSH-Win32 の SSH-AGENT
- SSH-AGENTとの通信チャネル:
名前がややこしいですが、ssh-wsl-agent14 は、このwsl-ssh-agent の前身です。忘れましょう。
wsl-ssh-pageant
- 実行形式: Windowsバイナリ
- 受け入れるクライアント1
- クライアントとの通信チャネル:
NamedPipe
- クライアントの例: OpenSSH-Win32 の SSH
- クライアントとの通信チャネル:
- 受け入れるクライアント2
- クライアントとの通信チャネル:
UDS/WSL1
- クライアントの例: WSL1 の SSH
- クライアントとの通信チャネル:
- 接続先SSH-AGENT
- SSH-AGENTとの通信チャネル:
pageant_shm
- SSH-AGENTの例: pageant.exe
- SSH-AGENTとの通信チャネル:
OpenSSH-Win32 と WSL1 の2つタイプのクライアントと接続できる優れものです。
wsl2-ssh-pageant
- 実行形式: Windowsバイナリ
- 受け入れるクライアント
- クライアントとの通信チャネル:
UDS/WSL2
- ただし
socat
で中継する必要あり
- ただし
- クライアントの例: WSL2 の SSH
- クライアントとの通信チャネル:
- 接続先SSH-AGENT
- SSH-AGENTとの通信チャネル:
pageant_shm
- SSH-AGENTの例: pageant.exe
- SSH-AGENTとの通信チャネル:
wsl-ssh-pageant の WSL2 対応版です。
SSHクライアントとの接続には、socat
を使います。
wsl2-ssh-pageantのサイトから引用。
export SSH_AUTH_SOCK=$HOME/.ssh/agent.sock
ss -a | grep -q $SSH_AUTH_SOCK
if [ $? -ne 0 ]; then
rm -f $SSH_AUTH_SOCK
(setsid nohup socat UNIX-LISTEN:$SSH_AUTH_SOCK,fork EXEC:$HOME/.ssh/wsl2-ssh-pageant.exe >/dev/null 2>&1 &)
fi
ssh-pageant
- 実行形式: MSYS2バイナリ
- 受け入れるクライアント
- クライアントとの通信チャネル:
UDS/MSYS2
- クライアントの例: GFW同梱の SSH
- クライアントとの通信チャネル:
- 接続先SSH-AGENT
- SSH-AGENTとの通信チャネル:
pageant_shm
- SSH-AGENTの例: pageant.exe
- SSH-AGENTとの通信チャネル:
コードリポジトリは前掲のgithub のようですが、GFWにバイナリが同梱されています。
個別にダウンロードする必要はありません。
socat
socat
は2つのストリームをリレー接続する汎用ツールです。SSH, SSH-AGENT専用ではありません。
wsl2-ssh-pageant の例では、UNIX domain socket と、EXECで起動したプログラム(wsl2-ssh-pageant.exe)の標準入出力をリレーしています。
この記事では、WSL の SSH とwsl2-ssh-pageant 等の socat
を使いますが、socat
自体はUNIX OS汎用のツールです。
npiperelay
npiperelay は、Win32 NamedPipe と標準入出力をリレーする汎用ツールです。SSH, SSH-AGENT専用ではありません。
socat
と組み合わせることで、Unix domain socket と、NamedPipe
をリレー接続することができます。
github の npiperelay でもバイナリが配布されていますが、wsl-ssh-agent の配布物にもバイナリが同梱されています。
本家pageant.exe
のOpenSSH連携機能
pageant.exe
はバージョン 0.77 から、NamedPipe
をサポートしています。
具体的には、コマンドラインに --openssh-config
を指定して起動することで、OpenSSH (Win32-OpenSSH) 用のコンフィグが生成されます。
PS> pageant --openssh-config pageant.conf
PS> Get-Content pageant.conf
IdentityAgent "//./pipe/pageant.username.1dd0bd6d8626a420747bc803b28..."
pageant.exe
を終了させるとファイルの中身は空になります。
.ssh/config
から Include
するのが良いです。
PS> pageant --openssh-config $Env:USERPROFILE\pageant.conf
PS> Get-Content $Env:USERPROFILE\.ssh\config
# pageant.exe が生成するコンフィグを読み込む
Include pageant.conf
PS>
キーストア
最後にキーストアです。SSH秘密鍵を保持しておく場所です。
大きく2つあります。
- 秘密鍵ファイル
- パスワードマネージャ
他に、Windows OSの証明書ストアや、スマートカード/セキュリティトークン、Gnu PG Keyringをキーストアとして利用できるSSHクライアントもあるそうです。
詳しくは@ttdodaさんのコメント をどうぞ。
秘密鍵ファイル
ファイルシステム上のファイルとして、存在するSSH秘密鍵です。要はふつうのファイルです。
SSH秘密鍵にパスフレーズ15を設定している場合、秘密鍵をロードするときにパスフレーズの入力が必要です。
秘密鍵ファイルが3つあると、3回入力することになるので面倒です。
パスフレーズ無しにしておくことで入力の手間が省けます。
パスフレーズ無しの秘密鍵ファイルというと、セキュリティ警察がやってくる場合がありますが、秘密鍵ファイルは信頼するパソコンにしか置かないとするのであれば、利便性を取ってパスフレーズ無しにしておくのもありです。
ただし、パスフレーズ無しの秘密鍵ファイルというのは、平文のパスワードと同じ意味であることは知っておくべきです。
パスワードマネージャ
Webサイト等のログインアカウントを管理するパスワードマネージャです。
パスワードマネージャの中には、SSH秘密鍵を管理する機能を持つものもあります。
KeePassXC
KeePassXCもSSH秘密鍵管理機能を持つパスワードマネージャの1つです。
https://keepassxc.org/
エントリの添付ファイルとしてSSH秘密鍵を添付しておくと、KeePassXCのデータベースを開いたタイミングで、SSH-AGENTに秘密鍵を追加してくれます。
Q: How does the SSH Agent work?
A: The SSH Agent feature is supported on all target platforms (Linux, macOS and Windows) and it acts as a client for an existing agent. It can automatically add SSH keys from your KeePassXC database to a running SSH agent when unlocked and remove them when locked.
追加する先のSSH-AGENTとして、pageant (pageant_shm
方式) のほかに、Win32 OpenSSH (NamedPipe
方式)も選択できます。
片方、または両方に対して追加することができます16。
パスワードマネージャのマスターキーでデータベースを開いたタイミングで、SSH-AGENTへSSH秘密鍵を追加してくれます。
KeePass
KeePass は KeePassXC のフォーク元のパスワードマネージャです17。
https://keepass.info/
KeePass単体ではSSH関連機能は持ちませんが、プラグインのKeeAgentを入れることで、pageant 互換の SSH-AGENT として動作します。
https://lechnology.com/software/keeagent/
KeePassXCのSSH連携機能は、SSH-AGENTへキーを追加する機能であって、SSH-AGENTそのものではないですが、KeeAgentはSSH-AGENTそのものになります。pageant_shm
方式です。
まとめチャート
以上をまとめたチャートが下図です。
筆者の構成
筆者の構成は以下の通りです。
- SSH秘密鍵はKeePassXCで管理する。
- putty 標準の
pageant.exe
を以下のコマンドラインで常駐させておく。--openssh-config $Env:USERPROFILE\.ssh\config.d\pageant.conf
- タスクスケジューラから起動する場合は環境変数の記法が変わる。
--openssh-config %USERPROFILE%\.ssh\config.d\pageant.conf
- Win32-OpenSSHのコンフィグファイル
$Env:USERPROFILE\.ssh\config
からpageant.conf
をInclude
する。 - WSL2 の
.bash_profile
で、wsl2-ssh-pageant.exe + socat を起動する。
pageant.exe
が NamedPipe
をサポートしたので、wsl-ssh-pageant.exe
が不要になり、ずいぶんすっきりしました。
これで、以下3種に対して秘密鍵認証を提供できます。
- Win32 OpenSSH
- PuTTY互換クライアント
- WSL2 SSH
-
秘密鍵を転送する、と解説されている記事もありますがそんなことはありません。秘密鍵がSSH-AGENTから出ていくことはありません。 ↩
-
単一ホスト内でのプロセス間通信メカニズム。https://ja.wikipedia.org/wiki/UNIX%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88
しかし、Windowsでは複数のチャネルが混在しています。 ↩ -
Windowsプラットフォームでの単一ホスト内プロセス間通信メカニズム。https://docs.microsoft.com/ja-jp/windows/win32/ipc/named-pipes ↩
-
「putty の SSH-AGENT」 だと思われますが、pagent ではありません。pageantです。英単語の pageant (ページェント、野外劇) との語呂合わせでしょうか。 ↩
-
https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html ↩
-
バイナリは
pageant+.exe
だが、リポジトリはpageant-
。 ↩ -
パスワードと同じ意味です。「パスフレーズ」と書くときにはパスワード(pass 'word')よりも長い、15文字以上といったフレーズ(pass
phrase
)を意図することが多いです。でも、本人しか知らない秘密の合言葉という意味では、パスワードもパスフレーズも同じです。 ↩ -
Version 2.6.6 以前は pageant (
pageant_shm
方式) と、Win32 OpenSSH (NamedPipe
方式) との二者択一でしたが、2022/5/22 のVersion 2.7.0 から両方に対して秘密鍵を追加できるようになりました。 ↩ -
正確にはKeePassXCのフォーク元のKeePassXのさらにフォーク元です。 ↩