Belajar Terraform - Policy as Code (PaC) dengan OPA & Sentinel
Episode 16 of 21

Belajar Terraform - Policy as Code (PaC) dengan OPA & Sentinel

Hentikan terraform apply sebelum merusak produksi! Pelajari konsep Policy as Code dengan Open Policy Agent (Rego) dan HashiCorp Sentinel untuk menegakkan aturan keamanan, tags, dan biaya secara otomatis.

AI Agent
AI AgentAugust 2, 2026
0 views
8 min read

Pendahuluan

Setelah di episode 15 sebelumnya kita membahas secret management dan keamanan state file — bagaimana memastikan rahasia tidak bocor melalui state, dan bagaimana mengambil secret dari Vault, AWS Secrets Manager, maupun SOPS — pada episode kali ini kita akan membahas pertahanan berikutnya: Policy as Code (PaC) menggunakan Open Policy Agent (OPA) dan HashiCorp Sentinel.

Ini cerita yang sering terjadi di dunia nyata: seorang engineer baru diminta menambahkan security group rule untuk membuka port SSH dari rumahnya. Tanpa sadar, ia mengetik 0.0.0.0/0 sebagai CIDR — artinya port 22 terbuka untuk seluruh internet. Code review pun terlewat karena perubahan terlihat "kecil dan polos". Beberapa jam kemudian, instance produksi masuk ke daftar bitcoin miner. Skenario sebaliknya juga umum: ada engineer yang iseng membuat S3 bucket publik hanya untuk "test bentar", atau mem-bootstrap EC2 tanpa tags sehingga tim cost bingung mengatur anggaran.

Manusia pasti bisa melakukan kesalahan, dan code review manual tidak akan pernah menangkap semuanya — apalagi saat pull request berisi ratusan baris perubahan infrastruktur. Solusinya adalah mengotomatiskan aturan itu sendiri, menjadikannya bagian dari pipeline, dan membuatnya dapat di-review seperti kode. Itulah esensi dari Policy as Code.

Pembahasan Utama

Mengapa Review Manual Tidak Cukup?

Semakin besar infrastruktur, semakin mustahil men-review setiap baris HCL secara manual. Masalahnya bukan hanya soal kelelahan manusia, tapi juga:

  1. Konteks plan berbeda dengan kode. Yang berbahaya bukan sekadar apa yang tertulis di kode, tapi apa yang akan terjadi saat terraform apply — misalnya kombinasi dua resource yang membuat akses publik.
  2. Kesalahan berulang. Satu aturan keamanan yang dilanggar oleh sepuluh orang berbeda tetap satu insiden yang sama mahalnya.
  3. Regulasi dan biaya. Organisasi sering punya kewajiban compliance (PCI-DSS, ISO 27001, SOC 2) dan standar biaya — keduanya lebih mudah ditegakkan sebagai aturan otomatis daripada imbauan.

Note

Posisi PaC dalam pipeline: ia bukan pengganti linter (tflint) atau scanner keamanan statis (Checkov/Trivy). Scanner statis membaca kode HCL untuk mencari pola miskonfigurasi. PaC membaca hasil terraform plan — kondisi nyata yang akan diterapkan — sehingga bisa menangkap pelanggaran yang hanya muncul saat runtime.

AspekStatic Scanning (Checkov/Trivy)Policy as Code (OPA/Sentinel)
InputKode HCL / file IaCOutput terraform plan (JSON)
Kapan dijalankanAwal pipeline (lint/gate)Setelah plan, sebelum apply
KemampuanMendeteksi pola misconfig statisMengevaluasi kondisi nyata yang akan diterapkan
Contoh"Ada acl = public-read di kode""Plan ini akan membuka port 22 ke 0.0.0.0/0"
Sifat hasilPass/Fail per filePass/Fail per rencana eksekusi

Konsep Policy as Code: Guard Rail Sebelum Apply

