Skip to main content

Compliance & Sentinel

Sentinel = HashiCorp's policy-as-code framework — บังคับ compliance ใน Terraform Cloud/Enterprise

Policy as Code คืออะไร?

แทนที่จะ document ว่า "ห้าม deploy public S3 bucket" — เขียนเป็น code ที่ enforce อัตโนมัติ

# ตัวอย่าง Sentinel policy
import "tfplan/v2" as tfplan

main = rule {
all tfplan.resource_changes as _, rc {
rc.type is "aws_s3_bucket" implies (rc.change.after.acl is not "public-read")
}
}

→ ถ้า PR มี S3 ที่ acl = "public-read" → policy fail → ไม่ให้ apply

เปรียบเทียบ Policy Tools

ToolVendorUsed ByCost
SentinelHashiCorpTerraform Cloud/Enterprise$$$ Paid
OPA / RegoCNCF (open)Anyone✅ Free
CheckovBridgecrewAnyone✅ Free
TerrascanTenableAnyone✅ Free
tfsecAquaAnyone✅ Free
KICSCheckmarxAnyone✅ Free

→ Sentinel ใช้ใน Terraform Enterprise เป็นหลัก, alternatives เป็น open-source

Sentinel ใน Terraform Cloud

Policy Sets

policies/no-public-s3.sentinel
import "tfplan/v2" as tfplan

s3_buckets = filter tfplan.resource_changes as _, rc {
rc.type is "aws_s3_bucket" and
rc.change.actions is not ["delete"]
}

main = rule {
all s3_buckets as _, bucket {
bucket.change.after.acl in ["private", null]
}
}

→ ป้องกันการสร้าง S3 ที่ public

Apply Levels

sentinel.hcl
policy "no-public-s3" {
source = "./policies/no-public-s3.sentinel"
enforcement_level = "hard-mandatory" # block apply
}

policy "instance-types" {
source = "./policies/instance-types.sentinel"
enforcement_level = "soft-mandatory" # warn, can override
}

policy "tagging" {
source = "./policies/tagging.sentinel"
enforcement_level = "advisory" # warn only
}

3 levels:

  • advisory — warn, no block
  • soft-mandatory — block but admin can override
  • hard-mandatory — block, no override

ตัวอย่าง Sentinel Policies

1. Allowed Instance Types

import "tfplan/v2" as tfplan

allowed_types = ["t3.micro", "t3.small", "t3.medium", "t3.large"]

instances = filter tfplan.resource_changes as _, rc {
rc.type is "aws_instance" and
rc.change.actions contains "create"
}

main = rule {
all instances as _, inst {
inst.change.after.instance_type in allowed_types
}
}

2. Required Tags

import "tfplan/v2" as tfplan

required_tags = ["Environment", "Owner", "CostCenter"]

main = rule {
all tfplan.resource_changes as _, rc {
rc.change.after.tags else {} is not null and
all required_tags as tag {
rc.change.after.tags contains tag
}
}
}

3. Restrict Regions

import "tfplan/v2" as tfplan

allowed_regions = ["ap-southeast-1", "ap-northeast-1"]

providers = tfplan.providers["registry.terraform.io/hashicorp/aws"]

main = rule {
all providers as p {
p.config.region in allowed_regions
}
}

4. No Public Security Groups

import "tfplan/v2" as tfplan

sgs = filter tfplan.resource_changes as _, rc {
rc.type is "aws_security_group_rule" and
rc.change.actions contains "create"
}

main = rule {
all sgs as _, sg {
sg.change.after.cidr_blocks not contains "0.0.0.0/0"
}
}

Open Policy Agent (OPA) — Free Alternative

OPA = open-source policy engine

policies/no_public_s3.rego
package terraform

deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
resource.change.after.acl == "public-read"
msg := sprintf("S3 bucket '%v' has public ACL", [resource.address])
}

ใช้:

# Generate plan JSON
terraform plan -out=tfplan
terraform show -json tfplan > plan.json

# Run OPA
opa eval --input plan.json --data policies/ "data.terraform.deny"

Checkov — Comprehensive Scanner

