はじめに
この記事では以下のことについて説明しています。
- Terraformで Secret Manager を構成する方法
「セキュリティ」なんて大体的なカテゴリにしていますが、Seceret ManagerをCloud Run functionsで使う方法を紹介しているだけです。
Secret Managerとは
以下公式引用です。
Secret Manager は、API キー、ユーザー名、パスワード、証明書などの機密データを保存して管理できるシークレットおよび認証情報管理サービスです。
要するに他所から見られると確実にまずい認証情報を置いておけるサービスです。一度コンソールの方で触ってみてください。
まずはこのSecret Managerに置く秘密情報を、Terraformで作成しましょう。
前準備:Secret Manager APIを有効にする
GCPの検索欄に「Secret Manager」を入力してサービスページにいきます。そして有効化しましょう。
Terraformで秘密情報をSecert Managerに置く
今回Secret Managerに配置する秘密情報 my-secret.json
です。main.tf
と同じ階層に置いておきます。
{
"hoge": "hogehoge"
}
シークレットのリソース定義
レプリケーションポリシーを「自動」にした my-secret
という名前のシークレットのリソースを作成します。
resource "google_secret_manager_secret" "my-secret" {
secret_id = "my-secret"
replication {
automatic = true
}
}
シークレットのバージョンのリソース定義
シークレットはバージョンという概念で値の管理をしています。まずシークレットの最新バージョンを示すリソース my-secret-version
を表現します。値は先ほど作成した my-secret.json
にします。
また、新しくバージョンが作られた際に過去のバージョンを全て破棄するようにします。
resource "google_secret_manager_secret_version" "my-secret-version" {
secret = google_secret_manager_secret.my-secret.id
secret_data = file("${path.module}/my-secret.json") # string形式なのでこれ
deletion_policy = "DELETE" # 新規バージョン作成時、過去のバージョンを全て破棄
}
注意1
deletion_policy
には設定できる項目が DELETE
, DISABLE
, ABANDON
の3つありますが、名前の雰囲気で設定すると「あれ?思ったように動かないぞ?」となります。
-
DELETE
: 直前のバージョンの破棄 -
DISABLE
: 直前のバージョンの無効化 -
ABANDON
: 直前のバージョンを保持
です。ABANDON
が破棄じゃないの罠すぎる。
注意2
また、deletion_policy
の更新とシークレットの追加を同時に行った場合、更新したdeletion_policy
が反映されるのは次にシークレットの追加を行った時です。 注意してください。
事例
■ 状況
ver.1のシークレット: 有効, versionの deletion_policy
: ABANDON
■ アクション
deletion_policy
を DELETE
にして ver.2 のシークレットを追加し terraform apply
■ 結果1
ver.1のシークレット: 有効 → 無効にならない
ver.2のシークレット: 有効
■ 行動2
ver.3 のシークレットを追加し terraform apply
■ 結果2
ver.1のシークレット: 有効
ver.2のシークレット: 破棄
ver.3のシークレット: 有効 → これ以降作られるverに新しいdeletion_policy
が適応される
解決策
deletion_policy
の更新 とシークレットの追加を別々の terraform apply
で行うことです。
最新のシークレットバージョンの data
ソースを定義する
新規バージョンの作成は上記の "google_secret_manager_secret_version" . "my-secret-version"
でできますが、参照は data
ソースを使います。リソースで作成済みでないと404エラーになってしまうので、depends_on
に対象のバージョンのリソースを記載します。
data "google_secret_manager_secret_version" "my-secret-version-latest" {
secret = google_secret_manager_secret.my-secret.id
version = "latest"
depends_on = [
google_secret_manager_secret_version.my-secret-version
]
}
シークレットにアクセスするIAMを定義
そしてそのシークレットにアクセスするIAMを定義します。シークレットにアクセスするのはCloud Run functionsのランタイムですが、そのランタイムのデフォルトサービスアカウントは <PROJECT_NUMBER>-compute@developer.gserviceaccount.com
の形式になっているので、IAMの member
に指定してあげます。
resource "google_secret_manager_secret_iam_member" "secretmanager-my-secret" {
secret_id = google_secret_manager_secret.my-secret.id
role = "roles/secretmanager.admin"
member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com"
condition {
title = "my-secret iam"
description = "my-secret へのアクセス許可を行うIAM"
expression = "resource.name.startsWith(\"${google_secret_manager_secret.my-secret.name}\")"
}
}
参考
そして terraform plan
を入力して、内容に問題がなければ terraform apply
をしましょう。
$ terraform apply
# ...省略
# data.google_secret_manager_secret_version.my-secret-version-latest will be read during apply
# (config refers to values not yet known)
<= data "google_secret_manager_secret_version" "my-secret-version-latest" { ... }
# google_cloud_run_service_iam_member.member will be created
+ resource "google_cloud_run_service_iam_member" "member" { ... }
# google_cloudfunctions2_function.default will be created
+ resource "google_cloudfunctions2_function" "default" { ... }
# google_secret_manager_secret.my-secret will be created
+ resource "google_secret_manager_secret" "my-secret" { ... }
# google_secret_manager_secret_iam_member.secretmanager-my-secret will be created
+ resource "google_secret_manager_secret_iam_member" "secretmanager-my-secret" { ... }
# google_secret_manager_secret_version.my-secret-version will be created
+ resource "google_secret_manager_secret_version" "my-secret-version" { ... }
# google_storage_bucket.default will be created
+ resource "google_storage_bucket" "default" { ... }
# google_storage_bucket_object.object will be created
+ resource "google_storage_bucket_object" "object" { ... }
# random_id.default will be created
+ resource "random_id" "default" { ... }
Plan: 8 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ function_uri = (known after apply)
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
# ...省略
Apply complete! Resources: 8 added, 0 changed, 0 destroyed.
Outputs:
function_uri = "https://xxx.a.run.app"
おわりに
Secret Managerを Terraform で構成する方法を紹介しました。次回はこうして作成したシークレットのバージョンにCloud Run functions からアクセスする方法を紹介します。
トラブルシューティング
Error: Error creating function: googleapi: Error 403: Could not create Cloud Run service 関数名. Cannot access API run.googleapis.com in project プロジェクト名...
エラー文
╷
│ Error: Error creating function: googleapi: Error 403: Could not create Cloud Run service sample-crf. Cannot access API run.googleapis.com in project プロジェクト名
│
│ with google_cloudfunctions2_function.default,
│ on main.tf line 42, in resource "google_cloudfunctions2_function" "default":
│ 42: resource "google_cloudfunctions2_function" "default" {
│
╵
解決策
GCPのAPIが一部有効になっていないため発生している可能性があります。以下のコマンドを実行してみましょう。
$ gcloud services enable run.googleapis.com --project プロジェクト名
Operation "operations/acf.p2-735381230523-757f2cf0-10ce-43af-9812-3e93e1993208" finished successfully.