sops × Terrgrunt で機密リソースを安全に IaC 管理する
こんにちは。エンジニアチームの山岸 (@yamagishihrd) です。
今回は、Secrets Manager シークレットや SSM パラメータのような機密情報を含むリソースを、sops を使って安全に Terraform (Terragrunt) 管理する方法について紹介します。
Terragrunt については別エントリでも書いているので、併せてご覧頂ければと思います。
背景
- シンプルフォームでは、クラウドリソースの IaC ツールとして Terraform (+Terragrunt) を利用しています。
- 一方、DB アクセス情報などアプリケーションが利用するシークレット情報を Secrets Manager で管理していますが、シークレットに含まれる値はもちろん機密情報を含むため平文で Git 管理できず、シークレット値については Terraform 化が出来ていませんでした。
- 機密情報を暗号化して安全に管理するための sops というツールがあり、Terragrunt との相性も良さそうだったので、導入・利用の手順についてまとめてみました。
sops について
本題に入る前に、sops について簡単にご紹介します。
- sops (Secrets OPerationS) とは、Firefox 開発元でもある Mozilla が公開している暗号化ツールです。
- 暗号鍵として PGP 鍵の他、AWS KMS, Google Cloud KMS, Azure Key Vault のようなクラウド管理型の暗号鍵にも対応しています。
- ファイル形式として、YAML, JSON, INI, バイナリ形式をサポートしています。
デモ内容
sops 暗号鍵として AWS KMS キーを使用し、Secrets Manager シークレットを Terraform 管理するケースを想定します。シークレットの値は YAML 形式で記述し、暗号化した状態で保存します。
サンプルのシークレットとして、以下を登録してみたいと思います。
db_username: postgres
db_password: Rh5]dq9x?tN{Z!2c
Terraform, Terragrunt についてはインストール済みの想定で進めます。筆者環境で利用するバージョンは以下の通りです。
-
Terraform ...
1.3.1
-
Terragrunt ...
0.39.2
事前準備
sops のインストール
Mac であれば Homebrew でインストールできます。詳細は 公式ページ をご確認ください。
% brew install sops
% sops -v
sops 3.7.3 (latest)
KMS キーの作成
sops の暗号化に使用する KMS キーを作成します。
resource "aws_kms_key" "default" {
description = "KMS key for sops"
key_usage = "ENCRYPT_DECRYPT"
customer_master_key_spec = "SYMMETRIC_DEFAULT"
is_enabled = true
enable_key_rotation = true
multi_region = false
}
resource "aws_kms_alias" "default" {
name = "alias/sops-demo-kms-key"
target_key_id = aws_kms_key.default.key_id
}
# outputs.tf
output "kms_key_arn" {
value = aws_kms_key.default.arn
}
環境変数の設定
作成した KMS キーの ARN を環境変数として設定し、sops から利用できるようにします。
% export SOPS_KMS_ARN=arn:aws:kms:ap-northeast-1:123456789012:key/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
以上で事前準備は完了です。
実施手順
暗号化ファイルの作成・編集
暗号化されたシークレットファイル(以下、"暗号化ファイル")を新規に作成するには、ファイル名を引数に sops
コマンドを実行します。(暗号化保存したファイルを再度編集する場合も同様)
% sops secret.yml
vi エディタが起動するので、シークレット情報を平文で編集します。
db_username: postgres
db_password: Rh5]dq9x?tN{Z!2c
編集を抜けると、引数に指定したファイル名で暗号化ファイルが保存されています。暗号鍵にアクセスできない限り復号化できないため、これで Git 管理可能な状態になりました。
db_username: ENC[AES256_GCM,data:WnvCfY/RpX8=,iv:u3BilWG59OI/flukI0zecL3jnvzZsCAwL+xh5wV3hTc=,tag:w1vuscrUeJqoF9CdCVXjEA==,type:str]
db_password: ENC[AES256_GCM,data:lg/QfPNn0cFpOe5hVvcWvQ==,iv:4G0Vof3xFEpDg/PsBtzD7rubKwtwttVr4+PTjqtvAeM=,tag:LM/O1ZET++iqVN9R6VbmuQ==,type:str]
sops:
kms:
- arn: arn:aws:kms:ap-northeast-1:123456789012:key/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
created_at: "2023-01-21T06:49:13Z"
enc: AQICAHhIlrm5Zlw2fGFM2pmLIJeZiPGO9TzojB/GpRpXImnV3wFWMfaiskiEetbYhPDTY4KVAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMU50BwxILtyVilRfRAgEQgDsJl5DbCdl1d4pppvHDqHSIPtPtzReOvJ2w+8lQt63D8dc6PnrGoc5qF0EgNl7ppuIro7TN0TZVBOhlng==
aws_profile: ""
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2023-01-21T06:49:57Z"
mac: ENC[AES256_GCM,data:76MElmFanG/sRW1fzW/OtIKzebXeJOGffv/yvaavgLW3xsT193iUopgS339cpXZH6Ujv8+CpKmAudcR4it2NYmnaqAtD083Ezrr4doYLLegsmKx0C63MVPIWbHxdOcGTpt4bQdEPOWT+q7Tf7Il+vnkmnxjGCW/41WVyN2kn/hE=,iv:7pMUV8KTm7sGr0kQW2/+Bti5AXypacJ2XwpSnjmmjW8=,tag:MsKLT9XZ8iER6zElemjncQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.7.3
暗号化ファイルを復号化して値を確認するには、-d
オプションをつけます。
% sops -d secret.yml
db_username: postgres
db_password: Rh5]dq9x?tN{Z!2c
Terragrunt からの読み込み
子 terragrunt.hcl から暗号化ファイルを読み取るには、sops_decrypt_file() という Terragrunt の Build-in 関数を使用します。読み取ったシークレット情報を Secrets Manager シークレットのモジュールへの入力として渡します。
include "root" {
path = find_in_parent_folders()
}
locals {
secret = try(yamldecode(sops_decrypt_file("secret.yml")), {})
}
terraform {
source = "${dirname(find_in_parent_folders())}//modules/secretsmanager/sample_secret/"
}
inputs = {
secret_string = local.secret
}
Secrets Manager シークレットの作成
続いてモジュール側の定義です。シークレット自体は aws_secretsmanager_secret で定義しますが、値を保持するのは aws_secretsmanager_secret_version になります。
入力から受け取った secret_string
を JSON オブジェクトにした上で attribute に設定します。シークレット名や ARN を出力に定義しておくことで、他リソースからの参照が容易になります。
# variales.tf ------------------------------------------------
variable "secret_string" {
type = map(string)
}
# main.tf ----------------------------------------------------
resource "aws_secretsmanager_secret" "default" {
name = "sample-secret"
}
resource "aws_secretsmanager_secret_version" "default" {
secret_id = aws_secretsmanager_secret.default.id
secret_string = jsonencode(var.secret_string)
}
# outputs.tf -------------------------------------------------
output "secret_arn" {
value = aws_secretsmanager_secret.default.arn
}
output "secret_name" {
value = aws_secretsmanager_secret.default.name
}
確認
terragrunt apply
後にリソースを確認してみると、きちんと設定した値を保持していることが確認できます。
手順は以上です。
まとめ
今回は Secrets Manager シークレットや SSM パラメータのような機密リソースを、sops 暗号化によって安全に Terragrunt 管理する方法についてご紹介しました。読者様の Terraform 運用の参考になれば幸いです。
Discussion