シークレットの保管場所:設定ファイルからVaultへ

新しいアプリケーションをセットアップしているとします。データベースの認証情報、APIキー、サーバーアドレスを.envファイルに書き込みます。自分のマシンでは問題なく動きます。チームメイトも同じ環境を再現できるように、そのファイルをGitにコミットします。数ヶ月後、アプリケーションは本番環境で稼働しています。そして誰かが誤ってその.envファイルを公開リポジトリにプッシュしてしまいます。数分後には、本番データベースのパスワードがインターネット上に流出しています。

このシナリオは珍しくありません。シークレットを保存する最も簡単な方法が、同時に最も危険な方法だからです。設定ファイルにシークレットを保存するところから、専用のシークレット管理ツールを使うようになるまでの道のりは、どのチームも通る道であり、多くの場合、痛い経験をしてから学びます。

設定ファイルの問題点

アプリケーションの作り方を学び始めた頃は、すべてを1つの設定ファイルにまとめるのが自然に感じられます。.envconfig.jsonapplication.propertiesといったファイルは、機密性の低いサーバーアドレスと、極めて機密性の高いデータベースパスワードが混在する場所になります。ファイル全体がGitに入り、リポジトリにアクセスできる人は誰でもすべてを見ることができます。

この方法は、あなたが唯一の開発者で、アプリケーションが自分のラップトップで動作している間は問題なく機能します。しかし、チームが大きくなったり、アプリケーションが実際のユーザーに使われるようになると、問題が表面化します。リポジトリをクローンするすべての開発者が本番パスワードを見ることができます。たとえリポジトリがプライベートであっても、シークレットはGitの履歴に永久に保存されます。最新のコミットからファイルを削除しても、古いコミットからは削除されません。シークレットはそこに永久に残り、Gitの履歴を閲覧する方法を知っている人なら誰でもアクセスできます。

多くのチームが次に取るステップは、シークレットを通常の設定から分離することです。設定ファイルはGitに残しますが、機密性の高い値はDB_PASSWORD=changemeのようなプレースホルダーに置き換えます。実際の値は別の場所、つまりコミットされないファイル、サーバー上の環境変数、チャットで共有されるドキュメントなどに保存します。これはGitにシークレットを保存するよりはましですが、新たな問題を引き起こします。誰がシークレットにアクセスしたかの記録がありません。パスワードをローテーションするには、複数の場所で手動で更新する必要があります。シークレットファイルが紛失したり破損したりした場合、復元できる管理されたバックアップはありません。

専用シークレットストアが変えること

複数のアプリケーションと環境を扱うチームは、やがてシークレット専用に作られたツールを探すようになります。Vault、AWS Secrets Manager、Azure Key Vault、GCP Secret Managerは、機密データを適切に処理するように設計されています。シークレットはもはやディスク上のファイルではありません。アプリケーションがAPIを通じて取得するオブジェクトになります。

ファイルからシークレットストアへの移行は、3つの根本的なことを変えます。

以下は、HashiCorp Vaultを使用してデータベースパスワードを保存および取得する簡単な例です。

# シークレットを保存
vault kv put secret/myapp DB_PASSWORD=supersecret

# シークレットを取得
vault kv get secret/myapp

# 出力:
# ====== Data ======
# Key              Value
# ---              -----
# DB_PASSWORD      supersecret

アクセス制御。 設定ファイルの場合、ファイルを読める人なら誰でもシークレットを見ることができます。Vaultの場合、どのアプリケーションやパイプラインがどのシークレットにアクセスできるかを定義します。ステージング環境のCIパイプラインはステージングの認証情報を取得できますが、本番のシークレットには触れられません。このような細かい制御はファイルでは不可能です。

監査証跡。 ファイルは誰が読んだかを記録しません。Vaultはすべてのアクセス要求を記録します。誰が、どのシークレットを、いつ要求したかです。シークレットが漏洩した場合、インシデントの前後にどのアプリケーションやユーザーがアクセスしたかを追跡できます。

