Secret Exposure Risk #
Secret yang ter-commit ke Git adalah insiden keamanan, bukan sekadar kesalahan. Git history bersifat permanen — bahkan setelah file dihapus atau commit di-revert, secret masih ada di dalam history yang bisa diakses oleh siapapun yang punya akses ke repository. Di ekosistem Terraform, jalur paparan secret lebih banyak dari yang disadari: file .tfvars yang ter-commit, state file yang disimpan tidak aman, log CI/CD yang menampilkan nilai sensitif, dan output plan yang terlalu verbose. Memahami semua jalur ini adalah langkah pertama untuk menutupnya.
Jalur Paparan Secret #
JALUR PAPARAN SECRET DI TERRAFORM:
1. GIT REPOSITORY
- File .tfvars yang berisi secret ter-commit
- terraform.tfstate local yang ter-commit
- Credentials hardcoded di main.tf atau provider config
Risiko: Semua yang ada di Git history PERMANEN dan bisa dibaca siapapun
2. CI/CD LOGS
- terraform plan output menampilkan nilai sensitive jika tidak ditandai
- Echo atau print credentials dalam pipeline script
- Error message dari provider yang menyertakan request/response body
Risiko: Log CI bisa dibaca oleh semua member tim, bahkan public jika repo public
3. STATE FILE
- Semua resource attribute tersimpan di state, termasuk yang sensitive
- State file tidak dienkripsi jika disimpan di S3 tanpa enkripsi
- terraform.tfstate lokal yang di-share via Slack atau email
Risiko: State berisi plaintext credential untuk semua resource
4. TERRAFORM PLAN OUTPUT
- Plan output bisa tampilkan nilai variable sebelum apply
- Plan yang di-post ke PR comment bisa kebaca banyak orang
Risiko: Secret muncul di PR yang mungkin public atau bisa dilihat banyak orang
5. TERRAFORM CLOUD / REMOTE BACKEND
- Variable yang tidak ditandai sensitive di Terraform Cloud
- Akses ke remote state yang tidak terkontrol
Risiko: Semua yang bisa read workspace bisa baca variabelnya
.gitignore untuk Terraform #
Baris pertama pertahanan adalah memastikan file sensitif tidak bisa ter-commit.
# .gitignore — wajib ada di setiap repository Terraform
# State files — JANGAN PERNAH commit ke Git
*.tfstate
*.tfstate.*
.terraform.tfstate.lock.info
# Local .terraform directory (provider binaries, dll.)
.terraform/
# File override — biasanya untuk pengembangan lokal
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Variable files yang mungkin berisi credential
*.tfvars # Hati-hati: ini juga mengexclude production.tfvars yang mungkin perlu
*.tfvars.json
# Lebih spesifik — hanya exclude file yang berisi secret
secret.tfvars
secrets.tfvars
credentials.tfvars
*.secret.tfvars
# Terraform plan output (bisa berisi sensitive values)
tfplan
*.tfplan
# Crash log
crash.log
crash.*.log
# Verifikasi .gitignore bekerja
git status --ignored # Lihat file yang di-ignore
# Verifikasi tidak ada .tfstate yang sudah ter-track
git ls-files | grep ".tfstate"
# Jika ada output: file sudah ter-track, perlu dihapus dari tracking
Mendeteksi Secret yang Sudah Ter-commit #
# git-secrets — cegah commit yang berisi AWS credentials
brew install git-secrets
# Setup di repository
git secrets --install
git secrets --register-aws
# Scan history yang sudah ada
git secrets --scan-history
# truffleHog — scan lebih comprehensive, termasuk high-entropy strings
pip install truffleHog
trufflehog git file://. --only-verified
# gitleaks — scanner populer, bisa diintegrasikan ke CI
brew install gitleaks
gitleaks detect --source . --verbose
# Integrasikan ke pre-commit hook
# .pre-commit-config.yaml:
repos:
- repo: https://github.com/zricethezav/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
Mengaudit State untuk Secret #
# Lihat semua atribut sensitif di state
terraform state pull | jq '.resources[].instances[].attributes | to_entries[] | select(.value | type == "string" and length > 20 and (startswith("AKIA") or contains("password") or contains("secret") or contains("key")))'
# Lebih praktis: gunakan terraform-state-mover atau custom script
# untuk identifikasi nilai yang terlihat seperti credential
# Contoh script sederhana
terraform state pull | python3 -c "
import json, sys, re
state = json.load(sys.stdin)
patterns = [
r'AKIA[A-Z0-9]{16}', # AWS Access Key
r'(?i)password\": \"[^\"]+\"', # Hal yang terlihat seperti password
r'(?i)secret\": \"[^\"]{8,}\"', # Secret yang cukup panjang
]
state_str = json.dumps(state)
for pattern in patterns:
matches = re.findall(pattern, state_str)
for match in matches:
print(f'POTENTIAL SECRET: {match[:50]}...')
"
Jika Secret Sudah Ter-commit: Langkah Mitigasi #
Jika secret sudah terlanjur masuk ke Git history, menghapus commit tidak cukup — secret masih ada di history dan harus dianggap sudah dikompromikan.
LANGKAH MITIGASI JIKA SECRET TER-COMMIT:
LANGKAH 1 (SEGERA): Rotasi credential
Anggap credential sudah dikompromikan meskipun belum ada bukti penyalahgunaan
Buat credential baru, nonaktifkan yang lama
Ini harus dilakukan SEBELUM mencoba membersihkan Git history
LANGKAH 2: Bersihkan Git history
git filter-repo --path file-berisi-secret --invert-paths
# Atau gunakan BFG Repo Cleaner untuk repository besar
# Force push ke remote (koordinasi dengan tim dulu!)
git push --force-with-lease
LANGKAH 3: Beritahu semua yang punya clone
Setiap orang yang punya clone lokal perlu:
git fetch --all
git reset --hard origin/main
LANGKAH 4: Periksa apakah repository pernah di-fork atau di-mirror
Jika ya: fork dan mirror juga berisi secret
Hubungi pemilik untuk membersihkan atau hapus fork
LANGKAH 5: Audit akses
Periksa CloudTrail / audit log untuk melihat apakah credential sudah digunakan
Jika ada penggunaan yang mencurigakan: incident response procedure
LANGKAH 6: Post-mortem
Identifikasi bagaimana secret bisa ter-commit
Update .gitignore dan pre-commit hooks
Training tim tentang secret management
Terraform Plan: Mencegah Secret Tampil di Output #
# Tandai semua variable yang sensitif
variable "db_password" {
type = string
sensitive = true # ← Nilai tidak akan tampil di plan output
}
variable "api_key" {
type = string
sensitive = true
}
# Output yang sensitif juga harus ditandai
output "db_connection_string" {
value = "postgresql://admin:${var.db_password}@${aws_db_instance.main.endpoint}/${var.db_name}"
sensitive = true # Tidak akan tampil di terraform output biasa
}
# Di CI pipeline: pastikan log tidak menampilkan sensitive values
# Tambahkan mask untuk nilai yang diketahui sensitif
# GitHub Actions: mask secret dari log
echo "::add-mask::${{ secrets.DB_PASSWORD }}"
# Atau: jalankan plan dengan format JSON dan filter sensitive values
# sebelum menampilkan ke PR comment
terraform plan -out=tfplan -json | jq 'del(.[] | .sensitive_values)' > plan_filtered.json
Ringkasan #
- Jalur paparan secret ada di mana-mana: Git history, CI logs, state file, plan output, dan remote backend — setiap jalur perlu ditutup dengan kontrol yang tepat.
.gitignoreyang benar adalah pertahanan pertama —.tfstate,.tfvarsberisi secret, dan.terraform/tidak boleh masuk ke Git.- Gunakan pre-commit hook dengan gitleaks atau git-secrets untuk mendeteksi secret sebelum ter-commit, bukan setelah.
- Secret yang sudah ter-commit harus dianggap dikompromikan — langkah pertama adalah rotasi credential, bukan membersihkan Git history.
sensitive = truepada variable dan output mencegah nilai tampil di plan output dan terminal, tapi tidak mencegah nilai masuk ke state.- Audit state secara berkala untuk mendeteksi nilai yang terlihat seperti credential — state berisi plaintext semua atribut resource.
← Sebelumnya: Credential Rotation Strategy Berikutnya: Least Privilege →