Mencegah misconfigurasi sebelum lahir: scanning Terraform dengan Trivy dan Checkov, menulis policy-as-code dengan OPA/Conftest untuk menolak bucket publik atau SG terbuka di pull request, drift detection, dan merakit pipeline IaC security lengkap dari pre-commit sampai gate CI

Setelah di episode 10 kita membahas compliance dan pemetaan framework, sekarang kita balik arusnya: alih-alih menemukan misconfigurasi lewat CSPM setelah resource lahir (episode 7), mengapa tidak mencegahnya lahir sama sekali — di pull request, sebelum terraform apply pernah berjalan?
Mengapa ini episode yang penting? Karena hampir semua temuan CSPM yang kita triage di episode 7 lahir dari satu titik yang sama: kode infrastruktur yang lolos review. Memasang kontrol di titik itu memberi efek domino terbaik dalam security cloud: satu policy mencegah ribuan insiden potensial, selamanya.
Tiga garis pertahanan untuk IaC, dari paling murah ke paling mahal:
Prinsip ekonominya sederhana: biaya perbaikan naik ~10x di tiap tahap. Bug yang tertangkap di pre-commit butuh satu edit; bug yang tertangkap auditor butuh post-mortem.
Dua scanner open source populer untuk Terraform/HCL:
| Tool | Keunggulan | Catatan |
|---|---|---|
| Trivy (config mode) | Satu binary multi-format: TF, K8s, Dockerfile | Cepat, mudah masuk CI |
| Checkov (Pulumi/Prisma) | Graph-based check, custom check Python | Check ID stabil untuk suppress |
Jalankan keduanya di repo Terraform:
trivy config --severity HIGH,CRITICAL .
checkov -d . --framework terraform --soft-fail-on SKIPPEDContoh hasil yang akan kalian lihat pada repo dengan S3 tanpa enkripsi:
Checkov Result: FAILED checks
Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"
FAILED for resource: aws_s3_bucket.docs
Check: CKV_AWS_19: "Ensure all data stored in the S3 bucket is securely encrypted at rest"
FAILED for resource: aws_s3_bucket.docsKebiasaan penting: kelola pengecualian secara eksplisit dengan komentar # checkov:skip=CKV_AWS_18:alasan-dokumen — bukan mematikan scanner. Setiap skip wajib punya alasan tertulis dan tanggal review ulang.
Scanner generik tahu "best practice"; organisasi kalian punya aturan sendiri ("semua tag wajib ada cost-center", "database hanya boleh di region ap-southeast-1"). Untuk itu kita tulis policy-as-code dengan Rego (OPA) dan jalankan via Conftest di pipeline.
Contoh policy: tolak security group dengan ingress terbuka ke dunia:
package terraform.security
deny[msg] {
r := input.resource_changes[_]
r.type == "aws_security_group"
ingress := r.values.ingress[_]
ingress.cidr_blocks[_] == "0.0.0.0/0"
port := ingress.to_port
msg := sprintf(
"SG %s membuka port %v ke internet (0.0.0.0/0)",
[r.address, port],
)
}Jalankan terhadap plan JSON — bukan source mentah — supaya policy mengevaluasi nilai final setelah variabel di-resolve:
terraform plan -out=tf.plan
terraform show -json tf.plan > tf.json
conftest test tf.json --policy policy/Output gagal akan menyebut resource dan alasannya persis seperti pesan reviewer senior — hanya saja konsisten, 24 jam sehari.
IaC security tidak lengkap tanpa menjawab: apakah realita masih cocok dengan kode? Drift lahir dari klik manual darurat, hotfix konsol, dan automation lain. Deteksi rutin:
terraform init -input=false
terraform plan -detailed-exitcode -out=drift.plan
# exit code 2 = ada perbedaan; 1 = error; 0 = sinkronJadwalkan di CI (cron mingguan): exit code 2 membuka tiket otomatis berisi diff. Aturan budayanya ketat namun adil: drift diperbaiki lewat kode, bukan di-revert paksa — kadang perubahan manual itu ternyata hotfix sah yang belum ditulis balik.
Rakit semuanya menjadi workflow GitHub Actions:
name: iac-security
on:
pull_request:
paths: ["terraform/**"]
jobs:
scan:
runs-on: ubuntu-latest
defaults: {run: {working-directory: terraform}}
steps:
- uses: actions/checkout@v4
- name: fmt & validate
run: |
terraform fmt -check -recursive
terraform init -backend=false && terraform validate
- name: static scan
run: trivy config --exit-code 1 --severity HIGH,CRITICAL .
- name: plan
run: |
terraform plan -input=false -out=tf.plan
terraform show -json tf.plan > tf.json
- name: opa policy gate
run: conftest test tf.json --policy ../policy/
- name: secret scan
run: |
curl -sfL https://raw.githubusercontent.com/gitleaks/gitleaks/master/scripts/install.sh | sh
gitleaks detect --source .. --no-git --redactLima gerbang dalam urutan murah→mahal: format, static scan, plan, policy gate, secret scan. PR yang lolos semua gerbang praktis sudah aman sebelum disentuh manusia reviewer — sehingga review fokus pada desain, bukan typo firewall.
Tip
Simpan baseline finding lama (--ignore-unfixed atau file baseline) agar pipeline langsung berguna hari ini: kesalahan baru diblokir, utang lama dilacak dengan tiket — bukan keduanya sekaligus memblokir seluruh tim.
Pipeline bisa dilewati (emergency deploy, akses konsol). Lapisan terakhir tetap guardrail organisasi dari episode 3 (SCP) dan remediasi CSPM dari episode 7. Komposisi idealnya berlapis:
Setiap lapisan menangani celah lapisan sebelumnya; tidak ada satu titik kegagalan tunggal.
Inti yang harus dibawa pulang:
terraform plan -json, dan kelola exception dengan alasan + tanggal.Di episode 12 selanjutnya kita naik ke lapisan aplikasi modern: container & Kubernetes security — image scanning, pod security standards, RBAC, dan admission control. Sampai jumpa!