🏗️ Terraform Lektion 12/15 ~7 Min. Experte

Policy as Code: Sentinel und OPA

Compliance-Checks vor Apply.

📝 Meine Notizen

Lernziele

Nach dieser Lektion kannst du erklären, warum Policy as Code über bloße Reviews hinausgeht, und die Unterschiede zwischen Sentinel (Terraform Cloud) und OPA/Conftest (Open Source) benennen. Du schreibst eine einfache Regel, die Pläne vor dem Apply blockiert, und weißt, wo im CI/CD-Flow Policies eingreifen.

Das Problem: Review allein reicht nicht

Dein Kollege öffnet einen PR: ein neuer S3-Bucket, eine Security Group mit 0.0.0.0/0 auf Port 22, eine RDS-Instanz ohne Verschlüsselung. Im Review steht „LGTM” — niemand hat die 400 Zeilen Plan-Diff Zelle für Zelle gelesen. Drei Tage später findet Security den offenen SSH-Port.

Manuelle Reviews skalieren nicht und sind fehleranfällig. Policy as Code codifiziert Regeln — „kein öffentlicher SSH”, „Verschlüsselung Pflicht”, „nur approved Instance-Types” — und prüft sie automatisch gegen jeden Plan, bevor Apply möglich ist.

Wo Policies eingreifen

Zwei Einhängepunkte:

  1. Pre-Apply (Plan-Phase): Policy evaluiert den JSON-Plan — welche Ressourcen würden erstellt/geändert? Blockiert oder warnt vor dem Apply.
  2. Static (HCL-Phase): Tools wie checkov/tfsoc scannen die .tf-Dateien ohne Plan — ergänzend, nicht ersetzend (Lektion 11).

Policy-as-Code auf Plan-Ebene sieht effektive Werte nach Variablen-Auflösung — genau das, was wirklich in der Cloud landen würde.

Sentinel — HashiCorps Policy-Sprache

Sentinel läuft nativ in HCP Terraform / Terraform Cloud. Du definierst Policies in .sentinel-Dateien und enforcest sie pro Workspace:

# deny-public-sg.sentinel
import "tfplan/v2" as tfplan

public_ingress = filter tfplan.resource_changes as _, rc {
    rc.type is "aws_security_group_rule" and
    rc.change.after.cidr_blocks contains "0.0.0.0/0" and
    rc.change.after.from_port is 22
}

main = rule {
    length(public_ingress) is 0
}

Enforcement Levels:

  • advisory — Warnung, Apply erlaubt
  • soft-mandatory — Override mit Begründung möglich
  • hard-mandatory — Apply blockiert, keine Ausnahme

Sentinel ist mächtig und an Terraform Cloud gebunden. Für Teams, die ohne TFC arbeiten, ist es keine Option — dann kommt OPA.

OPA / Conftest — cloud- und tool-agnostisch

Open Policy Agent evaluiert Regeln in Rego gegen JSON — unter anderem Terraform-Pläne, Kubernetes-Manifeste, CloudFormation. Conftest ist das CLI dafür:

# policy/deny_public_ssh.rego
package terraform.analysis

deny contains msg if {
    some rc in input.resource_changes
    rc.type == "aws_security_group_rule"
    rc.change.actions[_] == "create"
    rc.change.after.cidr_blocks[_] == "0.0.0.0/0"
    rc.change.after.from_port == 22
    msg := sprintf("SSH offen ins Internet: %s", [rc.address])
}

In CI:

terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
conftest test plan.json --policy policy/

Conftest scheitert mit Exit Code 1 bei Verletzungen — der PR blockiert. Kein Vendor-Lock-in, funktioniert in GitHub Actions, GitLab CI und lokal.

Sentinel vs. OPA — wann was?

SentinelOPA/Conftest
LaufzeitTerraform CloudÜberall (CI, lokal, Admission Controller)
SpracheSentinelRego
Inputtfplan/v2 nativJSON (Plan exportieren)
KostenTFC-LizenzOpen Source
K8s PoliciesNeinJa (gleiche Engine)

Platform-Teams mit TFC und ohne Kubernetes-Fokus: Sentinel ist integriert. Teams mit Multi-Tool-Landschaft (TF + K8s + Helm): OPA amortisiert sich über mehrere Use Cases.

Praxis: Conftest lokal gegen Plan

mkdir policy-lab && cd policy-lab
cat > main.tf <<'EOF'
resource "random_pet" "server" { length = 2 }
output "name" { value = random_pet.server.id }
EOF

cat > policy/deny_pet_prefix.rego <<'EOF'
package terraform.analysis

deny contains msg if {
    some rc in input.resource_changes
    rc.type == "random_pet"
    startswith(rc.change.after.prefix, "prod")
    msg := "random_pet mit prod-prefix nicht erlaubt in dev"
}
EOF

terraform init
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
conftest test plan.json --policy policy/   # sollte passieren

# Prefix auf prod setzen, erneut planen → conftest schlägt fehl

Für echte Security-Regeln ersetzt du random_pet durch aws_security_group_rule — das Pattern bleibt gleich.

Typische Stolperfallen

Policies nur in Prod, nicht in Dev: Regeln gelten überall — sonst testet niemand gegen sie, bis Prod blockiert.

Nur warnen, nie blocken: Advisory Policies werden ignoriert. Für Compliance-relevante Regeln: hard-mandatory oder CI-Exit 1.

Plan-JSON-Schema-Änderungen: Provider-Upgrades können Plan-JSON-Struktur ändern. Policies brauchen Wartung wie Code.

Policy-Sprache und Team-Skills: Rego hat Lernkurve. Starte mit 3–5 klaren Regeln, nicht mit 200 Zeilen Policy-Framework am ersten Tag.

Policies vs. IAM verwechseln: IAM verhindert API-Calls — Policy-as-Code verhindert, dass der Plan überhaupt approved wird. Beides ergänzt sich; Policy ersetzt keine least-privilege IAM-Rollen.

Interview-Vorbereitung

„Was ist Policy as Code bei Terraform?” — Automatische Durchsetzung von Compliance-Regeln gegen den Plan vor Apply; Sentinel in TFC, OPA/Conftest open source in CI.

Follow-ups:

  • „Sentinel vs. OPA?” — TFC-integriert vs. überall; Sentinel vs. Rego; OPA auch für K8s.
  • „Warum Plan-basiert statt HCL-basiert?” — Plan enthält aufgelöste Werte — was wirklich deployed wird.
  • „Wie integrierst du Conftest in CI?” — plan → show -json → conftest test → Exit 1 blockiert Merge.
  • „Unterschied advisory vs. mandatory?” — Warnung vs. harter Block.

Zusammenfassung

Policy as Code automatisiert Compliance-Checks auf Terraform-Plänen — jenseits dessen, was menschliche Reviews leisten. Sentinel ist die integrierte Lösung in Terraform Cloud; OPA/Conftest ist der offene Standard für CI-Pipelines und Multi-Tool-Umgebungen. Regeln gehören auf Plan-Ebene, werden hard enforced für kritische Controls und brauchen Pflege wie jeder andere Code.

In der nächsten Lektion holen wir bestehende Infrastruktur unter Terraform-Verwaltung — Import und Migration.