YubiKeyとSSH – Cygwin, Git for Windows, GnuPG

macOS Ubuntu FreeBSD に続いてCygwinとGit for Windowsでed25519-sk鍵タイプを試していきます。 GnuPGは2021-11-17現在-sk鍵タイプに対応していないので、YubiKeyのOpenPGPモジュールにてEd25519の認証鍵と署名鍵を作ってGitHubで使うための操作です。

Contents

Windows: 調査

WindowsではPuTTYとPageantが広く使われていたので Cygwin や MSYS2 にも ssh-pageant という Pageant と Cygwin のソケット $SSH_AUTH_SOCK のプロキシがあります。さらにWSL, WSL2が登場してすごいことになっているようです。Windows についてる OpenSSH for Windows、WSLまたはWSL2, MSYS2, Cygwinです。使わないものまで一気にやると沼にはまりそうなので適当に。

  • OpenSSH for Windows: OpenSSH_for_Windows_8.1p1, LibreSSL 3.0.2 (Windows 10 21H1 as of 2021-10-28)
  • WSL, WSL2: 利用するディストロ次第。
  • Git for Windows(MSYS2): OpenSSH_8.8p1, OpenSSL 1.1.1l 24 Aug 2021 (as of 2021-10-28)
  • Cygwin: OpenSSH_8.8p1, OpenSSL 1.1.1l 24 Aug 2021 (as of 2021-10-28)
  • Pageant: -sk 未対応 (as of 2021-11-17)
  • gpg-agent: -sk 未対応 (as of 2021-11-17)
  • KeePassXC: SSH Agent: Support OpenSSH 8.2 security key (FIDO/U2F) backed SSH keys -sk 対応済み。OpenSSH for Windows の ssh-agent とお話ししてくれるようですが、そちらが -sk に対応するまで使えないはず。

Cygwin: 準備

YubiKeyの選択の仕方は前置きをご覧ください。青いYubiKeyでもFIDO U2FかFIDO2の機能があれば使えるはずです。今回はfirmware 5.2.4の青いSecurity Keyシリーズで確認しています。5.2.3未満の場合はecdsa-skでお試しください。ecdsa-skはP-256楕円曲線だけなので使う理由があるのかは謎です。

各OpenSSHが使っているssh-agentのプロキシで頑張ると今回のように鍵タイプが増えた場合に使えないパスが出てきて悩みが増えそうなので、使うものに限定して ~/.ssh/ をjunctionで共有するにとどめることにします。

OpenSSH for Windows%USERPROFILE%/.ssh
Git for Windows(MSYS2)%USERPROFILE%/.ssh
Cygwin(c:/cygwin64)/home/username/.ssh
~/.ssh/ の場所
Gpg4win%APPDATA%/gnupg
(HKCU/Software/GNU/GnuPG/HomeDir, $GNUPGHOME, gpgconf.ctl & gpgconf.exe)
Git for Windows(MSYS2) %USERPROFILE%/.gnupg
Cygwin (c:/cygwin64)/home/username/.gnupg
ついでに~/.gnupg/ の場所

 setup-x86_64.exe で openssh をインストールします。

libfido2がインストールされますが hid.dll 利用のためU2F, FIDO2を利用する際に権限の昇格が必要になります

2022-10-03追記: 下の追記の通りopenssh-sk-winhello無しのlibfido2だけで権限の昇格不要になっています。

2021-11-22追記: libfido2にWindowsのWebAuthnライブラリ利用の変更が入っています。各パッケージが新しくなれば openssh-sk-winhello は不要になるかもしれません。

openssh-sk-winhelloの導入は不要になっています。ssh-keygenはワンショットなのでどうにかなりますが、sshするたびに権限を昇格させたり、ssh-agentはdaemonなので昇格させたまま動かしておくことになります。一方でOpenSSHにミドルウェアを置き換える仕組みも作りこまれていて、それを用いてlibfido2の代わりに権限の昇格不要なWebAuthnライブラリによる実装をしてくれた方がいます。UACのお世話にならないで済むのは非常にありがたいので利用させてもらいます。

Cygwin: 期待通りにならない場合は

