CI/CDとIaCのシークレットは、コードよりもstateとログと長期キーから漏れます。OIDCによるキーレス認証、Vault・SOPS・クラウドSecrets Managerの使い分け、最小権限・ローテーション・監査までを、海外の一次情報をたどりながら日本の現場向けに整理します。
CI/CDとIaCのシークレット管理は、「秘密の値をどこにしまうか」という置き場所の話に見えて、実際には「どこから漏れるか」を先に潰す設計の話です。コードにハードコードしなければ安全、という単純な話では済みません。漏洩の主経路は、コードよりもむしろstate・ジョブログ・長期キーの三つに寄っています。本稿では、この三経路を軸に、OIDCによるキーレス認証、Vault / SOPS / クラウドのSecrets Managerの使い分け、そして最小権限・ローテーション・監査の実務を、海外の一次情報をたどりながら整理します。
01漏れる経路は三つ — state・ログ・長期キー
まず「どこから漏れるか」を三つに分解します。設計の議論はここを共通認識にしないと噛み合いません。
- state / plan ファイル:Terraform / OpenTofu は、DBの初期パスワードやAPIトークンといった機微な値を、リソース属性やメタデータの一部として
stateに平文で保存します。HashiCorpの公式ドキュメントも、ローカル実行では state が平文ファイルとして保存され、設定に書いた秘密の値がそこに含まれると明記しています。 - ジョブログ:CIのログにシークレットがそのまま出力される事故です。GitLabのマスク機能は値を
[MASKED]に置換しますが、公式ドキュメント自身が「マスクは悪意ある利用者による値の取得を防ぐ確実な手段ではない」と注意喚起しています。envやprintenvで回避されうるため、マスクは最後の防波堤であって主対策ではありません。 - 長期キー:CIに登録した長寿命のクラウドアクセスキー。これが最も広く使われ、最も漏れると被害が大きい経路です。ログやstateから漏れた値も、結局はこの長期キーであることが多い。
02長期キーを捨てる — OIDCによるキーレス認証
三経路のうち、被害が最も大きい長期キーは、CI/CDとクラウドの間のOIDC連携で根本的に無くせます。GitHub Actionsの公式ドキュメントは、ワークフローが実行のたびにGitHubのOIDCプロバイダから発行される短命トークン(発行者は https://token.actions.githubusercontent.com)をクラウド側に提示し、短命のアクセストークンを直接受け取る仕組みを説明しています。GitHubに長期のクラウド認証情報を保存しない、いわゆる「No cloud secrets」の構成です。
- GitHub側では、ワークフローに
permissions: id-token: writeを付与してOIDCトークンの取得を許可します。 - クラウド側(例:AWS)では、GitHubのIssuerを信頼するOIDCプロバイダを作り、IAMロールの信頼ポリシーで
sub(どのリポジトリ・ブランチか)とaudクレームを条件に絞ります。 - 結果として、CIに保存する長期アクセスキーはゼロになり、ローテーション対象そのものが消えます。
sub 条件は必ずリポジトリとブランチ(環境)まで絞り込んでください。repo:org/* のようなワイルドカードは、フォークや別ブランチからの意図しないロール引き受けを許す典型的な設定ミスです。ここを緩くするとキーレス化のメリットが一気に崩れます。OIDCへの移行は、IaCのパイプライン設計とも密接に関わります。パイプライン全体の組み方はIaCのCI/CDパイプライン設計、複数アカウントにまたがる権限の統制はマルチアカウント統制もあわせてご覧ください。
03state / plan の暗号化とephemeral値
OIDCで長期キーを消しても、stateに残る機微値は別問題です。ここは二段構えで対処します。
第一に、そもそもstateに残さない。HashiCorpは Terraform 1.10 以降で ephemeral 値(ephemeral変数・ephemeralブロック・write-only引数)を導入し、実行時には利用できるがstateやplanには一切書き込まない値として扱えるようにしています。DBパスワードのような「渡すだけで保存不要」な値は、まずこれで state から追い出せないか検討します。OpenTofuでも同等の機能が提供されているため、フォーク側を採用している現場は公式で対応状況を要確認です。
第二に、残る分は暗号化と隔離。stateはリモートバックエンドに置き、保存時暗号化を効かせます。S3バックエンドなら encrypt オプション、GCSなら顧客管理鍵(CMEK)といった具合に、暗号化方式はバックエンド依存なので公式で要確認です。stateへのアクセスは最小権限に絞り、監査ログを取る。stateファイルの詳しい扱いはIaCのstate管理で掘り下げています。
sensitive = true はCLI出力とログでの表示を伏せるだけで、state内では平文のままです。「sensitiveにしたから安全」という誤解は監査で必ず突かれます。表示制御(sensitive)と保存制御(ephemeral・暗号化)は別レイヤだと切り分けてください。04秘密の置き場所 — Vault / SOPS / クラウドの使い分け
ephemeralやOIDCで減らしきれない「保管が必要な秘密」については、保管先を選びます。代表的な三つを、性格の違いで整理します。
- HashiCorp Vault(動的シークレット):クライアントの要求時にオンデマンドで生成され、TTLで失効する短命・一意のクレデンシャルを扱えます。公式は、デプロイパイプラインやTerraform実行のような時間制限のあるワークフローに適し、読み取られるまで存在しないため漏洩リスクを大きく下げられると説明しています。DB・AWS・GCP等のシークレットエンジンで、CI実行のたびに使い捨ての認証情報を発行する使い方が本命です。
- クラウドのSecrets Manager(例:AWS Secrets Manager):クラウド内で完結する運用に最適で、後述の自動ローテーションが最大の強みです。マネージドローテーション(Lambda不要)や、Lambdaによるカスタムローテーションに対応します。
- SOPS(Secrets OPerationS):CNCFのSandboxプロジェクトで、YAML / JSON / ENV / INI等のファイルの値だけをAWS KMS・GCP KMS・Azure Key Vault・age・PGPで暗号化します。キー名は平文のまま残るためGit上でdiffが読め、暗号化した秘密をそのままリポジトリにコミットして通常のレビュー運用に乗せられるのが特徴です。GitOps構成と相性が良く、鍵管理はKMSか、より簡潔なageを推奨とされています。
実務では三択ではなく併用になります。例えば「CIのDB接続はVaultの動的シークレット、アプリ実行時のAPIキーはクラウドのSecrets Manager、GitOpsで配布するKubernetes SecretはSOPS」という組み合わせは自然です。SOPSとGitOpsの組み合わせはGitOps(Argo CD / Flux)の文脈でも要になります。
05最小権限 — 権限を絞ってこそキーレスが効く
OIDCで長期キーを消し、Vaultで短命化しても、引き受けるロールの権限が広ければ意味が薄れます。最小権限の基本は変わりません。
- CIロールは環境ごと・用途ごとに分割する。本番へのapply権限とdev環境の権限を同一ロールに載せない。
- OIDC信頼ポリシーの
subは、リポジトリ・ブランチ・環境(GitHubのenvironment)まで絞る。 - Vaultのポリシーやクラウドのシークレットリソースポリシーで、read対象のシークレットを必要最小限に限定する。
「最小権限は理想論で運用が回らない」という声は現場で必ず出ます。その落としどころは最小権限の現実解で整理していますので、あわせてご覧ください。
06ローテーションと監査 — 事故を前提に短く回す
どれだけ絞っても漏洩ゼロは保証できません。だからこそローテーションで「漏れても短時間で失効する」状態を作り、監査で「誰がいつ何を読んだか」を追える状態を作ります。
- ローテーション:AWS Secrets Managerは、RDS等ではLambda不要のマネージドローテーション、それ以外はLambdaによるカスタムローテーションに対応します。スケジュールは
cron()/rate()式とローテーションウィンドウで設定し、最短4時間間隔まで指定できると公式は説明しています(間隔や上限はバージョン依存のため公式で要確認)。 - そもそも回さない設計:Vaultの動的シークレットやOIDCの短命トークンは、TTLで自動失効するため「ローテーションという運用作業」自体を減らせます。ローテーションの手間が重い現場ほど、短命化への投資効果が大きい。
- 監査:シークレットの読み取り・更新・ローテーションのイベントは、クラウドの監査ログ(CloudTrail等)やVaultの監査デバイスに残し、レビュー対象にします。マスクは前述の通り確実ではないため、ログ側だけでなく「シークレットストアへのアクセスログ」を主たる証跡にするのが堅い設計です。
—まとめ
CI/CDとIaCのシークレット管理は、単一のツール導入では完結しません。漏洩経路をstate・ログ・長期キーの三つに分けて捉え、それぞれに手当てを当てるのが実務の勘所です。
- 長期キーはOIDCキーレス認証で無くす。信頼ポリシーの
subはリポジトリ・ブランチまで絞る。 - stateはephemeral値でそもそも残さず、残る分は暗号化と最小権限で守る。
sensitiveは表示制御にすぎない。 - 保管が必要な秘密は、短命性ならVault、クラウド完結ならSecrets Manager、Git管理ならSOPS、と性格で使い分ける(併用が前提)。
- 最小権限で被害範囲を絞り、ローテーションと監査で「漏れても短く・追える」状態を作る。
いずれもバージョンや料金に依存する挙動は公式で要確認です。自社のパイプラインとIaC構成を前提に、どの経路から手を付けるべきかはお問い合わせください。
—参考(一次情報)
- OpenID Connect — GitHub Docs
- Manage sensitive data in your configuration — Terraform / HashiCorp Developer
- Understand static and dynamic secrets — Vault / HashiCorp Developer
- SOPS: Secrets OPerationS — Documentation
- Rotate AWS Secrets Manager secrets — AWS Secrets Manager
- CI/CD variables — GitLab Docs