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
| Tool | Vendor | Used By | Cost |
|---|---|---|---|
| Sentinel | HashiCorp | Terraform Cloud/Enterprise | $$$ Paid |
| OPA / Rego | CNCF (open) | Anyone | ✅ Free |
| Checkov | Bridgecrew | Anyone | ✅ Free |
| Terrascan | Tenable | Anyone | ✅ Free |
| tfsec | Aqua | Anyone | ✅ Free |
| KICS | Checkmarx | Anyone | ✅ 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