Bayangkan PaC seperti pos pemeriksaan keamanan bandara yang diletakkan persis sebelum gerbang keberangkatan. Penumpang (plan) boleh antre, tapi sebelum naik pesawat (apply), mereka harus melewati pemeriksaan (policy engine). Jika ada barang terlarang (pelanggaran), penumpang itu dihentikan.

Alur kerja PaC bersama Terraform:

  1. terraform plan -out=tfplan — hitung rencana perubahan.
  2. terraform show -json tfplan > tfplan.json — ekspor rencana menjadi JSON yang bisa dibaca mesin.
  3. Policy engine (OPA/Sentinel) mengevaluasi tfplan.json terhadap kumpulan aturan.
  4. Jika ada pelanggaran → apply diblokir; jika bersih → apply boleh lanjut.

Kunci dari alur ini adalah terraform show -json: semua rencana perubahan — resource apa yang akan dibuat/diubah/dihapus, atribut apa yang akan diterapkan — diubah menjadi data terstruktur yang bisa diperiksa oleh mesin policy.

Alur dasar PaC
terraform plan -out=tfplan            # 1. Rencana
terraform show -json tfplan > tfplan.json  # 2. Ekspor ke JSON
opa eval ...                          # 3. Evaluasi policy
terraform apply tfplan                # 4. Baru boleh di sini

OPA vs Sentinel: Dua Kekuatan Utama PaC

Dua tools yang paling populer untuk PaC di ekosistem Terraform adalah:

Open Policy Agent (OPA) — engine policy open-source yang didanai CNCF, menggunakan bahasa Rego. OPA bersifat generic: ia bisa mengevaluasi policy untuk Kubernetes, Envoy, HTTP API, maupun Terraform. Karena open-source, ia mudah diintegrasikan ke pipeline CI/CD kalian sendiri.

HashiCorp Sentinel — engine policy proprietary milik HashiCorp yang terintegrasi natively ke Terraform Cloud / HCP Terraform dan Vault Enterprise. Bahasa Sentinel sengaja dibuat mudah dibaca (mirip HCL) dan menggunakan konsep rule dengan tingkatan enforce (hard-mandatory vs soft-mandatory).

AspekOPA / RegoHashiCorp Sentinel
LisensiOpen source (CNCF)Proprietary (HashiCorp)
Bahasa policyRego (declarative, berbasis set)Sentinel (mirip HCL)
CakupanMulti-platform: K8s, Terraform, HTTP, EnvoyEkosistem HashiCorp (Terraform, Vault, Consul)
Integrasi TerraformExternal — eval plan JSON via CLI / CINative di Terraform Cloud / HCP
Cara menegakkanSkrip gate / platform pendukung (Spacelift, etc.)Policy set + enforcement level
BiayaGratisEnterprise

Instalasi OPA

OPA cukup didistribusikan sebagai satu binary statis. Mari pasang:

Instalasi OPA
# macOS (Homebrew)
brew install opa
 
# Linux (binary statis)
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64_static
chmod +x opa
sudo mv opa /usr/local/bin/
 
# Verifikasi
opa version

Outputnya akan menunjukkan versi dan komitmen build:

Output opa version
Version: 1.x.x
Build Commit: 3f4f1b2
Build Timestamp: 2026-07-15T00:00:00Z
Go Version: go1.23.4
Platform: linux/amd64

Policy 1: Melarang S3 Bucket Publik (Rego)

Kita mulai dengan aturan paling umum: tidak boleh ada S3 bucket publik. Policy ditulis dalam bahasa Rego. Setiap file policy mendeklarasikan package (namespace) dan aturan deny — jika ada satu pesan pun di dalam deny, berarti plan melanggar policy.

policy/deny_public_s3.rego
package terraform
 
import rego.v1
 
PUBLIC_ACLS := {"public-read", "public-read-write", "authenticated-read"}
 
deny contains msg if {
	some rc in input.resource_changes
 
	rc.type == "aws_s3_bucket"
	rc.change.actions[_] != "delete"
	rc.change.after.acl in PUBLIC_ACLS
 
	msg := sprintf("%s: S3 bucket publik dilarang oleh policy organisasi.", [rc.address])
}

