git-crypt を使って秘密情報を版管理する

よく訓練されたアップル信者、都元です。やばい、ブログの書き方忘れてるwwwwww

…気を取り直して。git-cryptという git plugin ツールはご存知でしょうか? 要するに、DBのパスワードやOAuthのシークレットなど、版管理はしたいけれどGitHub等にpublishするには抵抗がある情報を、 シームレスに暗号化して管理することによってリポジトリ内で版管理可能にするものです。

GNU Privacy Guard

git-cryptでは、GNU Privacy Guard という暗号化ソフトウェアを使います *1。GPGとかGnuPGと略されたりします *2

さて、git-cryptを使い始める前にGnuPGのセットアップが必要です。本稿では macOS に gpg (GnuPG) 2.2.3 をセットアップ する前提で解説します。homebrewは上手いこと導入しておいてください。

GnuPGのインストール

まず、初めてGnuPGを利用する方は、GnuPG本体および macOS 上でGnuPGを利用するためのパッケージをインストールします。 その後に、インストールした gpg-agent の設定ファイルを配置します。

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

キーペア生成とエクスポート、そして公開鍵の共有

続いて、今回は公開鍵暗号を使うために、自分のキーペアを生成します。GnuPGでは個々人をメールアドレスで認識するため、鍵の生成には名前とメールアドレスが必要です。

alice$ gpg --generate-key
gpg (GnuPG) 2.2.3; Copyright (C) 2017 Free Software Foundation, Inc.
(略)
Real name: Alice
Email address: alice@example.com
You selected this USER-ID:
    "Alice <alice@example.com>"

Change (N)ame, (E)mail, or (O)kay/(Q)uit? O

途中、パスフレーズを登録するダイアログが2回現れますので、入力してください。

ss

(略)
gpg: key XXXXXXXXXXXXXXXX marked as ultimately trusted
gpg: revocation certificate stored as '/Users/alice/.gnupg/openpgp-revocs.d/AAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXX.rev'
public and secret key created and signed.

pub   rsa2048 2017-12-05 [SC] [expires: 2019-12-05]
      AAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXX
uid                      Alice <alice@example.com>
sub   rsa2048 2017-12-05 [E] [expires: 2019-12-05]

これでキーの生成は完了です。続いて公開鍵をファイルにエクスポートしておきます。

alice$ gpg --output alice.gpg --export alice@example.com

同じ操作を、秘密情報を共有するメンバー各自で行います。

...
bob$ gpg --output bob.gpg --export bob@example.com

他者の公開鍵のインポート

生成した公開鍵ファイルを相互に交換します。本来の公開鍵交換は「なりすまし」を疑って、 信用に足る経路を経た交換を工夫したり、fingerprint と呼ばれるデータを検証する必要がありますが、まぁ今回は正しく鍵交換できていると仮定します。

受け取った他者の公開鍵は、下記のようにインポートできます。

alice$ gpg --import bob.gpg

GnuPGでは、公開鍵はこのままでは信用したことにならず、インポートした鍵を信頼する手続きを行います。

alice$ gpg --sign-key bob@example.com

ここまでで alice@example.com および bob@example.com という二者が相互に公開鍵の交換および信頼の手続きを終えているものとします。

さて、ここまでで、メンバー間で公開鍵による暗号および署名が利用できるようになりました。 個別のファイルに対して暗号化・復号化・署名・検証をコマンドで実行できますが、本稿の趣旨ではないので割愛します。

git-crypt

さて、GnuPGの準備が整ったので、git-crypt の説明にはいっていきます。

まずAlice側でgitリポジトリを作り、暗号化しなくても構わないファイルを配置しておきます。(既存のリポジトリでも構いません)

alice$ mkdir hello-git-crypt
alice$ cd hello-git-crypt
alice$ git init
alice$ echo 'Hello, world!' >public-info.txt
alice$ git add .
alice$ git commit -m 'initial commit'

git-cryptのインストールと、リポジトリに対するメンバーの公開鍵登録

続いて、メンバー各自で git-crypt のインストールを行います。

$ brew install git-crypt

続いてAliceはこの git リポジトリで git-crypt を有効化し、秘密情報にアクセスできるメンバーを登録します。 git crypt init は初回のみでかまいませんが、git crypt add-gpg-user はメンバーが増えるたびに行います。

