ssh-agent は、アンロックした秘密鍵をメモリーに保持し、それで認証チャレンジに署名するバックグラウンドのプログラムです。おかげで鍵のパスフレーズの入力は、接続ごとに 1 回ではなく、セッションごとに 1 回で済みます。 ssh-add は、そこに鍵を読み込むコマンドです。
あなたは正しいことをして、SSH 鍵にパスフレーズを付けました。そして今、昼休みまでに 5 回目の入力をしています。解決策は何十年も前から存在します。SSH エージェントです。鍵を一度アンロックすれば、以降のすべての接続は静かに認証されます。問題は、その「一度」が成り立つのは、エージェントが実際に動いていて、シェルがそれを見つけられて、鍵が読み込まれているときだけ、という点です。この 3 つは別々の事柄で、それぞれに固有の失敗の仕方があります。本記事は実践的なガイドです。OS ごとのエージェントの起動、ssh-add による操作、全体の自動化、そしてエージェント関連のトラブルのほぼすべてを占める 2 つのエラーの読み解きを扱います。
エージェントとは何か、一段落で
ssh-agent は、復号された秘密鍵をメモリーに保持する小さなバックグラウンドプロセスです——ディスクには置きません。サーバーがチャレンジを出すと、ssh はそのチャレンジをエージェントに渡し、エージェントはメモリー上の鍵で署名し、戻るのは署名だけです。鍵そのものはエージェントから出ることも、回線に乗ることもなく、ディスク上では暗号化されたままです。鍵を読み込むためのパスフレーズ入力は 1 回、以降の接続はすべてメモリーから賄われます。これが考え方のすべてです。エージェントは署名サービスであって、誰かが中身を読み出せる鍵のボールトではありません。秘密を守り、渡すのは署名だけです。(この一点はセキュリティ上の落とし穴でもあり、末尾で独立した節として扱います。)
Windows をお使いですか? まず Windows 版のガイドへ
error connecting to agent: No such file or directory からここに来たのなら、そのメッセージは Windows 特有のものです。Git Bash / MSYS2 の ssh から出るもので、その対処も同じく Windows 固有です——OpenSSH Authentication Agent サービス、Git Bash 自身のエージェント、そしてエラー 1058 です。いずれも Windows の ssh-agentにまとまっています。以降はすべて、クロスプラットフォームの ssh-agent と ssh-add のワークフローで、ここで読み解くエラーは OpenSSH クライアント自身のもの——Could not open a connection to your authentication agent と The agent has no identities です。
OS 別:エージェントの起動
Linux:eval "$(ssh-agent -s)"
デスクトップにログインしているなら、ほぼ間違いなくすでにエージェントがあります。systemd のユーザーサービスか、その役を担う GNOME Keyring のいずれかで、SSH_AUTH_SOCK がセッション全体に対してエクスポートされています。ssh-add -l で確認します。何も応答しない場合(素の SSH ログイン、コンテナー、最小構成のサーバーではよくあります)は、手動で 1 つ起動します:
eval "$(ssh-agent -s)"
eval が肝心です。ssh-agent -s は SSH_AUTH_SOCK と SSH_AGENT_PID の変数を表示するだけです。eval が、それらを現在のシェルに設定し、ssh がソケットを見つけられるようにします。ssh-agent を素で実行し、なぜ何も変わらないのかと首をかしげる。これが、どのツールからも見えないエージェントを「起動」してしまう、最もよくあるやり方です。
macOS:launchd が代わりに起動する
launchd がログイン時からエージェントを動かしてくれるので、起動するものは何もありません。macOS ならではの利点は、後述のキーチェーン連携です。
Windows で ssh-agent を起動する
最近の Windows には OpenSSH Authentication Agent サービスが付属していますが、手動起動に設定されていて、既定では動いていません。管理者権限の PowerShell から、一度だけ有効化します:
Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
以降は、どのターミナル(PowerShell、cmd、Git Bash 上の Windows OpenSSH クライアント)でも ssh-add が使え、エージェントは再起動をまたいで生き残ります。1 つ落とし穴があります。Git for Windows は独自の ssh と ssh-agent を同梱しており、Git Bash の中では PATH 上の ssh がそちらになっていて、別のエージェントと話していることがあります。ssh-add -l の結果が Git Bash と PowerShell で食い違う場合は、両者が 1 つのエージェントを共有するよう、Git をシステムのクライアントに向けます:
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"
完全なウォークスルー(サービス、Git Bash、ログイン時の鍵の読み込み)は、Windows の ssh-agentにあります。
ssh-add で操作する
鍵を読み込む:ssh-add ~/.ssh/id_ed25519
ssh-add # load the default keys (id_ed25519, id_rsa, …)
ssh-add ~/.ssh/id_ed25519 # load a specific key (prompts for the passphrase once)
パスを付けなければ、ssh-add は ~/.ssh から既定の鍵名を読み込みます。特定の鍵を読み込むにはパスを渡します。どちらの場合も、パスフレーズを一度だけ尋ね、以降は復号した鍵をメモリーに保持します。
読み込み済みを一覧する:ssh-add -l
ssh-add -l # list fingerprints of loaded keys
ssh-add -L # print the full public keys
ssh-add -d ~/.ssh/id_ed25519 # remove one key
ssh-add -D # remove all keys
ssh-add -l は、あらゆる認証問題の最初の診断です。期待している鍵がそのリストになければ、ほかがどれだけ正しく見えても、エージェントはその鍵で署名できません。
ssh-agent のタイムアウトを設定する:ssh-add -t
習慣にする価値のあるフラグが 2 つあります:
-tは有効期間を設定します。ssh-add -t 3600 ~/.ssh/id_ed25519は鍵を 1 時間だけ読み込みます。その後エージェントは鍵を忘れ、次に使うときに再び入力を求めます。3 週間の稼働時間のあいだアンロックされたままメモリーに居座る鍵は、あなたがそのことを忘れたずっと後まで使える鍵です。-cは確認を要求します。 署名要求のたびに、エージェントが応答する前にローカルのプロンプトが出ます。少し煩わしいですが、価値の高い鍵にはまさに適切です。
自動化する:AddKeysToAgent
手動の ssh-add という儀式は、~/.ssh/config(英語)の 1 行で消えます:
Host *
AddKeysToAgent yes
これで、鍵を必要とする最初の ssh がパスフレーズを一度尋ね、その副作用として鍵をエージェントに読み込みます。覚えておく別のコマンドはありません。プロンプトが繰り返し出なくなるだけです。有効期間を設定することもでき(AddKeysToAgent 1h)、フラグなしで -t の挙動が得られます。
macOS:キーチェーンで再起動を乗り切る。 既定では、エージェントは再起動時に鍵を忘れます。macOS はパスフレーズをログインキーチェーンに保存できるので、入力し直さずに鍵が再読み込みされます:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
そして ~/.ssh/config に:
Host *
AddKeysToAgent yes
UseKeychain yes
以降、パスフレーズの質問はログイン時にキーチェーンが答えるので、二度と目にすることはありません。その間も、ディスク上の鍵ファイルは暗号化されたままです。
誰もがぶつかる 2 つのエラー
Could not open a connection to your authentication agent(認証エージェントへの接続を開けませんでした)——ssh-add がエージェントを見つけられません。どれも動いていないか、1 つ動いているがこのシェルに SSH_AUTH_SOCK が設定されていないかのどちらかです。対処は上の OS 別の起動です。Linux なら eval "$(ssh-agent -s)" の行(まずデスクトップセッションがすでに提供していないか確認してください。ターミナルごとに 2 つ目のエージェントを起動すると、読み込んだ鍵がプロセス間に散らばるだけです)、Windows ならサービスの起動です。ログインセッションが一度も変数をエクスポートしていない cron ジョブや CI でも、これに出くわします。
The agent has no identities.(エージェントに識別情報がありません)——逆の問題です。エージェントは問題ありませんが、何も読み込まれていません。鍵を ssh-add してください。読み込んでいたのに消えた場合は、-t の有効期間が切れたか、エージェントが再起動したかです。これこそ AddKeysToAgent yes と macOS のキーチェーンが受け持つ役目です。
そして第 3 のケース。エージェントは完璧に見え(ssh-add -l に鍵が出る)、それでもサーバーが Permission denied (publickey) と言う場合です。これはもうエージェントの問題ではありません。publickey のチェックリストを順にたどってください。ユーザー名の誤り、提示する鍵の誤り、authorized_keys のパーミッションです。
アンロック済みのエージェントは生きた資格情報
エージェントが鍵を漏らすことは決してありません。ですが、ソケットは署名オラクルです。到達できる相手なら誰にでも署名し、誰が要求しているかは決して尋ねません。自分のマシンでは、これで問題ありません。ソケットは、あなただけが読めるユーザー専用のプライベートなディレクトリに置かれるからです。危ういのは混乱した代理人(confused deputy)の形です。意図しない何かがソケットに到達できるようになったとき——共有された踏み台で転送されたエージェントや、信頼していないのにあなたのユーザーとして動くプロセスなど——それは鍵を一度も見ることなく、あなたの鍵が届くすべてのサーバーに、あなたとして認証できてしまいます。鍵が漏れないというのは保証の半分にすぎません。誰がそれを使えるかを制御することがもう半分で、それがエージェントの衛生管理が少しの手間に見合う理由です。
- 鍵に有効期間を与える(
-t、またはAddKeysToAgentの有効期間)。アイドルのエージェントが自分から冷めるようにします。 - 席を離れるときはエージェントをロックする:
ssh-add -xはパスワードでロックし、ssh-add -Xで解除します。ssh-add -Dは単純にすべてを破棄します。 - 本番に届く鍵には
-cを使う。 - 既定でエージェントを転送しない。
ssh -A——ssh のエージェント転送——は、リモートホスト上であなたの鍵が生きたまま使える状態をさらすので、そこで root を持つ者は誰でも、あなたのセッションが続くあいだ、あなたとして署名できます。これは独立した一読に値します:ssh のエージェント転送は便利で危険(英語)。
Termalin での扱い方
Termalin は、このワークフロー全体を内蔵し、とがった角を削り落としています。そのキーエージェントは、鍵を一度アンロックしてあなたの代わりに署名します。有効化すべき OS サービスも、eval の行も、シェルごとに見失うソケットもありません。パスフレーズのキャッシュには既定で再ロックのタイムアウトが付いており、これは ssh-add -t の習慣を、あなたが覚える代わりに Termalin が引き受けてくれるものです。そして標準の ssh-agent プロトコルを話すので、git や既存の CLI ツールは、そのまま Termalin 経由で署名します。置き換えるのは儀式であって、ツールチェーンではありません。
Termalin は、内蔵エージェントが鍵を一度アンロックしてタイマーで再ロックする、無料のクロスプラットフォーム SSH クライアントです。ダウンロードするか、鍵を安全に扱う仕組みをご覧ください。