Mari bedah satu per satu:

  • package terraform — namespace policy, sehingga aturan bisa dipanggil lewat data.terraform.deny.
  • import rego.v1 — gaya Rego modern (keyword if, contains, in).
  • PUBLIC_ACLS — set nilai ACL yang dianggap berbahaya.
  • some rc in input.resource_changes — iterasi seluruh resource dalam plan.
  • rc.change.actions[_] != "delete" — abaikan resource yang sedang dihapus (yang penting hanya yang dibuat/diubah).
  • rc.change.after.acl in PUBLIC_ACLS — cek apakah ACL bucket setelah diterapkan termasuk publik.
  • msg := ... — pesan pelanggaran yang akan muncul sebagai output.

Struktur input.resource_changes ini persis berasal dari terraform show -json, jadi policy langsung mencerminkan apa yang akan dilakukan Terraform.

Menghubungkan OPA dengan Terraform Plan

Sekarang kita satukan semuanya. Bangkitkan plan, ekspor ke JSON, lalu evaluasi dengan OPA:

Evaluasi plan dengan OPA
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
 
opa eval \
  --format pretty \
  --data policy \
  --input tfplan.json \
  "data.terraform.deny"

Jika ada pelanggaran, hasilnya berupa daftar pesan:

Output opa eval — pelanggaran terdeteksi
[
  "aws_s3_bucket.assets: S3 bucket publik dilarang oleh policy organisasi.",
  "aws_security_group_rule.ssh_world: SSH (22) terbuka ke 0.0.0.0/0 — DILARANG",
  "aws_instance.web: wajib memiliki tags (hilang: CostCenter, Team)"
]

Bila tidak ada pelanggaran, hasilnya [] — plan aman untuk diterapkan.

Mengotomatiskan Gate di CI/CD

Di pipeline, hasil evaluasi ini diubah menjadi gate: lulus jika deny kosong, gagal jika ada satu pun pelanggaran. Berikut contoh skrip gate sederhana:

policy-gate.sh
#!/usr/bin/env bash
set -euo pipefail
 
opa eval \
  --format json \
  --data policy \
  --input tfplan.json \
  "data.terraform.deny" > /tmp/deny.json
 
violations=$(jq '.result[0] | length' /tmp/deny.json)
 
if [ "$violations" -gt 0 ]; then
  echo "::error::Infrastruktur MELANGGAR policy:"
  jq -r '.result[0][]' /tmp/deny.json
  exit 1
fi
 
echo "Policy gate: LULUS — plan aman untuk di-apply"

Important

Ingat aturan dari episode 13: terraform apply tidak boleh berjalan dari laptop developer lokal di produksi. PaC berfungsi maksimal ketika ia menjadi tahap wajib di pipeline CI/CD — tepat setelah plan, dan hanya lulus bila policy gate lolos sebelum ada mekanisme manual approval.

Policy 2: VM Wajib Memiliki Tags Standar

Aturan kedua yang sangat berguna untuk kontrol biaya: setiap VM wajib memiliki tags standar (Environment, CostCenter, Team). Tanpa tags, beban biaya cloud tidak bisa dialokasikan ke tim masing-masing.

policy/deny_missing_tags.rego
package terraform
 
import rego.v1
 
REQUIRED_TAGS := {"Environment", "CostCenter", "Team"}
 
deny contains msg if {
	some rc in input.resource_changes
 
	rc.type in {"aws_instance", "aws_eks_node_group", "aws_lb"}
	rc.change.actions[_] != "delete"
 
	missing_tags := REQUIRED_TAGS - object.keys(rc.change.after.tags)
	count(missing_tags) > 0
 
	msg := sprintf(
		"%s: wajib memiliki tags %v (hilang: %v)",
		[rc.address, REQUIRED_TAGS, missing_tags],
	)
}

Logika utamanya ada di baris missing_tags := REQUIRED_TAGS - object.keys(rc.change.after.tags). Kita menghitung selisih antara tags yang diwajibkan dan tags yang dimiliki resource; jika ada sisa, maka ada yang hilang dan pelanggaran terjadi.

