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.
flowchart TD
A["Secret"] --> B[".tfvars\ncommit"]
A --> C["State file\nunsecured"]
A --> D["CI/CD log\noutput"]
A --> E["Plan output\nverbose"]
B --> F["🔴 Exposure"]
C --> F
D --> F
E --> F
style A fill:#f59e0b,stroke:#d97706,color:#fff
style B fill:#ef4444,stroke:#dc2626,color:#fff
style C fill:#ef4444,stroke:#dc2626,color:#fff
style D fill:#ef4444,stroke:#dc2626,color:#fff
style E fill:#ef4444,stroke:#dc2626,color:#fff
style F fill:#ef4444,stroke:#dc2626,color:#fffJalur 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
flowchart LR
A[".gitignore\n.tfvars"] --> B["Prevent\ncommit"]
C["CI masking\nsensitive"] --> D["Prevent\nlog leak"]
E["State encryption\naccess control"] --> F["Prevent\nstate leak"]
style A fill:#3b82f6,stroke:#1e40af,color:#fff
style B fill:#10b981,stroke:#059669,color:#fff
style C fill:#8b5cf6,stroke:#6d28d9,color:#fff
style D fill:#10b981,stroke:#059669,color:#fff
style E fill:#f59e0b,stroke:#d97706,color:#fff
style F fill:#10b981,stroke:#059669,color:#fffSecret Scanning di CI/CD Pipeline #
Deteksi secret yang tidak sengaja ter-ekspose harus dilakukan secara otomatis.
# GitHub Actions: secret scanning
name: Secret Scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Manual scan dengan gitleaks
gitleaks detect --source . --verbose
# Scan hanya perubahan terbaru
gitleaks detect --source . --log-opts="--since=2024-01-01"
Emergency Response Plan #
# Jika secret sudah bocor ke Git:
# 1. ROTATE SECRET SEGERA
# Jangan tunggu — asumsikan secret sudah dikompromikan
aws iam delete-access-key --access-key-id AKIA... --user-name terraform-ci
aws iam create-access-key --user-name terraform-ci
# 2. Hapus dari Git history
# Gunakan BFG Repo-Cleaner
java -jar bfg.jar --delete-files terraform.tfstate repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# 3. Force push (koordinasi dengan tim!)
git push --force --all
# 4. Audit akses menggunakan credential yang bocor
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIA...
# 5. Review dan hardening proses untuk mencegah terulang
Secret Detection Tools #
# Install dan gunakan secret scanning tools
# gitleaks (Git secret scanner)
brew install gitleaks
gitleaks detect --source . --verbose
# truffleHog (deep scan Git history)
pip install trufflehog
trufflehog git file://. --only-verified
# detect-secrets (Yelp)
pip install detect-secrets
detect-secrets scan > .secrets.baseline
detect-secrets audit .secrets.baseline
# CI/CD: secret scanning di setiap PR
name: Secret Scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Scan full history
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
State File Encryption at Rest #
# State file mengandung sensitive values
# Pastikan backend mengenkripsi state
# S3 backend dengan KMS encryption
terraform {
backend "s3" {
bucket = "terraform-state-prod"
key = "infrastructure/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
kms_key_id = "alias/terraform-state"
}
}
# GCS backend dengan encryption
terraform {
backend "gcs" {
bucket = "terraform-state-prod"
prefix = "infrastructure"
encryption_key = "projects/my-project/locations/global/keyRings/terraform/cryptoKeys/state"
}
}
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 →