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.
  • .gitignore yang benar adalah pertahanan pertama — .tfstate, .tfvars berisi 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 = true pada 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 →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact