【令和最新版】sshcontrolは使わないで!最近のGPGによるSSH認証のベストプラクティス

はじめに GnuPGユーザーがGPG鍵をSSH認証にも使おうとしたら、今までは、sshcontrol を利用するという、記事やブログの内容がほとんどだと思いますし、ChatGPTやLLMを使ったボットなどもこの内容を案内していますが、いつからか、このsshcontrolを使う法は非推奨になっていました。 This…

公開日:

最終更新:

はじめに

GnuPGユーザーがGPG鍵をSSH認証にも使おうとしたら、今までは、sshcontrol利用するという、記事やブログの内容がほとんどだと思いますし、ChatGPTやLLMを使ったボットなどもこの内容を案内していますが、いつからか、このsshcontrol使う法は非推奨になっていました。

This file is deprecated in favor of the "Use-for-ssh" attribute in the key files. (§ sshcontrol https://www.gnupg.org/documentation/manuals/gnupg/Agent-Configuration.html)

sshcontrolに変わる新しい法は GnuPG 2.3.7(2022年リリース)で導入されました。つまり、もう3年以上前から sshcontrol非推奨なのですが、ネット上のほとんどの記事はまだ古い法を案内し続けています。

ここでは、それらの変更に対応した新しい法を示します。日本語でこれが解説されてあるのはGentoo LinuxのGnuPGのwikiぐらいでしょうか。

そもそもなぜGPG鍵でSSH認証するのか?

SSH鍵ペアだけでも十分安全に認証できます。ではなざわざGPG鍵をSSHに使うのか?主な理由はいくつかあります。

  • 鍵の一元管理 — 署名・暗号化・認証を1つのGPGマスター鍵の副鍵で管理できるので、鍵がバラバラにならない

  • YubiKeyなどのハードウェアトークンと相性 — GPG鍵をYubiKeyに格納すれば、署名もSSH認証もハードウェアトークン1つで完結する

  • 鍵の失効や有効期限の管理 — GPGの仕組みで副鍵の失効や有効期限の設定ができるので、SSH鍵のライフサイクル管理がやりやすい

  • Web of Trustとの統合 — GPGの信頼モデルの中でSSH認証鍵も管理できる

「別に今のSSH鍵で困ってないし…」という方も多いと思いますが、YubiKeyを買った瞬間に全部変えたくなる、というのはよくある話です。

前提

すでにsshcontrolを利用していえも、無くてもかまいません。

  • GnuPG 2.3.7以降がインストールされているgpg --version確認できます)

  • すでに秘密鍵や公開鍵をGPGが読みこんでいる

  • 認証用の副鍵(Authentication subkey)が存在するもしくはマスター鍵に認証(A)のケーパビリティがある

    • 副鍵は必要ではないですが、ベストプラクティスとして設定せねばなりません

  • GPG でSSH 接続したい先に同じ公開鍵がある

💡 認証用の副鍵を持っていない場合は、gpg --expert --edit-key <鍵ID> から addkey認証(Authentication)ケーパビリティを持つ副鍵を作成できます。--expertつけないとケーパビリティの選択画面が出ないので注意してください。

手順 1:Keygripを確認する

まず、SSH接続したいGPG鍵のKeygripを確認してください。

Keygripとは?

Keygripは、GPG鍵(やその副鍵)を一意に識別する40文字の16進数文字列です。鍵のID(Key ID)やフィンガープリントとは別物で、GPGの内部的な鍵の識別子として使われます。gpg-agent鍵をKeygripで管理しているので、SSH用の鍵を指定するきもこのKeygripが必要になります。

法1 gpg コマンドを使う

鍵のIDや副鍵なども全て見る場合

GPG コマンド
gpg --list-secret-keys --keyid-format long --with-keygrip

出力例はこんな感じです:

sec ed25519/AAAA1111BBBB2222 2024-01-01 [C] [expires: 2026-01-01]
Keygrip = 1234567890ABCDEF1234567890ABCDEF12345678
uid [ultimate] Your Name <your@email.com>
ssb ed25519/CCCC3333DDDD4444 2024-01-01 [S] [expires: 2025-01-01]
Keygrip = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ssb cv25519/EEEE5555FFFF6666 2024-01-01 [E] [expires: 2025-01-01]
Keygrip = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
ssb ed25519/GGGG7777HHHH8888 2024-01-01 [A] [expires: 2025-01-01]
Keygrip = CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