保存時と転送時の暗号化。 設定ファイルはシークレットをディスクに平文で保存します。Vaultはシークレットを保存時とネットワーク経由での送信時に暗号化します。たとえ誰かが基盤となるストレージにアクセスできたとしても、暗号化キーがなければシークレットを読むことはできません。

Vaultを使用する運用コスト

シークレット管理ツールは無料ではなく、そのコストは金銭的なものだけではありません。チームはツールの操作方法を学ぶ必要があります。Vaultがダウンすると、アプリケーションはシークレットを取得できなくなり、完全に停止する可能性があります。Vaultの障害が本番システムをダウンさせないように、高可用性、キャッシュ、またはフォールバックメカニズムの戦略が必要です。

クラウド管理型のシークレットストアは運用負荷を軽減しますが、リクエストごとまたはシークレットごとの課金が発生します。1分間に数千回のシークレットリクエストを行うチームは、そのコストが持続可能かどうかを検討する必要があります。Vaultのようなセルフホスト型のオプションはより多くの制御を提供しますが、維持、アップグレード、セキュリティ確保に専任の労力が必要です。

チームに合った選択をする

シークレットをどこに保存するかについて、唯一の正解はありません。適切な選択は、チームの規模、アプリケーションの数、リスク許容度によって異なります。

以下のフローチャートは、現在の状況にどのアプローチが適しているかを判断するのに役立ちます。

flowchart TD A[開始: アプリと開発者の数は?] --> B{チーム規模と複雑さ} B -->|小規模チーム、1アプリ| C[Gitにコミットしない .env ファイルを使用] B -->|成長中のチーム、複数アプリ| D[CIの環境変数を使用] B -->|大規模チーム、規制あり| E[専用Vault(例: HashiCorp Vault)を使用] C --> F[リスク: 手動ローテーション、監査なし] D --> G[改善: 集中設定、ある程度の監査] E --> H[最適: アクセス制御、監査、暗号化]

1つのアプリケーションと少数の環境を持つ小規模チームは、Gitにコミットしない別のファイルを使用できます。ただし、全員がリスクを理解していることが条件です。重要なのは、デフォルトでそうなるのではなく、意識的に選択することです。

複数のアプリケーション、複数の環境、複数の開発者がいるチームは、専用のシークレットストアの恩恵を受けるでしょう。セットアップのオーバーヘッドは、すべての環境で認証情報をローテーションする必要が生じたとき、または後で漏洩したシークレットに誰がアクセスしたかを調査する必要が生じたときに、最初の一度で元が取れます。

重要な原則は一貫性です。どの方法を選んでも、それを統一して適用してください。シークレットをファイル、環境変数、Vaultに混在させるアプローチは混乱を招き、偶発的な露出の可能性を高めます。

シークレットストレージの実践的チェックリスト

  • シークレットは通常の設定から分離されていますか?
  • シークレットはバージョン管理から除外されていますか(.gitignoreを確認)?
  • アプリケーションコードを変更せずに認証情報をローテーションできますか?
  • 各シークレットにアクセスできるアプリケーションとパイプラインを把握していますか?
  • 誰がいつシークレットにアクセスしたかを示すログがありますか?
  • シークレットストアがダウンした場合、アプリケーションは起動できるか、グレースフルに失敗しますか?

次に来ること

シークレットをどこに保存するかを知ることは、作業の半分に過ぎません。次の問題は、パイプラインがシークレットをパイプライン設定自体に保存せずに、どのように取得するかです。シークレットをログに出力したり、すべてのステップから見える環境変数に保存したり、ワークスペースファイルにキャッシュしたりするCI/CDパイプラインは、Gitにコミットされた設定ファイルと何ら変わりません。保存方法も重要ですが、シークレットがデリバリープロセスをどのように流れるかも同様に重要です。