openssh-sk-winhelloの導入は不要になっています。<strike>libfido2を通常のユーザ権限で使うとエラーメッセージを出してもらえないので何も起きていないように見えます。権限を昇格させるか、 openssh-sk-winhello の導入 のようにします。</strike>

一方で例えば2021-11-17現在だと以下のようなエラーが起きるかもしれません。

ssh-agentでエラーが起こっていてデバッグログを吐かせるにはssh-agentを起動しなおさないといけないので、先にssh単体ではどうなるのか確認してしまいます。同様にエラーとなりますが error while loading shared libraries: ?: cannot open shared object file: No such file or directory というどのライブラリがロードできないのかわからないつれないメッセージなので strace かけます。

cannot load cygcbor-0.8.dll

というわけでlibfido2が使っているCBORライブラリが読めなくてエラーが発生しています。パッケージ作成時にバージョン条件の指定がないとかでしょうか。openssh-sk-winhello利用でlibfido2は使わない場合でもssh-sk-helperが起動できないのでエラーとなります。早々に解消されると思いますが今はサクッと setup-x86_64.exe でlibcborをダウングレードしてしまいます。

Cygwin: openssh-sk-winhello の導入

2022-10-03追記: 下の追記の通りopenssh-sk-winhelloの導入は不要になっています。ここはスキップしてください。

2021-11-22追記: 前記の通り libfido2 にWindowsのWebAuthnライブラリを使う変更が入っています。パッケージが更新されればこの手順は不要になるかもしれません。

releasesよりOpenSSHに合ったSource codeをダウンロードし、伸長展開します。README.mdのBuildの項を見てビルドしていきます。ビルド環境が出来上がっていない場合はエラーメッセージに応じてREADME.mdを参照に適宜パッケージを追加してください。

/usr/lib/winhello.{dll,dll.a,la} がインストールされるので、.profileなり.xxxshrcなりCygwinなのでログインシェルだらけかと思うので気にせず適当なスクリプトで環境変数に設定します。

Cygwin: クライアント側

クライアント側を整えていきます。以下openssh-sk-winhelloを使用していますが、libfido2だけでも流れは同様です。

何もしないとSK_DEBUGが定義されていて警告が出ますが、32bit版のアプリケーションがWebAuthnを使ったときに起こるエラーに対するメッセージのようです。webブラウザでのWebAuthnでおなじみの雰囲気のダイアログが出ます。winhello.dllを呼び出した c:/cygwin64/usr/sbin/ssh-sk-helper.exe からと表示されています。

OKで進めるとFIDO2 PINを訊かれ、入力するとダイアログ及びピカピカ光ってタッチを要求されます。タッチすると鍵情報を保存するファイル名を訊かれます。入力すると鍵情報ファイルを暗号化するためのパスフレーズを訊かれます。このパスフレーズは-O residentを指定した場合のFIDO2(YubiKey)内の鍵情報には適用されません。あくまでもファイル(ここでは鍵ハンドルが格納されている~/.ssh/id_ed25519_sk)の暗号化のためのものです。またFIDO2の -O verify-required については後日検証にしてしまいます。今回は -O オプションの指定はしないで進行します。

Cygwin: ssh-agent

ssh-agentの情報の共有はsetxを使ってみます。お使いの$SHELLに合わせて .profile などのログインスクリプトに次のような記述を追加します。

時折 setx にえらい時間がかかりますが、こんな感じにしておけば Cygwin 以外のOpenSSHの邪魔をすることもなく複数minttyで使えるかと思います。

Cygwin: サーバ側

公開鍵をサーバまで持っていきログインするユーザの ~/.ssh/authorized_keys に追加、無ければ作成して追加します。

設定はこれだけで、既に8.2以降のOpenSSHサーバが動いている場合はそのまま利用できるので接続までスキップしてください。

Cygwin: サーバも同じ機械で動かす

クライアントOSでわざわざsshdを自力でインストールして動かし続けるのを強くお勧めはしないので簡単に。

hostキーや/etc/sshd_configを作成してくれます。メッセージをよく読んで進めます。サービスにはしないことをお勧めします。 /etc/sshd_config を適切に設定したのち、起動します。止め忘れないようにフォアグラウンドで動かします。

