TL; DR
- TerraformのSOPS providerがv1.3でEphemeral resourceに対応しました
- 実際にtfstateを確認したところ、機密情報が保存されなくなっていることが確認できました
はじめに
以前の記事で、自宅サーバーのシークレット管理にHashicorp Vaultを利用することを紹介しました。 その際に、Hashicorp Vaultへのシークレット登録に利用していたSOPS providerでは、コード上では暗号化できたとしてもtfstateに機密情報が保存されてしまうという問題があることを紹介し、その対策として、Terraform v1.10の新機能であるEphemeral resourceが今後導入されることを期待していました。
Ephemeral resourceは、公式ドキュメントにもあるように他のTerraformリソースとはライフサイクルが異なり、tfstateやplanファイルにその情報が保存されないリソースとなっており、機密情報を扱うユースケースに非常に適しています。
期待通り、2025年10月にリリースされたSOPS providerのv1.3では無事このEphemeral resourceに対応したため、実際に試してみました。
サンプルコード
このサンプルコードスニペットでは、SOPSで暗号化されたYAMLファイルから機密データを読み取り、その内容をHashicorp Vaultに登録する例を示しています。
// SOPSで暗号化された機密データの読み取り
// この時点ではまだEphemeral resourceは利用しない
data "sops_file" "secret_file" {
source_file = "./secret.enc.yaml"
}
locals {
secret_file_content = yamldecode(ephemeral.sops_file.secret_file.raw)
}
// 機密データの利用例(Hashicorp Vaultへのシークレット登録)
resource "vault_mount" "mount" {
path = "secret-mount"
type = "kv"
options = { version = "2" }
}
resource "vault_kv_secret_v2" "secret" {
mount = vault_mount.mount.path
name = "secret"
data_json_wo = jsonencode(local.secret_file_content.secret)
data_json_wo_version = 1
}
SOPS providerで読み取るファイルは、下のようにあらかじめSOPSで暗号されたファイルとなっており、これをみたとしても鍵を持っていなければ内容を知ることはできません。
secret:
secret1: ENC[AES256_GCM,data:{暗号化されたデータ},type:str]
secret2: ENC[AES256_GCM,data:{暗号化されたデータ},type:str]
なお、この例ではSOPSファイルからの機密情報読み取りと、その利用の両方の部分でTerraformを利用しているため、このそれぞれについて機密情報が漏洩しないかを気にする必要があります。 今回サンプルとしたHashicorp Vault providerのはwrite-only arguments)に対応しており、利用部分については下のようにtfstateへの機密情報保存を防ぐことができます。
// tfstateより抜粋
{
"module": "module.vault-secret",
"mode": "managed",
"type": "vault_kv_secret_v2",
"name": "grafana_credentials",
"provider": "provider[\"registry.terraform.io/hashicorp/vault\"]",
"instances": [
{
"attributes": {
"data": {},
"data_json": null,
"data_json_wo": null, // 機密データはtfstateには保持されない
"data_json_wo_version": 1,
"mount": "mount",
"name": "secret",
"path": "secret-mount/data/secret"
}
}
]
}
SOPS provider v1.3以前の挙動
それでは比較のためにEphemeral resource導入前の挙動をみてみましょう。 今回はSOPS providerのv1.2で試してみました。
実際にtfstateファイルをみてみると、下のようにsops_fileというdataブロック側でtfstateに機密データが保存されてしまうことがわかります。
// tfstateより抜粋
{
"module": "module.vault-secret",
"mode": "data",
"type": "sops_file",
"name": "secret_file",
"provider": "provider[\"registry.terraform.io/carlpett/sops\"]",
"instances": [
{
"attributes": {
// 平文で機密データが保存されてしまう
"data": {
"secret.secret1": "I am secret",
"secret.secret2": "I am secret too!",
},
"source_file": "./secret.enc.yaml"
}
}
]
}
tfstateは一般にアクセス制御を行うとはいえ、万が一漏洩してしまった場合にこれでは各種シークレットが全て丸裸となってしまいます。
SOPS provider v1.3ではどうなったか
それでは本題となるSOPS provider v1.3での挙動をみてみましょう。
このリリースでは、dataブロックとして存在していたsops_fileとsops_externalという2つそれぞれに対して同名・同インタフェースのephemeral resourceが追加されました1。
このdataブロックからephemeral resourceへの置き換えを行います。
-data "sops_file" "secret_file" {
+ephemeral "sops_file" "secret_file" {
source_file = "./secret.enc.yaml"
}
実際にapplyしてみると、読み込み先が変わるのでリソース自体には更新が入るので少し怖いですが、内容自体は変更なくそのままとなっています。
# module.vault-secret.vault_kv_secret_v2.secret will be updated in-place
~ resource "vault_kv_secret_v2" "secret" {
id = "secret-mount/data/secret"
name = "secret"
# (8 unchanged attributes hidden)
# (1 unchanged block hidden)
}
改めて先ほどと同様にtfstateをみてみると、今度は綺麗さっぱりとsops_fileに関する情報がなくなっていることが確認できました(丸ごとなくなっていたので実際の内容はなし)。 これによってtfstate内部から完全に機密情報をなくすことができました。
まとめ
この記事ではTerraformのSOPS providerがEphemeral resourceに対応したことを受けて、実際に試してみた内容を紹介しました。
これによって機密情報の読み取り部分に関してはtfstateから機密情報をなくすことができたので、Hashicorp Vault providerのようにwrite only argumentに対応しているproviderが増えていけばtfstateから完全に機密情報をなくせる世界も近いかもしれません。
Footnotes
-
なお、Terraform Registry側のドキュメントではまだEphemeral resourceに関する記載は追加されていませんが、GitHubのREADMEにはちゃんと記載があるので詳細はそちらを参照してください。 ↩