[Git] GitHubにGPG鍵を登録し署名付きcommit/tagを行う

Gitの初期設定で自分の名前やメールアドレスを入力するわけですが、特に認証などが入るわけではないので簡単に他人になりすましが出来てしまいます。そこで確かに自分がコミットまたはタグを付けたことを証明するために、署名を付けることができます。

GitHubを眺めていると見かける、コミットログの横に「Verified」バッジが表示されているのがそれです。

例えばプルリクを受け取った人が、なりすましを行った悪意のある第三者から送られてきたかどうか判断する手助けになるというわけですね。また同様にコミットやタグが改ざんされているかも検知することができます。

準備

署名を行うためのGnuPGをインストール、実際にGPG鍵を作成します。秘密鍵と公開鍵ができますので、公開鍵の方をGitHubに登録するという手順で進行します。

よく「GPG」か「PGP」なのか分からなくなりますw GNUであることを思い出せるかが鍵ですねw

あ、書き忘れてましたがここでは macOS Catalina 10.15.7 で作業をしていきます。

GPGのインストール

macOSの場合はHomeBrewで一発です。

$ brew install gpg

今回はGPG 2.3.2が入りました。

$ gpg --version
gpg (GnuPG) 2.3.2
libgcrypt 1.9.4
Copyright (C) 2021 Free Software Foundation, Inc.
License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

GPG鍵の生成

では早速鍵の作成を行います。gpg --gen-keyコマンドを実行すると名前とメールアドレスを聞かれますので適当に入力、アルファベットのオー(O)を入力します。

$ gpg --gen-key
()
本名: Makito Katsube
電子メール・アドレス: katsubemakito@gmail.com
次のユーザIDを選択しました:
    "Makito Katsube <katsubemakito@gmail.com>"

名前(N)、電子メール(E)の変更、またはOK(O)か終了(Q)? O

次にこの鍵にパスフレーズ(パスワード)の設定を求められます。入力した内容は絶対に忘れないよう必ずメモを取ってください。もし忘れても誰も助けてくれません。

ここまでエラーなど表示されていなければ無事に秘密鍵が作成されています。

GPG公開鍵をエクスポート

GitHubへ登録するための公開鍵を出力します。

まずは現在のPC内に保管されているGPG鍵の一覧を表示してみます。この中の「286AF〜」の部分が鍵のIDになるので、これをメモします。

$ gpg -k
pub   ed25519 2021-09-24 [SC] [有効期限: 2023-09-24]
      286AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
uid           [  究極  ] Makito Katsube <katsubemakito@gmail.com>
sub   cv25519 2021-09-24 [E] [有効期限: 2023-09-24]

鍵のIDを指定してエクスポートします。

$ gpg -a --export 286AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF > katsube_gpg.pub

GitHubへ公開鍵を保存

先ほど出力した公開鍵をクリップボードに持ってきます。macOSの場合はpbcopyコマンドが便利ですね。

$ cat katsube_gpg.pub | pbcopy

ではGitHub上で操作をしていきます。まずはGitHubの右上のメニューから「Settings」をクリック。

左メニューから「SSH & GPG keys」をクリック。

「New GPG key」ボタンをクリック

するとテキストエリアが表示されますので、クリップボードに入っている公開鍵をそのままペーストし送信ボタンを押せば完了です。簡単ですね。

GitにGPG関連の設定を追加

最後に.gitconfigの設定を行います。

署名するのに先ほどインストールしたGPGを指定、またどの署名を使うのか鍵のIDも指定します。

$ git config --global gpg.program gpg
$ git config --global user.signingkey 286AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

すべてのコミットやタグに署名を付けるなら以下のコマンドも実行します。後から無効にしたい場合はtrueの箇所をfalseにしてもう一度実行するか、テキストエディタなどで.gitconfigを直接編集し該当の箇所を削除します。

$ git config --global commit.gpgsign true
$ git config --global tag.gpgsign true

実際に試してみる

コミット

いつもどおりcommitするところに-Sオプションを付けるだけです。.gitconfigで常に署名するオプションを指定している場合はこのオプションも不要です。

$ git commit -S -m '1st commit'
$ git push

GitHub上でcommitログを確認すると「Verified」バッチが付くようになりましたね。クリックするともう少し詳細な情報も表示されます。

