はじめに
先日、「git-secretsはじめました」というエントリを公開しました。git-secretsは、APIキーなど秘密情報を含んだファイルがgitリポジトリへ誤ってcommitされないようにするためのツールです。一方で、非常に名前が似通った git-secret (sなし) というものが存在します。このgit-secret (sなし) は、git-secrets (sあり) 1 とは全く別のアプローチ、すなわちgitリポジトリに安全に秘密情報をアップロードしちゃおうという思想で開発が進められているbashツールです。本エントリではこの git-secret をざっくり紹介します。
Q: git-secretは何ができる?
A: 指定ファイルを暗号化、許可した人のみが復号できるようにすることで、公開gitリポジトリを通じて秘密情報を共有できます。
そのためにgit-secretがやってることは割と単純で、以下に簡単にまとめます。
- まず暗号化したいファイルを指定、そのファイル自身は
.gitignore
に追加することで2度とリポジトリにcommitされないようにする。 - 暗号化の設定として、相手のGPG公開鍵をインポート。
- GPGによる公開鍵暗号化。
- 共有先の情報と一緒に暗号化済ファイルを
git add
してcommit/push。
「漏洩防止のための水際防止策」であるところのgit-secrets (sあり) とは異なり、「水際防止策を講じるよりも前に考慮すべき点を一歩突っ込んだ形で実現するためのツール」だということがなんとなくおわかりになるかと思います。すなわち、「gitに秘密情報の入ったファイルをcommitしない」ということを暗号化を用いて実現しています。
準備
実際に使ってみて動作を確かめるため、準備を進めます。ここではmacOS Xあるいは*nixを利用することとします。また、git-secretはbashを前提としたツールですので、bashの利用を仮定しています。
GPGの導入
git-secretは GnuPG (GPG) を利用して暗号化・復号を行います。そのため、GPGが導入されていない場合は、その準備から必要になります。GPGそのものについての説明はここでは割愛します。QiitaにはGPGについての良エントリがありますので、そちらを参照してください。2
GPG Suiteのインストール
macOS Xの場合は、GPG Suite を導入しましょう。GPG Suiteの中に含まれるGPG Mailは不要であれば削除しても良いでしょう。homebrewでの導入も可能です。
$ brew install gnupg
*nixの場合は、お好きなパッケージ管理ツールないしソースからインストールしてください。Debian/Ubuntuの場合は、以下でインストール可能です。
$ sudo apt-get install gnupg
GPG Keyの生成
続いて、自分のGPG公開鍵・秘密鍵ペアを生成します。コンソールを開き、gpg --gen-key
コマンドを実行することで、以下のように対話型でGPG鍵ペアを生成できます。 このとき指定するメールアドレスがgitのuser.email
に指定するものと同じものとなるようにしてください。 これは、git-secretにおいて、user.email
がデフォルトでアクセス権限を与える相手 = 自分という設定となっているためです。GPG Suiteを導入したmacOS XではGPG Keychain.appからGUI経由でも生成できます。3
$ gpg --gen-key
gpg (GnuPG/MacGPG2) 2.2.10; Copyright (C) 2018 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Note: Use "gpg --full-generate-key" for a full featured key generation dialog.
GnuPG needs to construct a user ID to identify your key.
Real name: Test User
Email address: xxxx@xxxxxx.org
You selected this USER-ID:
"Test User <xxxx@xxxxxx.org>"
Change (N)ame, (E)mail, or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 028D26D9DF49446A marked as ultimately trusted
gpg: revocation certificate stored as '/Users/xxx/.gnupg/openpgp-revocs.d/041E87E64316B3685E352B0B028D26D9DF49446A.rev'
public and secret key created and signed.
pub rsa2048 2018-10-25 [SC] [expires: 2020-10-24]
041E87E64316B3685E352B0B028D26D9DF49446A
uid Test User <xxxx@xxxxxx.org>
sub rsa2048 2018-10-25 [E] [expires: 2020-10-24]
gpg --list-keys
で鍵が正しくインポートされているか確認してみましょう。
$ gpg --list-keys
/Users/xxx/.gnupg/pubring.kbx
-----------------------------
pub rsa2048 2018-10-25 [SC] [expires: 2020-10-24]
041E87E64316B3685E352B0B028D26D9DF49446A
uid Test User <xxxx@xxxxxx.org>
sub rsa2048 2018-10-25 [E] [expires: 2020-10-24]
上記の状態になればひとまずGPGの準備完了です。
git-secretのインストール
macOS Xの場合、homebrewからインストール可能です。
$ brew install git-secret
それ以外の場合、ソースからインストールが可能です。aptやrpmにも対応しています。4
$ git clone https://github.com/sobolevn/git-secret.git
$ cd git-secret && make build
$ PREFIX="/usr/local" make install
簡単な使い方
まずは、自分自身だけに秘密情報へのアクセスを許す例を使って、簡単に使い方を紹介しましょう。テスト用のディレクトリ./git-secret
にgitリポジトリを作って説明します。
$ mkdir git-secret
$ cd git-secret
$ git init
まずは git secret init
コマンドにより.gitsecret
ディレクトリを生成します。
$ git secret init
'/Users/xxx/git-secret/.gitsecret/' created.
cleaning up...
.gitsecret
ディレクトリは、暗号化対象ファイルの設定や、復号できる相手の公開鍵情報などが格納されます。これらをアクセスを許す相手と共有しないとデータが復号できないため、git add
します。セキュリティ上、.gitsecret
は公開しても実際に保護したいファイルのセキュリティに影響を与えませんが、暗号化対象ファイルの名前は秘匿されないので注意しましょう。
$ git add .gitsecret
まずは必ず自分だけは復号できるよう、自分のメールアドレス (この例ではxxx@xxxxxx.org
) に対してアクセス権限を与えます。このとき、gitのuser.email
に自分のメールアドレスが正しく指定されていることを確認してください。
$ git secret tell -m
gpg: keybox '/Users/xxx/git-secret/.gitsecret/keys/pubring.kbx' created
gpg: /Users/xxx/git-secret/.gitsecret/keys/trustdb.gpg: trustdb created
done. xxx@xxxxxx.org added as someone who know(s) the secret.
cleaning up...
このリポジトリに設定されたgit-secretにおいて、誰がアクセスを許可されているのか調べるにはgit secret whoknows
コマンドを実行します。
$ git secret whoknows
xxx@xxxxxx.org
自分だけがアクセス許可されていることがわかります。
続いて、秘密情報を入れたファイルを生成して、git-secretに秘匿するように設定します。secret.txt
を生成して、.gitignore
に追加、git secret add
でgit-secretへ設定します。このとき、.gitignore
に登録しないままだと、後の暗号化時にエラーが出ます。
$ echo "this is my secret" > secret.txt
$ echo "secret.txt" > .gitignore
$ git secret add secret.txt
1 item(s) added.
git secret hide
コマンドを実行すると、git secret add
コマンドで設定されたファイルが暗号化され、拡張子.secret
のファイルが生成されます。
$ git secret hide
done. all 1 files are hidden.
$ ls
secret.txt secret.txt.secret
このときgitのリモートリポジトリを通じて共有するのは、.gitsecret
ディレクトリに加えて、暗号化されたファイル本体である拡張子.secret
のファイルとなります。このため、secret.txt.secret
をgit add
し、commitしてみます。あとはよしなにリモートリポジトリを設定してpushしてみてください。
$ git add secret.txt.secret .gitsecret
$ git commit -m 'initial secret commit'
$ git remote add origin https://github.com/xxxxxx/x.......git
$ git push -u origin master
今度は、pushしたリモートリポジトリから別のディレクトリへgit clone
して、自分がsecret.txt
を得られるか、試してみましょう。secret.txt.secret
は、あんまり確認したことにはならないですが、cat
で中身を見ても元のテキストでない暗号文であることがわかります。
$ mkdir -p git-secret-clone
$ git clone https://github.com/xxxxxx/x.......git git-secret-clone
$ cd git-secret-clone
$ ls
. .. .git .gitignore .gitsecret secret.txt.secret
$ cat secret.txt.secret
secret.txt
へ復号するには、git secret reveal
コマンドを実行します。これにより、git-secretに設定されたすべての暗号化ファイルについて、自分のgpg公開鍵に対応する秘密鍵で復号処理が実行されます。
$ git secret reveal
done. all 1 files are revealed.
$ ls
. .. .git .gitignore .gitsecret secret.txt secret.txt.secret
$ cat secret.txt
this is my secret
GPG鍵ペアを生成したときに設定したパスフレーズの入力を求められ、無事に復号できました。
続いて、せっかくのgitリポジトリですので秘密情報のファイルを更新してcommitしてみましょう。秘密情報ファイルを更新した場合、改めてgit secret hide
を実行して再暗号化、暗号化ファイルの方のアップデートを行ってからcommitをすることになります。
$ echo 'updated' >> secret.txt
$ cat secret.txt
this is my secret
updated
$ git commit -a -m 'updated'
[master 5631f69] updated
1 file changed, 0 insertions(+), 0 deletions(-)
rewrite secret.txt.secret (100%)
$ git push
元のディレクトリに戻ってgit pull
、git secret reveal
をすることで、更新された秘密情報ファイルもリモートリポジトリ経由で取得できていることがわかります。
$ cd ../git-secret
$ git pull
remote: Counting objects: 5, done
remote: Finding sources: 100% (3/3)
remote: Getting sizes: 100% (3/3)
remote: Total 3 (delta 1), reused 3 (delta 1)
Unpacking objects: 100% (3/3), done.
From https://github.com/xxxxxx/x.......
7dea230..5631f69 master -> origin/master
Updating 7dea230..5631f69
Fast-forward
secret.txt.secret | Bin 1409 -> 1416 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
$ git secret reveal
File '/Users/xxx/git-secret/secret.txt' exists. Overwrite? (y/N) y
done. all 1 files are revealed.
$ cat secret.txt
this is my secret
updated
gitリポジトリを介した秘密情報のやり取りについてイメージはつかめましたでしょうか? 次の節では、この節で作ったgit-secret
ディレクトリとリポジトリを複数ユーザで利用することを想定し、秘密情報へアクセスできるユーザを追加したり削除したりする、もうちょっと突っ込んだ使い方について紹介します。
複数ユーザでの秘密情報の共有
アクセス可能ユーザの追加
アクセス権限を与えたい相手から、メールなどでその相手のGPG公開鍵を受け取ります。その鍵を適当な名前のファイルにして保存し、自分のローカルgpg鍵リポジトリ ~/.gnupg/
へインポートしましょう。追加したい相手全員に対して鍵を取得し、この作業を繰り返します。
$ gpg --import member_public_key.pub
gpg --list-keys
を実行すると自分の鍵に加えてその相手の鍵が正しくインポートされていることがわかります。
$ gpg --list-keys
/Users/xxx/.gnupg/pubring.kbx
-----------------------------
pub rsa2048 2018-10-25 [SC] [expires: 2020-10-24]
041E87E64316B3685E352B0B028D26D9DF49446A
uid Test User <xxxx@xxxxxx.org>
sub rsa2048 2018-10-25 [E] [expires: 2020-10-24]
pub rsa4096 2018-10-25 [SC] [expires: 2022-10-25]
DE22B0EC5E6A5B7B0E6D4FF871AF954BA86FDEED
uid [ unknown] Test User2 <yyyy@yyyy.org>
sub rsa4096 2018-10-25 [E] [expires: 2022-10-25]
続いて、git-secretを設定したgitリポジトリのディレクトリで、当該のユーザのメールアドレス (この例だとyyyy@yyyy.org) に対してアクセス権限を与えてみましょう。
$ git secret tell yyyy@yyyy.org
done. yyyy@yyyy.org added as someone who know(s) the secret.
$ git secret whoknows
xxxx@xxxxxx.org
yyyy@yyyy.org
上記のように、2人に対してアクセス権限が与えられたことがわかりました。このアクセス権限の追加処理を、許可したいユーザごとに繰り返します。
アクセス権限を持つユーザは追加できましたが、この段階では暗号化されたファイルの方はまだ更新されておらず、権限を持つユーザ全員がアクセス可能なものにはなっていません。そのため、改めてgit secret hide
コマンドを実行して再暗号化を行う必要があります。
$ git secret hide
done. all 1 files are hidden.
$ git commit -a -m 'added yyyy'
[master cf53b2d] added yyyy
3 files changed, 0 insertions(+), 0 deletions(-)
rewrite secret.txt.secret (100%)
$ git push
上記の処理で、yyyy@yyyy.orgさんもこのリモートリポジトリをcloneすることでsecret.txt
を復号して取得できるようになりました。
ユーザの失効
今度はyyyy@yyyy.orgさんからアクセス権限を剥奪してみます。
$ git secret killperson yyyy@yyyy.org
removed keys.
now [yyyy@yyyy.org] do not have an access to the repository.
make sure to hide the existing secrets again.
$ git secret whoknows
xxxx@xxxxxx.org
この段階では、まだyyyy@yyyy.orgさんは暗号化ファイルsecret.txt.secret
の復号が可能です。なので、アクセス権限の付与のときと同様に、git secret hide
コマンドで再暗号化し、リモートリポジトリのファイルも二度とアクセスできないようにします。
$ git secret hide
done. all 1 files are hidden.
$ git commit -a -m 'revoked yyyy'
[master ec49af1] revoked yyyy
2 files changed, 0 insertions(+), 0 deletions(-)
rewrite secret.txt.secret (100%)
$ git push
注意しなければいけないことは、過去のcommitには再暗号化前のファイルが入っているということです。このため、失効したユーザもcommitを遡ればファイルを復号することが可能です。このため、ユーザを失効した後は秘密情報そのものも更新しておくことが重要です。アクセスキーなどは再発行してファイルに上書きして更新しておきましょう。
Q&A
Q: git-secretの暗号化設定対象ファイル一覧はどう見るの?
A: git secret list
コマンドで一覧できます。
$ git secret list
secret.txt
Q: git-secretの暗号化設定対象からの解除はどうするのか?
A: git secret remove <file name>
で可能です。
$ git secret remove secret.txt
secret.txt -> secret.txt -> /Users/xxxx/git-secret/secret.txt
removed from index.
ensure that files: [secret.txt] are now not ignored.
cleaning up...
removeした後に再暗号化は不要ですが、gitリポジトリで生ファイルを共有をするならば.gitignore
の編集などを行いましょう。
Q: git-secrets (sあり) と併用できるの?
A: できます。git-secrets (sあり) の検査で引っかかるような秘密情報が入ったファイルは、必ず暗号化してから git commit
するというようなルール付けをすると、ヒューマンエラーも減るしgitを通じた秘密情報の共有もできるし良いのでは。
まとめ
GPGがとてもめんどくさいが何よりも気になるところではありますが、git-secretを利用することで、サーバのアクセスキーなどの秘密情報を「外に出さない」という運用から「安全に共有する」という運用に切り替えることができるようになります。アクセスキーの共有がし辛いなど、利便性が損なわれている場合には導入を検討してみてもいいと思います。
また、git-secretの現状バージョンでは
-
git secret tell
後などに必要となるgit secret hide
による再暗号化 -
git secret add
したファイルの.gitignore
への追加
などの煩雑な処理が必要です。これら、gitのhooksを編集することでgit commit
実行時などに自動的に行われるように対応すればgit-secretのめんどくさい手順が大幅にが大きく向上するでしょう。このあたりはまずは自前で編集してしばらく運用してみるつもりです。
参考
- How to keep your repository’s sensitive data secure using git-secret (Medium)
- git-secret (official)
おまけ:アクセス制御について
実際の使い方を紹介したところで、最後におまけとしてgit-secret (ひいてはGPG)の思想をなんとなく理解するために重要な、データのアクセス制御の概念を紹介しておきましょう。
情報セキュリティにおけるアクセス制御には、ざっくり2種類のアプローチがあります。1つは「セッションベースのアクセス制御」、もう1つは「暗号化ベースのアクセス制御」です。git-secretは、後者の「暗号化ベースのアクセス制御」の概念を利用したアプリケーションです。
セッションベースのアクセス制御
セッションベースのアクセス制御は非常に理解しやすいと思います。これは、ID/Passwordなどによる「認証」を基礎としています。認証により、アクセスを許可する主体 (例えばデータのオーナー) が
- 認証を要求してきた相手は誰なのか
- その相手にどこまでアクセスを許していいのか
を確認した上で、相手と安全かつ一時的な「セッション」を構築します。そのセッションを通じた場合のみ、データへのアクセスが許されるという方法です。ユーザ管理が必要なWebサービスなどは、通常こちらの考えでアクセス制御がなされています。もっと言っちゃえば、SSL/TLSなんかはこの考えで作られています。
暗号化ベースのアクセス制御
こちらは、セッションベースのアクセス制御とは全く異なる思想の方法です。非常に端的に言えば、暗号化ベースのアクセス制御とは、アクセスを許可する主体が「アクセスを許したい相手だけが復号できるように適切に暗号化した上で、暗号化データを配布してしまおう」という方法です。これは言い換えれば、アクセス許可を与えられた人以外に復号されることがなければ、暗号化データそのものは公開してしまっても構わないという考え方です。データそのものがわかんなきゃいいでしょ、ってことですね。5
ご理解の通り、適切に暗号化を行った上でgitリポジトリで全世界に公開してしまおうというgit-secretは、完全にこの思想に則ったアプリケーションです。その他、メディアデータは暗号化してContent Delivery Network (CDN) で公開・配布してしまって、復号するための権限として復号鍵を購入するようなのDRMのサービスはこちらの考えで作られていると言ってもよいでしょう。
それぞれのpros/cons
それぞれ非常に簡単に以下にまとめてみます。
セッションベースのアクセス制御
- pros:
- アクセスユーザの追加、失効、権限(アクセス範囲)変更が容易。
- cons:
- アクセスユーザ管理、認証、セッション管理のインフラ整備が比較的大変。
- アクセス制御するデータが1ファイルであったとしても、アクセスユーザ数分のセッション構築・帯域確保が必要。
暗号化ベースのアクセス制御
- pros:
- 各ユーザの公開鍵を用いて、(適切な暗号アルゴリズムにより) データを暗号化するのみでアクセス制御が可能。
- データを復号するために必要なのは、全ユーザで同じ暗号化データとなり、データ再利用性が高い。6
- cons:
- 一度配信してしまったデータに対しては、アクセスユーザの追加、失効、権限変更が困難。7
どちらがいい悪いというものではなく、一長一短なのは見てお分かりになる通りです。利用シーンやアクセス制御したいデータに応じて使い分けることが重要ですね。
-
git-secretsとgit-secret、名前が非常にややこしいため、強調して区別したいときには「sなし」と「sあり」と記載します。 ↩
-
New → 名前・パスワード入力 → Generate で生成。 ↩
-
ただし、法制度と技術は乖離しているので、一概に適用していい考えかどうかはその利用形態に応じて変わります。例えば経済産業省の個人情報保護ガイドラインの解釈では、数年前まで個人情報は暗号化していても個人情報という扱いで、暗号化された個人情報データが外部に出ること=個人情報漏洩とされていました。ちょっとあたまおかしい。 ↩
-
ただし、暗号アルゴリズムや暗号の利用方法次第です。最悪の場合、各ユーザごとに別の暗号化データを用意することになります。 ↩
-
前述したように、過去のcommitは失効したユーザでも依然アクセス可能なことなど、追加・失効・変更などが未来のデータに対してのみしか有効ではないことを意味します。 ↩