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 →

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