別のminttyから確認します。何か大昔に書いたのを思い出しながら…

Cygwin: 接続

接続です。

まず鍵ハンドルファイルのパスフレーズを訊かれ、入力するとダイアログの表示とYubiKeyがピカピカし始めるので、タッチすればログインできます。続いてssh-agentを試します。

ssh-add した際に訊かれたパスフレーズを最後に、あとはYubiKeyをタッチするように言われるだけです。署名は ssh-agent にお任せしたので ssh コマンドには SecurityKeyProvider を指定する必要がなくなります。あと WebAuthn ではタッチしなければいけない時にはWindowsのダイアログが出るので、わかりやすいです。

Git for Windows(MSYS2): 準備

Git for Windows: openssh-sk-winhello の導入

2021-11-22追記: 前記の通り libfido2 にWindowsのWebAuthnライブラリを使う変更が入っています。パッケージが更新されればこの手順は不要になるかもしれません。

Cygwin同様にlibfido2がhid.dllを使用しているので権限の昇格を不要にするためにopen-sk-winhelloを利用します。Install に書かれている通り releases からwinhello.dllをダウンロードします。デフォルトで /usr/lib か /usr/local/lib にある SKプロバイダしかロードされない ので、Cygwin同様に (C:/Program Files/Git)/usr/lib にコピーします。ここだけGit Bashを管理者権限で起動します。

SKプロバイダーの指定はCygwinと同様にできるので、ssh-agentも合わせて同じように .bashrc あたりに記述します。

Git for Windows: クライアント側

以降は同様なのでざっくりと。ssh-keygenした後に ~/.ssh/id_ed25519_sk.pub の内容を GitHub にコピペしてます。

ssh-agentがタッチを要求する際にWebAuthnだけではなくGit for Windowsのダイアログも出ますが、タッチで両方とも消えます。ssh-agentを使わない場合はsshコマンド用に SecurityKeyProvider /usr/lib/winhello.dll を ~/.ssh/config に書くか、 -o SecurityKeyProvider=/usr/lib/winhello.dll のように -o オプションで指定します。

GnuPG

OpenPGPモジュールだけで閉じた場合 FIDO U2Fモジュールは不要です。また前述のとおりgpg-agentがまだ-sk鍵タイプに対応していないため、-skを使う場合はgpg-agentをssh-agentの代替にできないです。以下昔ながらの使い方です。以降はOpenPGPモジュールを搭載していない青いSecurity Keyシリーズでは実施できません。

dev.yubico SSH authentication やそこからリンクされている Using Your YubiKey with OpenPGP そしてさらにそこからリンクされている YubiKey-Guide が主要な情報源です。主鍵、副鍵、それぞれ認証、署名、暗号化に使えたりとOpenPGPの仕組みに慣れてもややこしいです。

GnuPGをCygwinだけで使うということは今どきは無いはずなので、WindowsではGpg4winを使うのが簡単です。今回はインストールするのも面倒なのでPortableAppsのGPG Plugin Portableを利用させてもらいました。~/.gnupg の位置は前述のとおりです。

各ドキュメント、特にYubiKey-Guideは多岐にわたって詳細に書かれているので、どこをちゃんと読めばよいのかの一助に。

  • 鍵の作成や保管はBitLockerを有効にしたUSBドライブやSDカード上でやればDislockerを使ってLinuxなどでもそのまま読めます。
  • Pageantは不要です。設定でgpg-agentがPageantの代替になるので、Pageantを前提とするプログラムもPageant無しで動作します。ただしPageantもgpg-agentもまだ-sk鍵タイプには対応していません。
    • gpg –export-ssh-key KEYID でOpenSSH用の公開鍵を出力できます。gpg-agent + MSYS2やCygwinならばssh-pageant、WSLならばweasel-pageantを動かして ssh-add -L するという手もあります。
  • OpenPGPでやり取りする必要がある場合でもキーサーバーに登録する必要はないです。キーサイニングパーティーに参加しましょう(たぶんやってない)。
  • RSA, ECDSA, Ed25519どれを使えばよいのかはセキュリティービットなどでググってください。Ed25519はセキュリティービット以外にもECDSAに存在するような弱点を気にして設計されているのでEd25519、firmwareが古くて使えない場合はRSA 4096ビット、としておけば良いかと思います。ECDSAならばP-521楕円曲線を使うのが良さそうですが、RSA 4096ビットでパフォーマンスが気になる場面に出会わないので使う理由がいまいち無いかもです。-skはともかくEd25519を扱えるOpenSSHは2014-01-30リリースの6.5以降なので、OpenSSHが理由でEd25519が選択できない場面は少なそうです。