タグ

タグの場合は-sオプションを付けます。コミットとの時と異なり小文字である点に注意が必要です。こちらも.gitconfigで常に署名するオプションを指定している場合は指定不要です。

$ git tag -s v1.0 -m '1st version'
$ git push --tags

こちらもGitHub上で確認すると「Verified」バッチが付くようになります。同様にクリックするともう少し詳細な情報も表示されます。

パスフレーズを毎回入力したくない

さすがに高頻度で聞かれるのは面倒ですよね。こちらのサイトを参考にさせていただき、PinentryというGnuPG製のソフトを導入します。

インストール

いつも通りbrewで一発です。

$ brew install pinentry-mac

設定ファイルを追加します。~/.gnupgは最初にgpgをインストールした際に作成されています。

$ echo 'pinentry-program /opt/homebrew/bin/pinentry-mac' > ~/.gnupg/gpg-agent.conf

すでに起動中のプロセスがあるとうまく動かないので停止します。

$ gpgconf --kill gpg-agent

試してみる

適当な内容でファイルを編集しコミットすると、初回だけ以下のようなダイアログが表示されパスフレーズを聞かれます。パスフレーズを入力後に必ず「Save in Keychain」にチェックをした上でOKボタンをクリックします。

これで次回以降はパスフレーズを聞かれることが無くなります。実際にTerminalのウィンドウを閉じ、もう一度開いて試してみてください。またここではコミットで試していますがタグも同様に聞かれなくなります。

その他

git logで署名付きか確認

git logコマンドに--show-signatureオプションを付けると各コミットの署名の有無をチェックできます。

$ git log --show-signature 1f802290b72be948f8311ea871417cab985a57a4
commit 1f802290b72be948f8311ea871417cab985a57a4 (tag: v1.0)
gpg: 金  9/24 18:03:28 2021 JSTに施された署名
gpg:                EDDSA鍵286AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFを使用
gpg: "Makito Katsube <katsubemakito@gmail.com>"からの正しい署名 [究極]
Author: Makito Katsube <katsubemakito@gmail.com>
Date:   Fri Sep 24 18:03:28 2021 +0900

    1st commit

秘密鍵のバックアップ

PCは消耗品ですからいずれ壊れます。作業が一段落したら忘れる前に秘密鍵を失う前にバックアップを行っておいた方が安全ですね。パスフレーズの入力が必要です。

$ gpg --export-secret-keys --armor > gpg-private.keys

復旧する場合は以下のコマンドです。

$ gpg --import gpg-private.keys

fatal: failed to write commit object

当初、署名付きのコミットを行おうとすると以下のようなエラーが出てしまいました。

$ git commit -S -m '1st commit'
error: gpg failed to sign the data
fatal: failed to write commit object

Gitを通さずに直接GPGで署名してみると、具体的なエラー内容がわかったのでググって調べると、

$ echo "test" | gpg --clearsign
gpg: 署名に失敗しました: Inappropriate ioctl for device
gpg: [stdin]: clear-sign failed: Inappropriate ioctl for device

Terminal上で以下の環境変数を定義することで回避できました。忘れないうちに.zshrcへ転記しておきます。

$ export GPG_TTY=$(tty)

トラブルシューティング

エラー「Pinentryがありません」

ちょっと前に書かれた記事を参考にしながらPinentryをHomebrewでインストールした場合、以前とパスが変更になっているためPinentryが存在しないといった旨のエラーが出ます。

$ git commit -m 'foobar'
error: gpg failed to sign the data:
[GNUPG:] KEY_CONSIDERED FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 2
[GNUPG:] BEGIN_SIGNING H10
gpg: 署名に失敗しました: Pinentryがありません
[GNUPG:] FAILURE sign 67108949
gpg: signing failed: Pinentryがありません

ちょっと前までは以下のコマンドを実行していましたが、

$ echo 'pinentry-program /usr/local/bin/pinentry-mac' > ~/.gnupg/gpg-agent.conf

現在は次のコマンドを実行します。現在のプロセスをKillするのも忘れずに。

$ echo 'pinentry-program /opt/homebrew/bin/pinentry-mac' > ~/.gnupg/gpg-agent.conf
$ gpgconf --kill gpg-agent

参考ページ