LoginSignup
218
189

混沌を極めるWindowsのssh-agent事情

Last updated at Posted at 2021-05-03

どうしてこうなった。

何の話?

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してくれるツールです。

  1. SSH-AGENT に秘密鍵をロードしてしまえば、あとはパスワード(パスフレーズ)入力なしでSSH認証できる
  2. 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.exeNamedPipe もサポートするので、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-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

OpenSSH-Win32 と WSL1 の2つタイプのクライアントと接続できる優れものです。

wsl2-ssh-pageant

  • 実行形式: Windowsバイナリ
  • 受け入れるクライアント
    • クライアントとの通信チャネル: UDS/WSL2
      • ただし socat で中継する必要あり
    • クライアントの例: WSL2 の SSH
  • 接続先SSH-AGENT
    • SSH-AGENTとの通信チャネル: pageant_shm
    • SSH-AGENTの例: pageant.exe

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

コードリポジトリは前掲の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 方式です。

まとめチャート

以上をまとめたチャートが下図です。

window-ssh-agent-20230930.drawio.png

筆者の構成

筆者の構成は以下の通りです。

window-ssh-agent-Ver3.drawio.png

  • 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.confInclude する。
  • WSL2 の .bash_profile で、wsl2-ssh-pageant.exe + socat を起動する。

pageant.exeNamedPipe をサポートしたので、wsl-ssh-pageant.exe が不要になり、ずいぶんすっきりしました。
これで、以下3種に対して秘密鍵認証を提供できます。

  • Win32 OpenSSH
  • PuTTY互換クライアント
  • WSL2 SSH
  1. 秘密鍵を転送する、と解説されている記事もありますがそんなことはありません。秘密鍵がSSH-AGENTから出ていくことはありません。

  2. 単一ホスト内でのプロセス間通信メカニズム。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では複数のチャネルが混在しています。

  3. Windowsプラットフォームでの単一ホスト内プロセス間通信メカニズム。https://docs.microsoft.com/ja-jp/windows/win32/ipc/named-pipes

  4. https://ja.wikipedia.org/wiki/Portable_Executable

  5. https://www.msys2.org/

  6. https://www.cygwin.com/

  7. https://gitforwindows.org/ 2

  8. 「putty の SSH-AGENT」 だと思われますが、pagent ではありません。pageantです。英単語の pageant (ページェント、野外劇) との語呂合わせでしょうか。

  9. https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html

  10. http://nanno.dip.jp/softlib/man/rlogin/

  11. https://ja.osdn.net/projects/ttssh2/

  12. https://filezilla-project.org/ 2

  13. バイナリはpageant+.exe だが、リポジトリは pageant-

  14. https://github.com/rupor-github/ssh-agent-wsl

  15. パスワードと同じ意味です。「パスフレーズ」と書くときにはパスワード(pass 'word')よりも長い、15文字以上といったフレーズ(pass phrase)を意図することが多いです。でも、本人しか知らない秘密の合言葉という意味では、パスワードもパスフレーズも同じです。

  16. Version 2.6.6 以前は pageant (pageant_shm方式) と、Win32 OpenSSH (NamedPipe方式) との二者択一でしたが、2022/5/22 のVersion 2.7.0 から両方に対して秘密鍵を追加できるようになりました。

  17. 正確にはKeePassXCのフォーク元のKeePassXのさらにフォーク元です。

218
189
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
218
189