GnuPG: 主鍵の新規作成

~/.gnupg (以下ではC:/Users/omnium/AppData/Roaming/gnupg) はシステムドライブのBitLockerが有効でない場合は BitLockerが有効なドライブからジャンクションを作成するとかEFSを有効にしておくなどで自衛します。

YubiKeyのOpenPGPモジュールの鍵保管場所の数の関係で主鍵の「現在の認められた操作」をSignとCertifyにします。途中入力するパスフレーズはこの鍵を利用するときのパスフレーズです。主鍵と紐づけるメールアドレスは後から複数追加できますが、今回は簡単のためGitHubで使う場合はここでGitHub確認済みのメールアドレスを指定します。

主鍵出来上がりです。0FC7…. が主鍵のkey idで、途中の表示でも使われていますが尻尾の8バイト(16桁)や、4バイト(8桁)でも指定できます。今回は鍵数個しか作らないので8桁でも十分です。

続けます。なにやらgpgが突然日本語を話してくれなくなりました。

GnuPG: 認証用の副鍵の新規作成

主鍵で署名された認証用の副鍵が作成されました。まだYubiKeyが登場していませんがOpenSSHで利用する場合はここまでで一旦完了なので試します。主鍵が署名にも使えるので、GitHub用の鍵の準備も全部完了しています。省略しますがOpenPGPで秘密のやり取りをする場合は同様に[E]の副鍵を、これは1,2年の有効期限を付けて作成すればOKです。

これをOpenSSHサーバ側の ~/.ssh/authorized_keys に追記します。GitHubのSSH keysに追加するのもこれです。

OpenSSHはGnuPGの鍵のことは知らないのでGnuPGの鍵でOpenSSH用に署名してくれるようにgpg-agentを設定します。やり方としては GPG4Win の gpg-agent を Pageant 機能を有効にして起動して、Cygwin, MSYS2, WSL側で Pageant と $SSH_AUTH_SOCK のプロクシを動作させる、あるいはCygwin, MSYS2, WSL側で ~/.gnupg を参照できるようにして gpg-agent を ssh-agent機能を有効にして起動する、となるかと思います。今回は好みで後者のやり方でGitHub用にGit for Windowsで使うという体でやってみますが、 ~/.gnupg を使うアプリケーションが複数バージョンになるので安全のためには前者のプロクシを使うやり方が良いかと思います。特にGnuPGはメジャーバージョンが異なるとデータベース形式が異なります。

GnuPG: Git for Windowsのgpg-agentを動かす

Windowsのコマンドプロンプトからジャンクションを作ります。

Git Bashを起動します。使っているといきなりlsがディレクトリが黒バックに青という過酷な視力検査を始めるので好みに合わせます。前景ではなくて背景を青にしてみます。(現在は青が薄紫になるGit Bash用のWindowsコンソールのプロファイルをインストール時に導入できるようになったようです。)

それはともかくMSYS2のgpgで先ほど作成した鍵が見えることを確認します。

よさそうなのでgpg-agentを設定していきます。~/.gnupg/gpg-agent.conf に次のように記載します。

TTLは良いように修正してください。すでに~/.gnupg/gpg-agent.confがあった場合は重複行などを調整してください。gpg-agentを起動します。

ssh-agentとして動作しているようでなので ~/.bashrc に同様のコマンドを追加しておきます。ところで鍵の追加を忘れています。~/.gnupg/sshcontrolファイルにkeygripなるものを記述します。keygripの表示オプションをつけます。

認証用[A]のkeygripが分かったので ~/.gnupg/sshcontrol に追記します。