ここで重要なのは [A](Authentication)フラグが付いている副鍵のKeygripです。上の例だと CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC ですね。[S]sign署名、[E]encryption暗号化、[C]認証局(Certify)なので、間違えないようにしてください。

鍵のIDは見なくても良い場合

gpg -k --with-keygrip

ずれにせよ、 --with-keygrip必要ですね。

法2 gpg-connect-agent を使う

gpg-connect-agent 'keyinfo --list' /bye

出力はこんな感じです:

S KEYINFO <KEYGRIP> D - - - P - - -
S KEYINFO <KEYGRIP> D - - - P - - -
...
OK

こちらはKeygripの羅列なので、どれが認証用かはこれだけでは分かりません。法1と併用するのがおすすめです。

手順 2:gpg-agentがSSH認証に関われるようにする

シェルの設定を行います。この手順自体はsshcontrol を使われていたなら設定をしなくても良いはずです。初めての方は以下の手順を踏んでください。

2-1. gpg-agent.confにSSHサポートを有効化する

~/.gnupg/gpg-agent.conf以下の行を追加します:

enable-ssh-support

これで gpg-agentSSHエージェントとしても動作するようになります。

2-2. シェルの環境変数を設定する

.bashrc.zshrcあるいはお使いのシェルの設定ファイルに以下を追加してください

# GPGがTTYを認識できるようにする
export GPG_TTY="$(tty)"

# SSHがgpg-agentを使うようにソケットを指定する
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)

# gpg-agentを起動する(すでに起動していれば何もしない)
gpgconf --launch gpg-agent

GPG_TTY設定は地味に大事です。これがないとパスフレーズの入力プロンプト(pinentry)が正しいターミナルに表示されず、認証がハングすることがあります。

SSH_AUTH_SOCK gpg-agentSSHソケットに向けることで、ssh コマンドが ssh-agent代わりに gpg-agent使うようになります。

2-3. gpg-agentを再起動する

設定を反映させるため、gpg-agent再起動します:

gpgconf --kill gpg-agent
gpgconf --launch gpg-agent

しくは、新しいターミナルを開き直せばOKです。

手順 3:gpg-agentにSSH接続に使って良い鍵を教える

ここがsshcontrolに代わる新しい法のキモです。gpg-connect-agent使って、対象の鍵にUse-for-ssh属性を付与します。

gpg-connect-agent 'keyattr <KEYGRIP> Use-for-ssh: true' /bye

<KEYGRIP>部分を、手順1で確認した認証用副鍵の[A]Keygripに置き換えてください。

設定できたか確認する

gpg-connect-agent 'keyattr <登録したKEYGRIP> Use-for-ssh: ' /bye

入力して、

D true
OK

出たらセットアップ終了です。

登録を解除したい場合

逆に、SSH認証から鍵を外したい場合は false指定します:

gpg-connect-agent 'keyattr <KEYGRIP> Use-for-ssh: false' /bye

ssh-add -L で確認する

gpg-agent正しくSSH鍵を提供しているか、SSHの側からも確認できます:

ssh-add -L

登録した鍵のSSH公開鍵が表示されれば成功です。The agent has no identities.出た場合は、手順2のシェル設定や gpg-agent再起動を見直してください。

SSH公開鍵をエクスポートする

SSH接続先(GitHubやサーバーの ~/.ssh/authorized_keys)に登録する公開鍵は、以下のコマンドでエクスポートできます:

gpg --export-ssh-key <メールアドレスまたは鍵ID>

出力は ssh-ed25519 AAAA... user@email.com のようなSSH公開鍵のフォーマットになっているので、そのまま authorized_keys貼り付けたり、GitHubの設定画面に登録したりできます。

一つの法として、ssh-add -L出力をそのまま使うこともできます。こちらも同じSSH公開鍵フォーマットです。

手順 4:接続してみる

例えばGitHubにすでに今回登録したSSHの公開鍵を登録して、接続できるようにしている場合は

ssh -T git@github.com

接続できます。

うまくいけば、こんなメッセージが返ってきます:

Hi <ユーザー名>! You've successfully authenticated, but GitHub does not provide shell access.

パスフレーズ付きの鍵の場合は、ここで pinentryダイアログが出てパスフレーズの入力を求められます。正しく入力すれば認証が通ります。