alice$ git crypt init
alice$ git crypt add-gpg-user alice@example.com
[master c410435] Add 1 git-crypt collaborator
 2 files changed, 3 insertions(+)
 create mode 100644 .git-crypt/.gitattributes
 create mode 100644 .git-crypt/keys/default/0/AAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXX.gpg
alice$ git crypt add-gpg-user bob@example.com
[master 4cdb9ab] Add 1 git-crypt collaborator
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 .git-crypt/keys/default/0/BBBBBBBBBBBBBBBBBBBBBBBBYYYYYYYYYYYYYYYY.gpg

add-gpg-user ではリポジトリの .git-crypt/keys/default/0/ ディレクトリ内に公開鍵を配置して、コミットしています。

秘密情報ファイルの配置

秘密情報ファイルを作成し、それと同時に .gitattributes というファイルを作成します。 このファイルで「private-info.txt は暗号化して管理すべき」という指示をしています。 これら2つのファイルをコミットします。

alice$ echo 'Hello, git-crypt!' >private-info.txt
alice$ echo 'private-info.txt filter=git-crypt diff=git-crypt' >>.gitattributes
alice$ git add .
alice$ git commit -m 'Add secret info'

さて、どうなっているでしょうか。

alice$ cat private-info.txt
Hello, git-crypt!

alice$ git diff HEAD~
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..332bdf2
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+private-info.txt filter=git-crypt diff=git-crypt
diff --git a/private-info.txt b/private-info.txt
new file mode 100644
index 0000000..00ae0dd
--- /dev/null
+++ b/private-info.txt
@@ -0,0 +1 @@
+Hello, git-crypt!

おや、catしてみても、diffを見ても、暗号化しているようには見えません。 しかし実は、aliceによる秘密情報ファイルの操作は、自動的・暗黙的に暗号化して行われるようになっているのです。

他者による秘密情報ファイルの参照

では、このリポジトリを alice が push して、それを bob が clone した場合…。

bob$ git clone https://.../hello-git-crypt.git
bob$ cd hello-git-crypt
bob$ cat public-info.txt
Hello, world!
bob$ cat private-info.txt
GITCRYPT(以下、文字化け)

private-info.txt だけが暗号化されてます。

しかし、以下のコマンドを入力するとパスフレーズ入力のダイアログが現れ、これに正しく応答すると…。

bob$ git crypt unlock
(パスフレーズダイアログに応答)
bob$ cat private-info.txt
Hello, git-crypt!

はい、この通り。以降はaliceと同じように、特に意識することなく秘密情報ファイルは暗号文としてコミットされ、そしてローカル内では平文の状態で操作ができます。

ちなみに、あまり使うことは無いかもしれませんが、下記のコマンドにより、ローカル内でも暗号文状態に戻せます。

bob$ git crypt lock
bob$ cat private-info.txt
GITCRYPT(以下、文字化け)

おまけ

生成した GnuPG の公開鍵および秘密鍵は下記のようにエクスポートできます。バックアップ等でご利用ください。

alice$ gpg --output alice.gpg --export alice@example.com
alice$ gpg --output alice_secret.gpg --export-secret-key alice@example.com

またこれらのコマンドに --armor オプションを追加すると、テキストファイル形式でエクスポートできます。

GitHub にはGnuPGの公開鍵を登録する機能があり、テキスト形式の公開鍵をクリップボード経由で登録しておくと便利です。

alice$ gpg --armor --export alice@example.com | pbcopy
alice$ open https://github.com/settings/gpg/new

GitHubに登録したSSH公開鍵は https://github.com/<username>.keys でアクセスできることは有名ですが、 上記で登録したGnuPGの公開鍵は https://github.com/<username>.gpg でアクセス可能です。

鍵交換に上手く活用できそうですね。

脚注

  1. その他の選択肢もありますが、ここではひとまずGnuPGで考えます。
  2. この辺りには PGP, OpenPGP, GPG という似たようなあれやこれやが飛び交っています。私もPGPとGPGっていう命名には、どうしてこうなったとしか思えません…。これらはざっくりと言ってしまえば、一つの仕様に対する色んな実装です。それぞれが異なったライセンス形態と歴史的経緯を持っていますが、基本的にはだいたい同じことができる、と雑に理解しておくと良いと思います。