gpg-agent.conf でTTLを指定しているので、特に上書きする理由が無いので keygrip の後ろのTTLは0です。ssh-add -Lの返答はファイルの更新とともに変わって gpg –export-ssh-key と同じ公開鍵であることが確認できます。まだでしたらGitHubにログインしてSSH keysに ssh-ed25519 …の行を張り付けます。

GitHub用には[S]の鍵もできているので、このままGit BashでGnuPGの署名用の公開鍵を表示させます。

表示させたGnuPGの公開鍵をGitHubのGPG keysに追加します。引き続きGit Bashで

あとは通常のGitの使い方と同じで、commitの際に署名を付けるために先ほど指定したパスフレーズを訊かれます。gpg-agentが動いているので以降はTTLの間は訊かれません。pushしてcommit履歴にVerifiedが付くことを確認できます。Unverifiedの場合はUnverifiedをクリックすると理由が表示されます。

GnuPG: YubiKeyに鍵を移動する

回り道をしました。作成した主鍵と副鍵をYubiKeyに引っ越します。ここまでYubiKey無しで作業してきましたが、各鍵ペアはYubiKey内で作成することもできます。YubiKeyを無くしたり壊した場合に暗号化したものを生きている間に復号する手段が無くなってしまうため、YubicoもOpenPGPの鍵はYubiKey外で作成して安全にバックアップを取り、YubiKeyの中に移動するというやり方を推奨しています。バックアップは ~/.gnupg を丸ごと保管するか、以下のように出力がバージョンに依存しにくい正統派のやり方で安全に保管してから鍵をYubiKeyに移動します。まずは ~/.gnupg を利用中の gpg-agent を止めます。

ふたたびWindowsのコマンドプロンプトを使います。

このバックアップを別の機械でインポートして同じような状態になるかを確認します。バックアップ完了したのでYubiKeyを整えていきます。まずOpenPGP用のPINとAdmin PINをそれぞれのデフォルトの 123456, 12345678 から変更します。

続けて各鍵をYubiKey内に移動します。

YubiKeyに移動した鍵は>マークが付くようになっています。 ~/.gnupg から秘密鍵は消えているので、この状態で –export-secret-key しても使えないものが出力されるので、正常なバックアップを上書きしてしまうと泣けるかと思います。またYubiKey無しでSSHのログインや署名をすると、YubiKeyを刺すようにダイアログが表示されるようになります。すべて完了です。お疲れさまでした。

GnuPG: さらにおまけ: YubiKey内で鍵ペアを作成する

前述のとおりGitHubだけで使うということであればYubiKey内で鍵を作成させるというのもありになるので試してみます。いったんYubiKeyのOpenPGPモジュールをリセットします。~/.gnupgも丸ごと削除しています。以下YubiKeyのOpenPGPモジュールのリセットの仕方です。YubiKeyの中のOpenPGPの鍵消えます。危険です。そのままデフォルトに戻ったPINも変更します。

まっさらになったYubiKeyの中で署名用と認証用のOpenPGP鍵を作成していきます。

さくっと出来上がりました。途中バックアップについて訊かれました。本当に取れるのであればセキュリティーキーの存在を脅かすゆゆしさですが、ちゃんと秘密鍵は入っていないということなのでここで保存する意味がありません。ユーザ情報はGnuPGだけが持っているもののようです。ところで暗号化用の鍵もできているので、間違えて使わないように消します。

YubiKey内には残っているようですが、GnuPGからYubiKeyへのポインタは消えたので、間違えて使うことはなさそうです。あとは公開鍵を各フォーマットで表示できれば完了です。

完了です。ファイルとしてはYubiKeyへのポインタと公開鍵が残るのでこれらのバックアップが依然として必要ですが、BitLockerが有効でない場所に置いてもYubiKeyが安全に保管されていてシリアルで結びつけるのが困難であれば問題になることもないです。GitHub用YubiKeyとして入社時に配るとかいうのも良いかもしれません。

最後は-skから大幅にずれましたが、他に気になるOSがあったらまたお試しするかもしれません。ありがとうございました。

コメントを残す

メールアドレスが公開されることはありません。