これで終了です!お疲れさまでした。

sshcontrol からの移行

sshcontrol には、鍵のKeygripが書かれているずなので、それを一行ずつ手順 3実行すれば良いはずです。

~/.gnupg/sshcontrol中身はこんな感じになっているはずです:

# 認証用鍵のKeygrip
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC 0

先頭のKeygrip部分を使って、一つずつ keyattr コマンドを実行してください。

複数の鍵がある場合は、スクリプトを組んでもいいでしょう。例えばこんな感じです:

#!/bin/bash
# sshcontrolから移行するスクリプト
while IFS= read -r line; do
# コメント行と空行をスキップ
[[ "$line" =~ ^#.*$ || -z "$line" ]] && continue
# Keygripを取り出す(最初のフィールド)
keygrip=$(echo "$line" | awk '{print $1}')
echo "Registering keygrip: $keygrip"
gpg-connect-agent "keyattr $keygrip Use-for-ssh: true" /bye
done < ~/.gnupg/sshcontrol

移行が完了したら、sshcontrol ファイルは削除してしまっても問題ありません。念のためバックアップを取ってから消すと安心ですね。

トラブルシューティング

「ssh-add -L」で「The agent has no identities.」と出る

これは ssh gpg-agentSSHソケットを見つけられていないか、gpg-agentSSHサポートを有効にしていない場合に起こります。

  1. echo $SSH_AUTH_SOCK で、パスが /run/user/<UID>/gnupg/S.gpg-agent.ssh のようなgpg-agentのソケットを指しているか確認してください。もし /tmp/ssh-... のようなパスになっていたら、ssh-agent使われてしまっています。

  2. ~/.gnupg/gpg-agent.conf enable-ssh-support入っているか確認してください。

  3. gpg-agent再起動してみてください

gpgconf --kill gpg-agent
gpgconf --launch gpg-agent

SSH接続時に「sign_and_send_pubkey: signing failed」と出る

gpg-agentパスフレーズを聞くためのpinentryプログラムが正しく動いていない可能性があります。

  1. GPG_TTY正しく設定されているか確認:

echo $GPG_TTY

何も出力されない、またはターミナルのデバイスパスでない場合は export GPG_TTY="$(tty)"実行してください。

  1. gpg-agentTTY情報を更新させる

gpg-connect-agent updatestartuptty /bye

SSHは通るが、なぜか別の鍵が使われている

gpg-agent複数のSSH鍵を提供している場合、SSHは最初にマッチした鍵を使います。ssh -v接続時のデバッグ出力を確認すると、どの鍵が試行されている分かります:

ssh -vT git@github.com

Offering public key行を見ると、どの鍵が使われているか確認できます。意図しない鍵が使われている場合は、~/.ssh/config IdentityFile指定するか、不要な鍵の Use-for-ssh falseしてください。

pinentryのダイアログが出ない / ハングする

リモート接続(tmuxやscreen経由、SSH越しなど)で作業している場合に起きやすいです。

  1. gpg-connect-agent updatestartuptty /bye実行して、現在のTTYを gpg-agent教えてください。

  2. gpg-agent.conf pinentry-program正しいpinentryバイナリを指しているか確認してください。CUIで使いたいなら pinentry-cursesGUIなら pinentry-mac(macOS)や pinentry-gnome3(Linux)などです。

それでもダメなとき

ssh -T git@github.com

接続して、公開鍵の認証が出来ない場合は、シェルの設定の内容をもう一度確認したり、シェルからインタープリタ的に設定した後、シェルを閉じないでそのまま接続を行ってください。

また、以下のようなリセットコマンドもあります。

gpg-connect-agent reloadagent /bye

gpg-connect-agent updatestartuptty /bye

どちらも OK と帰ってきたらリセット完了です。

最終手段として、gpg-agentログを有効にして原因を調べることもできます。~/.gnupg/gpg-agent.conf以下を追加してください

debug-level advanced
log-file ~/.gnupg/gpg-agent.log

gpg-agent再起動後、SSH接続を試みて、ログファイルの内容を確認してください。

おわりに

お読みいただきありがとうございました。近日中に、GPGにゼロから入門し、さらにSSH鍵など、様々な応用までカバーするための記事を公開する予定です。

参考