Belajar OpenTofu - Secret Management & State Hardening
Episode 15 of 21

Belajar OpenTofu - Secret Management & State Hardening

Rahasia di IaC bocor lewat tiga pintu: output plan, state plaintext, dan kredensial statis di kode. Episode ini menggabungkan client-side state encryption dengan sensitive variables, lalu menarik rahasia dinamis dari Vault, AWS Secrets Manager, dan GCP Secret Manager saat apply.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Pendahuluan

Di episode 14 sebelumnya kita meninjau managed platforms — Spacelift, env0, Scalr — dan Digger. Semua platform itu menjalankan OpenTofu, tapi tidak satu pun yang bisa menyelamatkan kalian ketika rahasia sudah bocor lewat hal yang mereka anggap biasa: file state, output plan, atau variabel yang ditulis asal. Keamanan infrastruktur tidak ditentukan oleh platform, melainkan oleh bagaimana rahasia disimpan, dienkripsi, dan dikirim ke engine.

Di episode 15 ini kita mengeraskan dua lapis sekaligus. Pertama, kita mengombinasikan client-side state encryption — yang pertama kali kita bahas di episode 6 — dengan sensitive variables agar rahasia tidak tampil di plan maupun output. Kedua, kita mengganti kredensial statis di kode dengan rahasia dinamis yang ditarik langsung saat tofu apply dari HashiCorp Vault, AWS Secrets Manager, dan GCP Secret Manager.

Pembahasan Utama

sensitive = true: Menghentikan Kebocoran di Plan dan Output

Menulis password langsung di variables.tf adalah kebiasaan yang harus dihentikan sejak hari pertama. Cara yang benar: jadikan variabel itu sensitive = true, sehingga nilainya tidak pernah ditampilkan di output tofu plan maupun tofu output, dan tidak tampil ketika dirender ke layar:

variables.tf
variable "db_password" {
  type        = string
  sensitive   = true
  description = "Password database, dipasok dari secret manager saat apply"
}
 
output "db_connection_string" {
  value     = "${var.db_username}@${var.db_endpoint}"
  sensitive = true
}

Nilai sensitive membuat OpenTofu mengganti isinya dengan panah merah di output plan. Hanya satu tanda peringatan yang harus diingat: sensitive = true hanya menutupi tampilan — nilai aslinya tetap tersimpan utuh di state.

Caution

Jangan pernah menaruh nilai rahasia di file .tfvars yang di-commit, karena jejaknya akan abadi di riwayat git. Di CI, pasok variabel sensitive lewat secret environment atau file .tfvars yang dihasilkan pipeline dan dibuang setelah digunakan.

State yang Plaintext Adalah Pintu Bocor yang Diam

Lapisan tadi hanya mengatur tampilan. Lapisan berikutnya adalah penyimpanan: state berisi nilai asli dari setiap resource, termasuk password dan kunci. Di episode 6 kita sudah melihat solusi OpenTofu untuk ini — client-side state encryption, yang mengenkripsi state di client sebelum dikirim ke backend, sehingga bahkan admin S3 pun tidak bisa membaca isinya:

encryption.tf (recap dari episode 6)
encryption {
  key_provider "aws_kms" "main" {
    kms_key_id = "arn:aws:kms:ap-southeast-1:123456789012:key/example-key-id"
  }
 
  method "aes_gcm" "main" {
    keys = key_provider.aws_kms.main
  }
 
  state {
    method = method.aes_gcm.main
  }
}

Kombinasikan keduanya: sensitive = true menjaga rahasia agar tidak tampil di output dan plan, sementara encryption menjaga rahasia yang sama agar tidak terbaca di state. Ini dua sisi dari satu pintu keamanan yang sama.

Rahasia Dinamis: Vault saat Apply

Kredensial statis di dalam file .tf — token atau key — selalu menjadi risiko: tersalin ke laptop, bocor ke git, atau menginap di cache CI. Solusinya adalah menarik rahasia saat apply, bukan menyimpannya. Provider Vault membaca secret dari HashiCorp Vault langsung pada waktu OpenTofu menjalankan ekspresi:

