クラウド時代の秘密情報管理 ベストプラクティス 〜2017冬〜

[リクルートライフスタイル Advent Calendar 2017](https://qiita.com/advent-calendar/2017/recruitlifestyle)、25日目を務める @tmshn です。

最近は主に[飲食店検索のためのチャットボット](https://www.hotpepper.jp/smart_kanji/chat/)を作るプロジェクトに参加していますが、チームのアカウントを管理するアカウントおじさんをやったりもしています。

# はじめに

皆さん、チームでの秘密情報ってどうやって管理していますか？

サーバへの SSH 鍵、他チームが提供する API へのアクセストークン、DB にアクセするためのパスワード……私たちの身の回りには、管理しなくてはいけない「秘密情報」がたくさんあります。
個人が管理すべきものは各自が適切に使用・保管すればいいですが、バッチジョブが使うシステムアカウントの認証情報などは、そうはいきません。

今回は、そういったチームでの秘密情報をどうやって管理したらいいか、2017年冬時点で私たちがたどり着いたベストプラクティスをご紹介します。

なお本記事の内容は、AWS や GCP のようなクラウド上でシステムを構築していることを前提にしていますので予めご了承ください。

（昨年は[学会の参加報告](https://qiita.com/tmshn/items/3ccc5d84daa23a98d4be)を書きましたが、今年も全くコードが出てこない長文記事になりました……）

# 何が悩みか

## 1. 誰がどの情報を持っているかをコントロールしづらい

たとえば、あるバッチジョブで使うデータベースのパスワードがあるとします。

最初にバッチを作成した Alice は、そのパスワードを知っています。しかしそれ以外は誰も知らないとしたら、それはシステムが属人化された非常に不健全な状態ですね（繰り返しますが、個人が使う秘密情報は個々人が管理すべきですよ）。

ではやっぱりチーム内で共有すべきだということになり、Alice は同僚たちにその暗号化キーをメールで送信しました。めでたしめでたし？ いえ、こうなるともう、誰が知っていて誰が知らないか制御不能です。
同僚の Bob が異動することになった、Charlie がうっかり送るべきでない人に転送してしまった、など何が起きるか分かりません。

一度インターネットに上がった情報は二度と消せないという箴言もありますが、一度自分の手を離れた情報はどこまで広まるかコントロールできません（もちろん送られるべきでない人に送られてしまっているという事態はすでに重大なセキュリティインシデントですが、そういう最悪な状況も十分ありえるよね、という性悪説に立った話をしています）。

さらに厄介なことに、1つの秘密情報へのアクセス権から、他の情報が芋づる式に分かってしまうことが多々あります。現実的な例でいえば git の push 権限から CI サーバが持つ情報にアクセスできたり、コンテナレジストリの pull 権限からバッチ用イメージに埋め込まれた鍵ファイルを取得できたりなどです。

## 2. どのシステムがどの情報を使っているのか把握しづらい

最初に Alice がバッチジョブで使ったパスワードは、今やチーム全体で様々なバッチで使われています。

あるときそのパスワードのリークが疑われたため、データベースアカウントを再作成し、そのアカウントを使っていた全バッチで新しいパスワードを使うことになりました。……さて、では「そのアカウントを使っていた全バッチ」のリストはすぐに出せますか？ というのが次に問題になることです。

候補が少なければまだ良いですが、数百とかになってくると、もう手に負えません。

また、アカウントを入れ替えたくなるのはリークが疑われたときだけとは限りません。利用場所が増えたことで付与されている権限が肥大化してしまったときなどは、より権限を細分化した複数アカウントに分割したくなるでしょう。

# どう解決するか

まず大前提として、不必要な秘密情報は発行しない、という原則を意識すべきです。特にクラウドプロバイダが提供するサービスを組み合わせて使っている限りは、ほとんど不要になると思います。

たとえば AWS Lambda から DynamoDB にアクセスするためにはロールで十分なので、アクセスキーを発行する必要はありません。GCE VM インスタンスから BigQuery にアクセスするのためのサービスアカウントは直接インスタンスにアタッチできるので、鍵ファイルをダウンロードする必要はありません。

それでもどうしても必要なものをどうするか。そのソリューションが下記の2点です。

## 1. 情報の共有場所をクラウドのストレージサービスに固定する

共有すべき情報は、すべてストレージサービスにアップロードしてチーム内でできるだけオープンに共有してしまいます。そして、他の手段では共有しないようにします。

AWS でいえば S3、GCP でいえば GCS ですね。

そんなところに置いていいのかと一瞬不安になりますが、いずれも下記要件を満たしており、安全性は保証できます。

- サーバサイドでの暗号化（GCS の場合はデフォルトで有効）
- アクセスログの記録
- バケット単位での読み書きの権限設定（S3 はもっと細かく設定できます）

これにより、誰がどの情報にアクセスできるのか・実際にアクセスしたのかが容易に管理できるようになります。

## 2. 情報はこまめに入れ替え、利用時取得を徹底させる

しかし上記のルールだけでは、ストレージからダウンロードした情報を誰かが持っていってしまわないとも限りません。

それを防ぐのが、こまめな入れ替え（ローテーション）です。リークが疑われたときにだけ入れ替えるのではなく、日常的に入れ替えを行うことで情報の寿命を限定します。これにより、利用者に利用時取得を徹底させることができ、また漏洩時のリスクを最小化することができます。

利用時取得というのは、文字通り秘密情報を使う瞬間にストレージから取得するということです。そのため、たとえばバッチ用 Docker イメージに秘密情報を埋め込んだりする必要がなく安全性が高まります。

考え方としては OTP などでよく使われる「30分だけ有効なトークン」と同じですね。さすがに30分毎に入れ替えるのはやりすぎだと思いますが、日次か週次くらいで入れ替えるようにするのが良いと思います。

# おわりに

本記事では「誰がどの情報にアクセスできるのか」「どのシステムがどの情報を利用しているか」という秘密情報管理における関心事を、クラウドストレージでの共有とこまめな入れ替えという2本柱でコントロールできることを紹介しました。

アイデア自体はシンプルなため、正直我々も最初はこんなものがうまくいくのか半信半疑でした。しかし、しばらくこのやり方を運用してみた現在では、その簡単さ・透明性の高さ・そして強力を実感しています。

このアイデアはもともと、Google I/O に参加した同僚が現地で仕入れてきたものでした。でもあとになって気がついたのですが、実は Google Cloud Platform のブログにもほぼ同じものが紹介されていたんです。

- [Google Cloud Platform Japan 公式ブログ: Cloud KMS を使い暗号化キーの管理をクラウドで](https://cloudplatform-jp.googleblog.com/2017/01/managing-encryption-keys-in-the-cloud-introducing-Google-Cloud-Key-Management-Service.html)
    - 秘密情報の管理において、暗号化キーを自分たちで管理しない場合は GCS が最適なソリューションであると紹介されています
- [Google Cloud Platform Japan 公式ブログ: Google Cloud のサービス アカウント キーを安全に管理する ](https://cloudplatform-jp.googleblog.com/2017/08/help-keep-your-Google-Cloud-service-account-keys-safe.html)
    - 秘密情報（ここでは GCP のサービスアカウント）を GCS で管理し、毎日入れ替えるというやり方が提案されています

そんなわけで、2017年冬時点で私たちがたどり着いている、秘密情報管理のベストプラクティスのご紹介でした。
もっと良いやり方や、上記のやり方への改善点をご存じの方。ぜひコメント欄などでご教示ください。

それでは皆さん、ご自愛のうえ、素敵な年末をお過ごしください。