Share
Embed
2026-04-14 Jagu'e'r Cloud Native分科会 Terraform Stateにおけるシークレットの平文保存という課題とその解決
Terraform Stateにおけるシークレットの平文保存という課題とその解決
[0ベースから学ぶ、クラウドネイティブの春! - connpass](https://jaguer-cloud-native.connpass.com/event/387350/) で発表した内容
SUZUKI Masashi
April 14, 2026
Transcript
34 slides
-
おまえだれよ 名前: すずきまさし/x:@masasuz/masasuzu 所属: 株式会社スリーシェイク シニアアーキテクト クラウドインフラ何でも屋さん すきなもの: AWS, Google
Cloud, Terraform 2024-2025 AWS Community Builder Cloud Operations 2025 Japan All AWS Certifications Engineers Google Cloud Partner Top Engineer 2026 2 -
TerraformとStateの関係 .tf ファイル リソース定義(コード) Terraform plan / apply で差分を検出・適⽤ ↕
両者を⽐較 Google Cloud 実際のクラウドリソース terraform.tfstate リソースの現在の状態 全属性値を平⽂で記録 読み取り API呼び出し 状態を記録 .tf ファイルが設計図、Stateが現場の台帳 Terraformは両者を比較して差分を検出・適用する Stateにはリソースの全属性値が平文で記録される ← ここが問題 5 -
Stateファイルの中身 実際のstateファイルを見てみると... { "type": "google_secret_manager_secret_version", "name": "api_key", "instances": [ {
"attributes": { "secret": "projects/my-project/secrets/my-api-key", "secret_data": "sk-1234567890abcdef", // ← 平文! "version": "1" }, "sensitive_attributes": [[{ "type": "get_attr", "value": "secret_data" }]] } ] } Secret Managerに保存したはずのシークレットが、stateに平文で入っている ※ sensitive_attributes はCLI表示をマスクするためのメタデータ。state内の値自体は保護しない 6 -
ワークツリーを守っても、stateから漏れる sops provider や google_kms_secret でシークレットを暗号化してリポジトリに保存していても、 Terraformが復号した値はstateに平文で書き込まれる 暗号化された値 sops provider
google_kms_secret 🔒 安全 Terraform Cloud KMS等で復号 plan / apply terraform.tfstate 平⽂で保存! ⚠ ここから漏れる リポジトリ上のシークレット管理に気を配っていても、stateという意外な場所から漏れうる ※ google_kms_secret にはephemeral版が存在しない。 sops provider の sops_file にはephemeralがありま す。 7 -
sensitive = true では不十分 sensitive = true は、 terraform plan
/ apply / output のCLI出力時に値を <sensitive> にマスクするため のフラグ output "api_key" { value = data.google_secret_manager_secret_version.api_key.secret_data sensitive = true } CLI出力はマスクされる: $ terraform output api_key = <sensitive> ここまでは安心。では stateファイルの中身は? 8 -
sensitive = true では不十分(続き) しかし stateファイル内は: "outputs": { "api_key": {
"value": "sk-1234567890abcdef", "sensitive": true } } "sensitive": true のすぐ隣に平文でAPIキーが並んでいる 9 -
Stateが漏洩したら? Stateファイルへの読み取り権 = 全シークレットへのアクセス権 → サービスアカウントキーの窃取、APIキーの不正利用に直結する DBパスワードはPrivate Network内なのでまだマシ。APIキーは外から直接使えてしまう よくある漏洩経路 Gitへの誤コミット
— .gitignore への追加忘れ GitHubで path:**/terraform.tfstate を検索すると...なんかいっぱいありますね 共有ストレージの権限設定ミス — GCS/S3バケットの公開 CI/CDログ — TF_LOG=TRACE でHTTPボディが丸見え stateに書かれないようにしたい。 でもどうやって? Terraform 1.10 / 1.11 で、stateに書かないための仕組みが導入された 10 -
Ephemeral Resourcesとは 一時的なリソース。Stateに一切保存されない。 最高。 Data Source の代わりに、機密情報をGoogle Cloudから取得するときに使用する Terraform 1.10
で導入(2024年11月) plan / apply の実行中のみ存在し、実行後は破棄される Stateファイルにもplanファイルにも書き込まれない 毎回新鮮な値が取得される ユースケース サービスアカウントの一時アクセストークン Secret Managerのシークレット値の読み取り 短命の認証情報全般 12 -
Before: data で取得するとstateに残る # Secret Managerからシークレットを取得 data "google_secret_manager_secret_version" "api_key" {
secret = "my-api-key" } # → stateにシークレットの値が平文で保存される "attributes": { "secret_data": "sk-1234567890abcdef", // ← stateに残る! "secret": "projects/my-project/secrets/my-api-key" } 13 -
After: ephemeral で取得するとstateに残らない # 同じSecret Managerからシークレットを取得 ephemeral "google_secret_manager_secret_version" "api_key" {
secret = "my-api-key" } # → stateには何も保存されない data を ephemeral に変えるだけ。stateにシークレットが残らなくなる 14 -
ただし: ephemeralの値を渡せる場所は限定される stateに保存されない値が通常の引数に流れると、結局stateに保存されてしまう。 そのため、ephemeralの値は以下の場所でしか使えない。 provider ブロックの設定 他の ephemeral ブロック managed
resourceの write-only引数のみ(通常の引数にはNG) provisioner / connection の設定 → リソースに渡すにはwrite-only属性との組み合わせが前提。次章で紹介。 15 -
Google Providerの対応リソース Ephemeral Resource 用途 google_service_account_access_token SAの一時アクセストークン google_service_account_id_token SAのIDトークン google_service_account_jwt
SAのJWT生成 google_service_account_key SAキー情報の取得 google_secret_manager_secret_version Secret Managerのシークレット値 google_client_config Provider設定情報(access_token含む) 6リソース対応(2026年4月 / Provider v7.27.0時点) 16 -
既存コードの修正は実質2行 resource "google_sql_user" "db_user" { name = "app-user" instance =
google_sql_database_instance.main.name # password ではなく password_wo を使う password_wo = var.db_password # stateに保存されない password_wo_version = 1 # 変更時にインクリメント } password → password_wo に変えて、 password_wo_version を追加するだけ 19 -
_wo_version の仕組み Stateに値が保存されない = Terraformは前回と今回でパスワードが変わったか比較できない → 変更を伝える別の手段が必要 → それが _wo_version
_wo_version (整数)はstateに保存される パスワードを変更したら _wo_version をインクリメント Terraformは _wo_version の変更を検知してapplyを実行する 注意: _wo_version をインクリメントし忘れると、パスワードを変えても反映されない 20 -
Before / After: Stateの中身 Before: password を使った場合 "attributes": { "name":
"app-user", "password": "S3cret!P@ssw0rd-12345", // ← 平文で保存 "project": "my-gcp-project-id" } After: password_wo を使った場合 "attributes": { "name": "app-user", "password_wo": null, // ← 保存されない! "password_wo_version": 1, "project": "my-gcp-project-id" } たったこれだけで、パスワードがstateから消える 21 -
Google Providerの対応リソース リソース Write-Only属性 google_sql_user password_wo google_secret_manager_secret_version secret_data_wo google_bigquery_data_transfer_config sensitive_params.secret_access_key_wo
google_compute_vpn_tunnel shared_secret_wo google_alloydb_cluster initial_user.password_wo google_alloydb_user password_wo google_compute_ssl_certificate private_key_wo google_compute_region_ssl_certificate private_key_wo google_monitoring_notification_channel sensitive_labels.*_wo (3属性) 9リソース / 11属性対応(同時点) 22 -
使い分けの目安 Ephemeral Resources Write-only Attributes 方向 読み取り専用 書き込み専用 用途 トークン取得、シークレット参照
パスワード設定、秘密鍵設定 例 SAアクセストークン、Secret Manager Cloud SQLパスワード、SSL秘密鍵 迷ったら: 「値を取得して使う」→ Ephemeral、 「値を設定する」→ Write-only 23 -
シークレット保護の全体像 守りたい場所 手段 Gitリポジトリに機密情報が入り込まない sops provider 等で暗号化 Terraform Stateに機密情報が入り込まない Ephemeral
Resources / Write-only Attributes 今日の話は下段。上段だけでは不十分で、両方やって初めて安全になる 24 -
まとめ 1. Terraform stateにはシークレットが平文で保存される sensitive = true はCLI表示のマスクのみ。stateは守れない 2. Ephemeral
Resources で一時認証情報をstateに残さない Terraform 1.10+ / SA トークンやSecret Manager の値に対応 3. Write-only Attributes でパスワードをstateに残さない Terraform 1.11+ / Cloud SQL, AlloyDB, Secret Manager 等に対応 やること Terraform 1.11+ / Google Provider 7.6+ にアップデートして、 _wo 属性と ephemeral リソースへの移行を始めよう Stateの中身、一度覗いてみてください。思ったより怖いものが見えるかもしれません。 26 -
sensitive vs Ephemeral vs Write-only 比較 観点 sensitive = true
Ephemeral Resources Write-only ( _wo ) Stateへの保存 保存される(平文) 保存されない 保存されない CLI表示 <sensitive> 非表示 (write-only) 変更検知 値の差分で検知 毎回再取得 _wo_version で検知 用途 最低限のマスキング 一時認証情報 パスワード・APIキー Terraformバージョン v0.14+ v1.10+ v1.11+ 29 -
バージョン要件 Terraform本体 機能 必要バージョン sensitive = true v0.14+ Ephemeral Resources
v1.10+ Write-only Attributes v1.11+ Google Cloud Provider 機能 必要バージョン 初のEphemeral Resource(SA系4種) v6.13.0+ 初のWrite-only Attribute v6.23.0+ Secret Manager ephemeral対応 v7.6.0+ client_config ephemeral対応 v7.10.0+ 推奨: Terraform >= 1.11 + Google Provider >= 7.6+ 30 -
多層防御(Defense in Depth) シークレット管理は一つの対策だけでは不十分。組み合わせが重要。 レイヤー 対策 効果 1. リポジトリ sops
provider / google_kms_secret 等で暗 号化 Gitリポジトリに平文シークレットを残 さない 2. Stateソースレベ ル Ephemeral / Write-only シークレットをstateに保存しない 3. State保存時暗号 化 GCS + Cloud KMS (CMEK) stateファイルを暗号化保存 4. アクセス制御 IAM最小権限 + 公開防止 不正アクセスを防止 5. 運用 .gitignore + バージョニング 誤コミット防止 + リカバリ 32 -
シークレット取得方法のephemeral対応状況 方式 stateに残る ephemeral対応 data "sops_file" 残る あり(sops provider v1.3.0+)
data "google_kms_secret" 残る なし(ephemeral版が存在しない) data "google_secret_manager_secret_version" 残る あり(Google Provider v7.6.0+) google_kms_secret を使っている場合は現状だと別の方式を利用したほうが良い 33 -
参考リンク Terraform provider for Google Cloud 7.0 is now GA
(HashiCorp Blog) Terraform 1.10: Ephemeral Values (HashiCorp Blog) Terraform 1.11: Write-only Arguments (HashiCorp Blog) Use ephemeral resources in the Google Cloud provider Use write-only attributes in the Google Cloud provider Sensitive Data in State (HashiCorp) Google Provider 7.0 Upgrade Guide 34