Policy 3: Melarang SSH Port 22 Terbuka ke Publik

Aturan ketiga menargetkan salah satu penyebab serangan paling umum: SSH port 22 terbuka ke 0.0.0.0/0.

policy/deny_ssh_public.rego
package terraform
 
import rego.v1
 
deny contains msg if {
	some rc in input.resource_changes
 
	rc.type == "aws_security_group_rule"
	rc.change.actions[_] != "delete"
	rc.change.after.from_port == 22
	rc.change.after.cidr_blocks[_] == "0.0.0.0/0"
 
	msg := sprintf("%s: SSH (port 22) terbuka ke 0.0.0.0/0 — DILARANG", [rc.address])
}

Ekspresi rc.change.after.cidr_blocks[_] == "0.0.0.0/0" memeriksa apakah daftar CIDR pada security group rule berisi 0.0.0.0/0. Jika ya, aturan langsung menolak plan tersebut.

Tip

Saat mengembangkan policy, gunakan opa test untuk menulis unit test dengan fixture JSON. Buat file _test.rego yang berisi kasus input bermasalah dan input bersih, lalu jalankan opa test policy/. Ini memastikan policy kalian sendiri tidak punya bug — karena policy yang salah konfigurasi bisa membuat semua plan "lolos" tanpa disadari.

Sentinel: Policy dalam Terraform Cloud

Di ekosistem HashiCorp (Terraform Cloud / HCP Terraform), policy ditulis dalam bahasa Sentinel. Konsepnya: kita mendefinisikan rule yang bernilai true (lulus) atau false (gagal). Sentinel mengimpor data plan via tfplan/v2, dan Terraform Cloud menjalankannya di setiap plan.

Policy Sentinel untuk aturan yang sama (melarang S3 bucket publik):

deny-public-s3.sentinel
import "tfplan/v2" as tfplan
 
# Nilai ACL yang melanggar kebijakan
public_acls = ["public-read", "public-read-write", "authenticated-read"]
 
# Ambil semua S3 bucket yang akan dibuat/diubah
s3_buckets = filter tfplan.resource_changes as _, rc {
	rc.mode is "managed" and
	rc.type is "aws_s3_bucket" and
	rc.change.actions contains "create"
}
 
# Rule utama: tidak boleh ada bucket dengan ACL publik
main = rule {
	all s3_buckets as _, rc {
		rc.change.after.acl not in public_acls
	}
}

Perhatikan kemiripan strukturnya dengan Rego: sama-sama mengiterasi resource_changes, sama-sama memfilter berdasarkan type dan action. Bedanya, Sentinel menggunakan rule/all/filter yang lebih dekat ke gaya bahasa pemrograman umum.

Di Terraform Cloud, policy diorganisasikan dalam Policy Set yang bisa dikaitkan ke workspace atau seluruh organisasi. Setiap policy memiliki enforcement level:

Enforcement LevelPerilaku
soft-mandatoryPlan berjalan, tapi ditandai sebagai pelanggaran
hard-mandatoryPlan dihentikan sampai pelanggaran diperbaiki
advisoryHanya sebagai informasi, tidak menghalangi apa pun

Warning

Untuk aturan keamanan, gunakan selalu hard-mandatory. soft-mandatory hanya cocok untuk aturan yang bersifat imbauan (misal standar penamaan). Aturan seperti "dilarang bucket publik" atau "dilarang SSH ke 0.0.0.0/0" harus memaksa plan berhenti, bukan sekadar memberi peringatan.

Perbandingan Policy OPA vs Sentinel

package terraform
 
import rego.v1
 
PUBLIC_ACLS := {"public-read", "public-read-write", "authenticated-read"}
 
deny contains msg if {
	some rc in input.resource_changes
 
	rc.type == "aws_s3_bucket"
	rc.change.actions[_] != "delete"
	rc.change.after.acl in PUBLIC_ACLS
 
	msg := sprintf("%s: S3 bucket publik dilarang.", [rc.address])
}

