Policy as Code #
Infrastruktur yang bisa diprovisioning dengan cepat adalah kemampuan yang luar biasa — tapi juga bisa menjadi risiko jika ada yang bisa membuat resource dengan konfigurasi berbahaya tanpa ada yang mendeteksinya. Policy as Code menjawab pertanyaan ini: bagaimana memastikan bahwa setiap konfigurasi Terraform yang masuk ke repository memenuhi standar keamanan, kepatuhan, dan konvensi internal — secara otomatis, setiap saat, tanpa bergantung pada reviewer yang mungkin lelah atau tidak familier dengan semua aturan.
Mengapa Policy as Code #
TANPA POLICY AS CODE:
Engineer A membuat S3 bucket dengan public access → masuk ke production
Reviewer tidak menyadari bahwa `block_public_acls = false` adalah risiko
Data leak terjadi 3 bulan kemudian
Masalah: Reviewer tidak bisa mengingat dan memeriksa semua aturan keamanan
di setiap PR, terutama ketika ada puluhan PR per minggu.
DENGAN POLICY AS CODE:
Engineer A membuat S3 bucket dengan public access
CI pipeline otomatis menjalankan Checkov
Checkov: "FAILED: CKV_AWS_21 — S3 Bucket has public access blocks disabled"
PR tidak bisa di-merge sampai masalah diperbaiki
Aturan dijalankan konsisten di setiap PR, tanpa kelelahan
Checkov: Security Scanner untuk Terraform #
Checkov adalah tool open-source yang menganalisis konfigurasi Terraform dan mendeteksi ratusan security dan compliance issue berdasarkan best practice dari berbagai framework (CIS, HIPAA, PCI-DSS, SOC2).
# Instalasi
pip install checkov
# Scan direktori konfigurasi Terraform
checkov -d infrastructure/environments/production
# Output contoh:
# Check: CKV_AWS_21: "Ensure the S3 bucket has access control list (ACL) disabled"
# FAILED for resource: aws_s3_bucket.logs
# File: /infrastructure/environments/production/main.tf:45-52
#
# Check: CKV_AWS_86: "Ensure S3 bucket has a lifecycle configuration"
# PASSED for resource: aws_s3_bucket.logs
#
# Passed checks: 42, Failed checks: 2, Skipped checks: 0
# Hanya scan file tertentu
checkov -f main.tf
# Output dalam format JSON untuk parsing
checkov -d . --output json > checkov-results.json
# Skip check tertentu (dengan alasan yang jelas)
checkov -d . --skip-check CKV_AWS_20 # Lewati check ACL publik (sudah ada WAF)
# Menonaktifkan check spesifik langsung di konfigurasi (dengan komentar alasan)
resource "aws_s3_bucket_public_access_block" "assets" {
bucket = aws_s3_bucket.assets.id
# checkov:skip=CKV_AWS_53:Public bucket diperlukan untuk static website hosting
# Website ini memang perlu public read, dilindungi oleh Cloudflare WAF
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
tfsec: Security Scanner yang Lebih Ringan #
tfsec adalah alternatif Checkov yang lebih ringan dan cepat, cocok untuk dijalankan di setiap commit.
# Instalasi
brew install tfsec # macOS
# atau
curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash
# Scan
tfsec infrastructure/
# Output dengan format yang lebih ringkas
tfsec infrastructure/ --format compact
# Hanya tampilkan severity tinggi
tfsec infrastructure/ --minimum-severity HIGH
# Custom checks dengan file konfigurasi
tfsec infrastructure/ --custom-check-dir ./security-checks/
OPA/Conftest: Custom Policy yang Lebih Fleksibel #
Untuk aturan yang lebih spesifik ke standar internal tim (konvensi naming, tag wajib, pembatasan region), OPA (Open Policy Agent) dengan Conftest memberikan fleksibilitas yang lebih besar.
# Instalasi conftest
brew install conftest
# Struktur:
policy/
├── terraform.rego ← Policy untuk konfigurasi Terraform
└── plan.rego ← Policy untuk plan output
# policy/terraform.rego — aturan konvensi internal
package main
# Semua resource harus punya tag Environment
deny[msg] {
resource := input.resource_changes[_]
resource.change.after != null
not resource.change.after.tags.Environment
msg := sprintf("Resource '%s' harus punya tag 'Environment'", [resource.address])
}
# Tag Environment harus salah satu dari nilai yang valid
deny[msg] {
resource := input.resource_changes[_]
resource.change.after != null
env := resource.change.after.tags.Environment
not valid_environments[env]
msg := sprintf("Tag Environment '%s' tidak valid. Gunakan: dev, staging, production", [env])
}
valid_environments := {"dev", "staging", "production"}
# Instance type tidak boleh lebih besar dari t3.large di non-production
warn[msg] {
resource := input.resource_changes[_]
resource.type == "aws_instance"
resource.change.after.instance_type == "m5.xlarge"
resource.change.after.tags.Environment != "production"
msg := sprintf("Instance type besar '%s' di non-production — pertimbangkan downsize", [resource.address])
}
# RDS tidak boleh punya deletion_protection = false di production
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_db_instance"
resource.change.after.tags.Environment == "production"
not resource.change.after.deletion_protection
msg := sprintf("RDS '%s' di production harus punya deletion_protection = true", [resource.address])
}
# Jalankan conftest dengan plan output
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
conftest test tfplan.json --policy policy/
# Output:
# FAIL - tfplan.json - main - Resource 'aws_instance.web' harus punya tag 'Environment'
# WARN - tfplan.json - main - Instance type besar 'module.compute.aws_instance.api' di non-production
# 1 test, 0 passed, 0 warnings, 1 failure
Integrasi ke CI Pipeline #
# .github/workflows/terraform.yml — tambahkan job security scan
security-scan:
name: Security & Policy Check
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@v12
with:
directory: infrastructure/environments/production
framework: terraform
output_format: sarif
output_file_path: checkov-results.sarif
soft_fail: false # Pipeline gagal jika ada HIGH severity
- name: Upload Checkov Results to GitHub Security Tab
uses: github/codeql-action/upload-sarif@v3
if: always() # Upload meski Checkov gagal
with:
sarif_file: checkov-results.sarif
- name: Setup Terraform (untuk generate plan)
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.7.0"
- name: Generate Plan JSON for OPA
run: |
terraform init -backend=false
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
working-directory: infrastructure/environments/production
- name: Run OPA Policy Check
run: |
curl -L -o conftest https://github.com/open-policy-agent/conftest/releases/download/v0.46.0/conftest_Linux_x86_64
chmod +x conftest
./conftest test tfplan.json --policy policy/
working-directory: infrastructure/environments/production
Blocking vs Warning #
Tidak semua policy violation harus memblokir pipeline. Menentukan mana yang blocking dan mana yang warning adalah keputusan penting.
BLOCKING (pipeline gagal, PR tidak bisa di-merge):
✗ S3 bucket dengan public access tanpa justifikasi
✗ Security group dengan 0.0.0.0/0 pada port sensitif (22, 3306, 5432)
✗ RDS tanpa encryption at rest
✗ Resource tanpa tag Environment
✗ Credential hardcoded dalam konfigurasi
WARNING (dicatat tapi tidak memblokir):
⚠ Instance type besar di non-production
⚠ RDS tanpa backup di dev environment
⚠ Resource yang belum menggunakan versi provider terbaru
⚠ Module yang menggunakan versi lama
Ringkasan #
- Policy as Code mengotomasi review keamanan — ratusan aturan dijalankan konsisten di setiap PR tanpa bergantung pada reviewer yang mungkin melewatkan sesuatu.
- Checkov adalah pilihan utama untuk security scanning umum — cepat disetup, ratusan rule built-in untuk AWS/GCP/Azure.
- tfsec lebih ringan dari Checkov — cocok untuk scan cepat di setiap commit atau lokal development.
- OPA/Conftest untuk aturan spesifik internal — tag convention, naming, pembatasan region, aturan compliance khusus.
- Integrasikan ke CI pipeline sebagai job terpisah yang berjalan setelah validate dan sebelum apply.
- Bedakan blocking vs warning — tidak semua violation perlu memblokir pipeline; terlalu banyak blocking rule membuat tim mencari cara bypass.
← Sebelumnya: Automated Apply Berikutnya: Drift Detection Automation →