vault.tf
variable "vault_token" {
  type      = string
  sensitive = true
}
 
provider "vault" {
  address = var.vault_addr
  token   = var.vault_token
}
 
data "vault_generic_secret" "db" {
  path = "secret/data/db/prod"
}
 
resource "aws_db_instance" "postgres" {
  engine         = "postgres"
  username       = data.vault_generic_secret.db.data["username"]
  password       = data.vault_generic_secret.db.data["password"]
}

Perhatikan polanya: rahasia tidak pernah disimpan sebagai literal di kode. OpenTofu membaca nilai dari Vault saat apply berlangsung, dan nilai tersebut tidak akan pernah muncul di riwayat git.

Alternatif Native Cloud: AWS dan GCP Secret Manager

Ketika seluruh stack sudah berjalan di satu cloud, memakai secret manager bawaan cloud mengurangi komponen yang harus dikelola. Di AWS, data source aws_secretsmanager_secret_version mengambil nilai terbaru sebuah secret, dan nilainya bisa berupa JSON yang di-decode:

secretsmanager.tf
data "aws_secretsmanager_secret_version" "db" {
  secret_id = "prod/db-credentials"
}
 
resource "aws_db_instance" "postgres" {
  engine   = "postgres"
  username = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)["username"]
  password = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)["password"]
}

Di GCP, google_secret_manager_secret_version mengambil versi secret yang aktif dan nilainya tersedia lewat atribut secret_data:

secretmanager.tf
data "google_secret_manager_secret_version" "api_key" {
  secret = "api-key"
}
 
resource "google_cloudfunctions2_function" "api" {
  environment_variables = {
    API_KEY = data.google_secret_manager_secret_version.api_key.secret_data
  }
}
Sumber rahasiaKapan cocok dipakaiData source OpenTofu
HashiCorp VaultMulti-cloud, rotasi aktif, policy ketatvault_generic_secret
AWS Secrets ManagerStack penuh di AWSaws_secretsmanager_secret_version
GCP Secret ManagerStack penuh di GCPgoogle_secret_manager_secret_version

Tip

Rotasi rahasia di secret manager tidak mengharuskan perubahan kode sama sekali. Saat nilai berubah di Vault atau AWS, jalankan ulang tofu plan lalu tofu apply — data source membaca versi terbaru dan resource yang bergantung padanya diperbarui otomatis.

Pola Praktik Aman untuk Produksi

  • Selalu kombinasikan sensitive = true pada variabel dan output; dua-duanya punya jalur kebocoran masing-masing.
  • Enkripsi state di client, jangan pernah mengandalkan enkripsi default backend saja.
  • Jangan kirim file plan.tfplan ke artefak publik — plan menyimpan nilai rahasia yang sama seperti state.
  • Di CI, otentikasi ke Vault sebaiknya memakai AppRole atau OIDC, bukan token pribadi.
  • tofu console dan tofu plan -no-color bisa menampilkan nilai sensitive — waspadai saat menangkap log.

Penutup

Pada episode 15 ini kita telah mengeraskan penanganan rahasia:

  • Menggunakan sensitive = true pada variabel dan output untuk menghentikan kebocoran di plan dan output.
  • Menggabungkannya dengan client-side state encryption agar state tidak terbaca di storage backend.
  • Menarik rahasia dinamis dari HashiCorp Vault lewat vault_generic_secret saat apply.
  • Menggunakan aws_secretsmanager_secret_version dan google_secret_manager_secret_version untuk cloud-native.
  • Menyusun pola praktik aman: tanpa secret di git, tanpa plan publik, tanpa token pribadi di CI.

Mengamankan rahasia adalah satu hal; memastikan apa yang boleh dan tidak boleh dibuat di cloud adalah hal lain. Di episode 16 selanjutnya kita akan membangun Policy as Code dengan Open Policy Agent: mengekstrak plan JSON dengan tofu show -json dan menulis aturan Rego yang menolak security group port 22 terbuka ke publik atau storage tanpa enkripsi. Sampai jumpa!

Belajar OpenTofu - Secret Management & State Hardening | Belajar OpenTofu