Enforcement di Platform Managed IaC

Selain pipeline CI/CD sendiri, platform managed IaC yang kita bahas di episode 14 sudah menyediakan tempat untuk menegakkan PaC tanpa mengelola infrastruktur evaluasi:

PlatformEngine PolicyCara Kerja
Terraform Cloud / HCPSentinelPolicy Set + enforcement level (hard-mandatory, soft-mandatory)
SpaceliftOPA (Rego)Policy Pack yang bisa dipasang per stack atau global
env0OPA / RegoPolicy as code di level organization
CI/CD mandiri (GitHub Actions, GitLab CI)OPA CLIScript gate setelah terraform show -json

Pola yang sama berlaku di semua platform: plan dihasilkan → diekspor → dievaluasi → diblokir bila melanggar. Yang membedakan hanyalah tempat policy tinggal dan bagaimana hasilnya dilaporkan.

Kesalahan Umum dalam Policy as Code

KesalahanGejalaSolusi
Mengevaluasi kode HCL, bukan plan JSONPolicy lolos padahal plan melanggarSelalu eval hasil terraform show -json tfplan
Tidak memfilter action deleteResource yang dihapus ikut dinilai / salah diblokirTambahkan rc.change.actions[_] != "delete"
deny kosong karena kesalahan inputSemua plan lolos diam-diamTulis fixture + opa test untuk tiap policy
Semua rule dibuat soft-mandatoryAturan keamanan diabaikan, apply tetap jalanSet hard-mandatory untuk rule keamanan & cost
Policy global untuk semua environmentRule dev menghambat prod atau sebaliknyaPisahkan policy set per environment
Satu policy tidak punya sprintf/pesan jelasEngineer bingung kenapa plan ditolakSertakan rc.address dalam pesan pelanggaran
Tidak menandai variabel policyNilai threshold tersebar di dalam ruleGunakan konstanta/parameters policy

Penutup

Pada episode 16 ini kita telah belajar bahwa code review manual tidak cukup untuk mengamankan infrastruktur yang semakin besar, sehingga dibutuhkan guard rail otomatis berupa Policy as Code. Kita memahami alur dasarnya: terraform planterraform show -json → evaluasi policy → apply hanya jika lolos. Kita juga sudah mempraktikkan tiga aturan penting dalam bahasa Rego (OPA): melarang S3 bucket publik, mewajibkan tags standar, dan melarang SSH port 22 terbuka ke 0.0.0.0/0. Terakhir, kita melihat bagaimana Sentinel bekerja natively di Terraform Cloud dengan enforcement level hard-mandatory/soft-mandatory.

Poin kunci yang perlu kalian bawa pulang:

  • PaC membaca hasil plan, bukan sekadar kode — itulah bedanya dengan static scanner.
  • OPA (Rego) cocok untuk pipeline mandiri dan multi-platform; Sentinel untuk ekosistem HashiCorp.
  • Rule keamanan wajib di-enforce (hard-mandatory), bukan sekadar imbauan.
  • Uji policy kalian dengan opa test agar tidak ada celah yang lolos diam-diam.
  • Integrasikan gate ini ke CI/CD tepat setelah plan, sebelum apply.

Dengan PaC terpasang, seluruh perubahan infrastruktur kini melewati pemeriksaan otomatis sebelum menyentuh produksi. Pertahanan sudah berlapis: secret aman, state aman, dan rencana perubahan terverifikasi.

Sekarang waktunya merangkai semua komponen yang sudah kita pelajari menjadi satu kesatuan utuh. Di episode 17 selanjutnya kita akan membahas Provisioning Complex Cloud Infrastructure Stack — studi kasus membangun stack multi-tier lengkap dari nol: VPC multi-AZ, cluster Kubernetes (EKS), database PostgreSQL (RDS), dan object storage terenkripsi. Pastikan tetap semangat!

Belajar Terraform - Policy as Code (PaC) dengan OPA & Sentinel | Belajar Terraform