0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Terraform×AWS】パスワードをStateに残さない、機密情報の扱い方

0
Posted at

Terraformでデータベースのパスワードを扱うとき、sensitive = true を付けておけば安全だと思っていないでしょうか。実は sensitive は画面への表示を隠すだけで、値そのものはStateファイルに平文で保存されます。

この記事では、TerraformでパスワードやAPIキーなどの機密情報を扱うときに、どこに値が残るのかを整理し、Stateに残さないための方法を紹介します。

機密情報はどこに残るのか

Terraformで機密情報を扱うと、次の場所に値が残る可能性があります。

  • コード(.tf、.tfvars):値を直接書くと、Gitの履歴に残る
  • Stateファイル:リソースの属性として、値が平文で保存される
  • planの出力ファイル:terraform plan -out で保存したファイルにも値が含まれる
  • CIのログ:sensitiveを付けていない値は、plan結果としてログに表示される

特に見落とされやすいのがStateファイルです。次のようなコードは、どれもパスワードがStateに残ります。

# パスワードを変数で渡す
resource "aws_db_instance" "main" {
  # ...
  password = var.db_password
}

# ランダムに生成する
resource "random_password" "db" {
  length = 32
}

# Secrets Managerから読み込む
data "aws_secretsmanager_secret_version" "db" {
  secret_id = "myapp/db-password"
}

random_passwordの生成結果も、データソースで読み込んだシークレットの値も、Stateに保存されます。Stateを読める人は、パスワードも読めると考える必要があります。

sensitive = trueでできること・できないこと

variable "db_password" {
  type      = string
  sensitive = true
}
  • できること:planやapplyの画面表示で、値を「(sensitive value)」と伏せる。CIのログに値が出るのを防げる
  • できないこと:Stateファイルへの保存を止めることはできない

sensitiveは「うっかり画面に出さない」ための機能であり、値を守る機能ではありません。

方法1:RDSのパスワードはAWSに管理させる

RDSであれば、パスワードの生成と保管をAWSに任せるのが最も確実です。

resource "aws_db_instance" "main" {
  identifier                  = "myapp-db"
  engine                      = "postgres"
  instance_class              = "db.t4g.micro"
  allocated_storage           = 20
  username                    = "app_admin"
  manage_master_user_password = true
  skip_final_snapshot         = true
}

manage_master_user_password = true を指定すると、AWSがパスワードを生成してSecrets Managerに保存します。**Terraformはパスワードの値を一度も扱わないので、Stateにも残りません。**アプリケーションは、Secrets Managerからパスワードを取得します。

方法2:シークレットの「入れ物」だけをTerraformで作る

APIキーなど、外部サービスから発行された値は、Terraformで生成できません。この場合は、Secrets Managerのシークレット(入れ物)だけをTerraformで作り、値はTerraformの外から登録します。

resource "aws_secretsmanager_secret" "external_api_key" {
  name = "myapp/external-api-key"
}

値はAWS CLIなどで登録します。

aws secretsmanager put-secret-value \
  --secret-id myapp/external-api-key \
  --secret-string "xxxxxxxxxxxxxxxx"

ECSなどのアプリケーションには、値ではなくシークレットのARNだけを渡します。ECSのタスク定義であれば、secretsのvalueFromにARNを指定すると、起動時に値が注入されます。Terraformのコードにも、Stateにも、値は出てきません。

方法3:ephemeralとwrite-only引数を使う

Terraform 1.10で「ephemeral(一時的な)リソース」、1.11で「write-only引数」が追加されました。この2つを組み合わせると、Terraformの中で値を生成・登録しつつ、Stateには残さないことができます。

ephemeral "random_password" "db" {
  length  = 32
  special = true
}

resource "aws_secretsmanager_secret" "db" {
  name = "myapp/db-password"
}

resource "aws_secretsmanager_secret_version" "db" {
  secret_id                = aws_secretsmanager_secret.db.id
  secret_string_wo         = ephemeral.random_password.db.result
  secret_string_wo_version = 1
}
  • ephemeral "random_password":実行中だけ値を生成し、Stateやplanファイルには保存しない
  • secret_string_wo:write-only(書き込み専用)の引数。AWSには値を送るが、Stateには保存しない
  • secret_string_wo_version:write-only引数は前回の値をTerraformが覚えていないため、値を更新したいときはこの番号を上げて変更を知らせる

使うには、Terraform 1.11以降と、ephemeralやwrite-only引数に対応したバージョンのAWSプロバイダー・randomプロバイダーが必要です。比較的新しい機能なので、利用するプロバイダーのドキュメントで対応状況を確認してください。

それでもStateは守る

上の方法でパスワードをStateから減らせても、Stateには他にもリソースの構成情報が含まれます。Stateそのものの保護も欠かせません。

  • S3バックエンドで暗号化する:S3のバケットでSSE-KMSを有効にし、KMSキーの利用権限も絞る
  • バケットへのアクセスを絞る:Stateを読める人をIAMで最小限にする
  • バージョニングを有効にする:誤って壊したときに戻せるようにする
  • Stateをローカルに残さない:terraform.tfstateをGitにコミットしない。.gitignoreに入れておく

まとめ

  • sensitive = true は表示を隠すだけで、値はStateに平文で残る
  • RDSのパスワードは manage_master_user_password でAWSに管理させる
  • 外部のAPIキーなどは、シークレットの入れ物だけをTerraformで作り、値は外から登録する
  • Terraform 1.11以降なら、ephemeralとwrite-only引数でStateに残さずに値を扱える
  • それでもStateは機密情報として、暗号化とアクセス制御で守る

「パスワードをどこに書くか」ではなく、「パスワードがどこに残るか」で考えると、安全な設計を選びやすくなります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?