Checkov (ดูเพิ่ม) มี 750+ pre-built policies:

checkov -d . --framework terraform

Output:

Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"
FAILED for resource: aws_s3_bucket.data
File: /main.tf:5-10

terraform-compliance — BDD-style

terraform-compliance — Gherkin syntax

Feature: AWS Resources

Scenario: All S3 buckets must be encrypted
Given I have aws_s3_bucket defined
Then it must contain server_side_encryption_configuration
terraform-compliance -p tfplan -f features/

CI/CD Integration

GitHub Actions

.github/workflows/policy.yml
on: pull_request

jobs:
policy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3

- name: Generate Plan
run: |
terraform init
terraform plan -out=tfplan
terraform show -json tfplan > plan.json

# Checkov
- name: Checkov
uses: bridgecrewio/checkov-action@master
with:
file: plan.json
framework: terraform_plan

# OPA
- name: OPA
run: |
opa eval --input plan.json \
--data policies/ \
--format pretty \
"data.terraform.deny"

# tfsec
- name: tfsec
uses: aquasecurity/tfsec-[email protected]

Policy Library Examples

CIS Benchmark Compliance

cis-aws-1.4.0.sentinel
import "tfplan/v2" as tfplan

# CIS 1.4: Ensure S3 buckets have encryption
buckets = filter tfplan.resource_changes as _, rc {
rc.type is "aws_s3_bucket"
}

bucket_encryption_configs = filter tfplan.resource_changes as _, rc {
rc.type is "aws_s3_bucket_server_side_encryption_configuration"
}

main = rule {
all buckets as _, b {
any bucket_encryption_configs as _, ec {
ec.change.after.bucket is b.change.after.id
}
}
}

HIPAA Compliance

hipaa.sentinel
# All RDS must have encryption
import "tfplan/v2" as tfplan

dbs = filter tfplan.resource_changes as _, rc {
rc.type is "aws_db_instance"
}

main = rule {
all dbs as _, db {
db.change.after.storage_encrypted is true and
db.change.after.backup_retention_period >= 7
}
}

ตัวอย่าง: Policy Suite for Org

policies/
├── security/
│ ├── no_public_s3.sentinel
│ ├── encrypt_rds.sentinel
│ ├── no_open_security_groups.sentinel
│ └── require_kms.sentinel
├── cost/
│ ├── allowed_instance_types.sentinel
│ ├── max_storage_size.sentinel
│ └── require_savings_plan.sentinel
├── tagging/
│ ├── required_tags.sentinel
│ └── tag_format.sentinel
└── governance/
├── allowed_regions.sentinel
└── no_iam_admin_policy.sentinel

Best Practices

✅ DO:
- เริ่มจาก policy เล็กๆ (advisory) ก่อน hard-mandatory
- Document ทุก policy + reasoning
- Test policy กับ plan จริง
- Version policy + review เหมือน code
- Mix Sentinel + OPA + Checkov ครอบคลุม
- Run policy check ใน CI ทุก PR

❌ DON'T:
- ห้าม enforce ทุก policy เป็น hard-mandatory ทันที (พังทีม)
- ห้ามมี policy ที่ false positive เยอะ (จะถูก ignore)
- ห้ามคิดว่า policy = ปลอดภัย 100%
- ห้าม skip policy ใน "แค่ฉันคนเดียว"

Free vs Paid

ถ้าไม่ใช้ Terraform Cloud:

  • Checkov — comprehensive built-in policies
  • OPA — flexible, write your own
  • tfsec / Trivy / KICS / Terrascan — security scanners

ถ้าใช้ Terraform Cloud:

  • Sentinel — integrated, gating
  • OPA — Terraform Cloud supports both

สรุป

  • Policy as Code = enforce compliance through automation
  • Sentinel = HashiCorp paid (Terraform Cloud/Enterprise)
  • OPA / Checkov / tfsec = open-source alternatives
  • 3 enforcement levels: advisory, soft-mandatory, hard-mandatory
  • Common policies: tags, regions, instance types, encryption, public access
  • ใช้ใน CI ตาม PR + CD ตอน apply

ต่อไป → Trivy