Implementing Compliance as Code#
Compliance as code encodes compliance requirements as machine-readable policies evaluated automatically, continuously, and with every change. Instead of quarterly spreadsheet audits, a policy like “all S3 buckets must have encryption enabled” becomes a check that runs in CI, blocks non-compliant Terraform plans, and scans running infrastructure hourly. Evidence generation is automatic. Drift is detected immediately.
Step 1: Map Compliance Controls to Technical Policies#
Translate your compliance framework’s controls into specific, testable technical requirements. This mapping bridges auditor language and infrastructure code.
SOC2 Trust Service Criteria#
| SOC2 Control | Technical Requirement | Enforcement Point |
|---|---|---|
| CC6.1 Logical Access | RBAC with least privilege, no cluster-admin for workloads | Kubernetes admission, IAM policy |
| CC6.3 Role-Based Access | Service accounts per workload, no shared credentials | Kubernetes admission |
| CC6.6 System Boundaries | Default-deny network policies in all namespaces | Kubernetes admission |
| CC6.8 Prevent Unauthorized Software | Only signed images from approved registries | Kubernetes admission |
| CC7.1 Monitoring | Audit logging enabled, logs shipped to central SIEM | Infrastructure as code, runtime check |
| CC7.2 Anomaly Detection | Alerting on privilege escalation, unusual API access | Runtime monitoring |
| CC8.1 Change Management | All changes via GitOps, no manual kubectl apply | Kubernetes admission, CI gate |
PCI-DSS v4.0#
| PCI-DSS Requirement | Technical Requirement | Enforcement Point |
|---|---|---|
| 1.2.1 Network segmentation | Network policies isolating cardholder data environment | Kubernetes admission |
| 2.2.1 Secure configurations | CIS benchmark pass on all nodes and containers | Infrastructure scan, admission |
| 6.3.2 Vulnerability management | No critical/high CVEs in deployed images | CI gate, admission |
| 7.2.1 Access control | RBAC policies, no default service account usage | Kubernetes admission |
| 8.3.1 MFA | OIDC with MFA for cluster access | Identity provider configuration |
| 10.2.1 Audit trails | API server audit logs with 90-day retention | Infrastructure as code |
| 11.3.1 Vulnerability scanning | Weekly automated scanning of running workloads | Scheduled job |
HIPAA Security Rule#
| HIPAA Standard | Technical Requirement | Enforcement Point |
|---|---|---|
| 164.312(a)(1) Access Control | RBAC, namespace isolation for PHI workloads | Kubernetes admission |
| 164.312(a)(2)(iv) Encryption | Encryption at rest and in transit (mTLS) | Infrastructure as code, admission |
| 164.312(b) Audit Controls | Complete audit trail of PHI access | API audit logs, application logs |
| 164.312(e)(1) Transmission Security | TLS 1.2+ on all endpoints, mTLS between services | Admission, network policy |
Step 2: Select and Deploy a Policy Engine#
Three tools cover most use cases. They are not mutually exclusive – use Checkov in CI and Kyverno or Gatekeeper at admission.
Checkov: Static Infrastructure Scanning#
Checkov scans Terraform, Kubernetes manifests, Helm charts, and Dockerfiles before they are applied:
# Scan Terraform files
checkov -d ./terraform/ --framework terraform
# Scan Kubernetes manifests
checkov -d ./k8s-manifests/ --framework kubernetes
# Scan Helm charts (renders templates first)
checkov -d ./helm-chart/ --framework helm
# Output in JUnit format for CI integration
checkov -d ./terraform/ -o junitxml > checkov-results.xmlCheckov includes over 2,500 built-in policies mapped to CIS benchmarks, SOC2, PCI-DSS, and HIPAA. Run only the policies relevant to your compliance scope:
# Run by compliance framework
checkov -d ./terraform/ --compliance-framework soc2
# Run specific checks
checkov -d ./terraform/ --check CKV_AWS_18,CKV_AWS_19,CKV_AWS_21OPA Gatekeeper: Kubernetes Admission Control#
Gatekeeper evaluates policies at the Kubernetes API admission point. Non-compliant resources are rejected before they are created.
Install with Helm (helm install gatekeeper gatekeeper/gatekeeper -n gatekeeper-system --create-namespace). Define constraint templates (policy logic in Rego) and apply constraints using them:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: require-compliance-labels
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
parameters:
labels:
- "app.kubernetes.io/name"
- "compliance/data-classification"
- "compliance/owner"Kyverno: Kubernetes-Native Policies#
Kyverno uses Kubernetes YAML natively – no Rego required. For teams that already manage Kubernetes resources in YAML, the learning curve is lower.
Install with Helm (helm install kyverno kyverno/kyverno -n kyverno --create-namespace). Enforce that containers must not run as root:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-non-root
annotations:
policies.kyverno.io/category: Pod Security
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Containers must run as non-root. Maps to SOC2 CC6.1,
PCI-DSS 2.2.1, HIPAA 164.312(a)(1).
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-containers
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Containers must not run as root (runAsNonRoot must be true)"
pattern:
spec:
containers:
- securityContext:
runAsNonRoot: trueKyverno can also mutate resources (inject labels, set defaults) and generate resources (create network policies when a namespace is created).
Conftest: Pre-Deployment Policy Testing#
Conftest uses OPA/Rego to test structured data files (YAML, JSON, HCL, Dockerfile) in CI before admission. Useful when you want OPA’s policy language for pre-deployment checks:
conftest test deployment.yaml -p ./policies/
terraform show -json plan.tfplan | conftest test - -p ./policies/terraform/Step 3: Integrate Policies into CI/CD#
Policies must run automatically. Manual compliance checks are compliance theater.
# GitHub Actions: compliance gates
name: Compliance Checks
on:
pull_request:
branches: [main]
jobs:
checkov-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: bridgecrewio/checkov-action@v12
with:
directory: ./terraform
framework: terraform
soft_fail: false
kyverno-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Test policies against manifests
run: kyverno apply ./policies/ --resource ./k8s-manifests/
conftest-terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
terraform -chdir=terraform init -backend=false
terraform -chdir=terraform plan -out=plan.tfplan
terraform -chdir=terraform show -json plan.tfplan > plan.json
conftest test plan.json -p ./policies/terraform/Block merges if any compliance check fails.
Step 4: Generate Audit Trails#
Auditors need evidence. Compliance as code generates evidence automatically, but you must collect and retain it.
Policy Decision Logs#
Gatekeeper logs every admission decision (allow/deny) including the resource, the policy evaluated, and the result. Configure audit logging:
# Gatekeeper audit configuration
helm upgrade gatekeeper gatekeeper/gatekeeper \
--namespace gatekeeper-system \
--set audit.logLevel=INFO \
--set audit.constraintViolationsLimit=100Kyverno generates Policy Reports as Kubernetes resources (kubectl get policyreport -n production). These list every resource evaluated, pass/fail status, and which policy was applied. Export them to your log aggregation system for long-term retention.
CI Pipeline Records and State Snapshots#
Every CI run that executes compliance checks is an audit artifact. Retain CI logs, test results (JUnit XML), and the specific policy versions evaluated. Store in tamper-evident storage (S3 with object lock or write-once retention policies).
Periodically capture the compliance state of running infrastructure:
#!/bin/bash
DATE=$(date +%Y-%m-%d)
kubescape scan framework cis-v1.23-t1.0.1 -f json -o "audit/k8s-cis-$DATE.json"
kube-bench run --json > "audit/kube-bench-$DATE.json"
kubectl get clusterpolicyreport -o json > "audit/policy-report-$DATE.json"
checkov -d ./terraform/ --framework terraform -o json > "audit/checkov-$DATE.json"
aws s3 cp audit/ s3://compliance-audit-bucket/$DATE/ --recursiveStep 5: Establish Continuous Compliance Monitoring#
Compliance at deployment is necessary but not sufficient. Configuration drifts, new CVEs emerge, and certificates expire. Continuous monitoring catches what CI cannot.
Scheduled Compliance Scans#
Run full compliance scans on a schedule against live infrastructure using Kubernetes CronJobs. Schedule kube-bench, kubescape, and Checkov scans daily and store results as audit evidence.
Gatekeeper Audit and Drift Detection#
Gatekeeper’s audit controller periodically evaluates all existing resources against constraints, catching resources created before a policy existed:
kubectl get constraints -o json | jq '.items[].status.totalViolations'For infrastructure drift, run terraform plan -detailed-exitcode on a schedule (exit code 2 means drift). Alert when actual state diverges from declared state.
Alerting on Compliance Violations#
Route violations to your alerting system with severity-based response: Critical (privilege escalation, encryption disabled) – page on-call immediately. High (missing network policies, RBAC drift) – alert within 1 hour, create incident ticket. Medium (missing labels, benchmark failures) – daily alert, backlog ticket with SLA.
Organize policies by framework (policies/soc2/, policies/pci-dss/, policies/hipaa/, policies/cis/) with annotations mapping each to its compliance control.
Common Mistakes#
- Writing policies without mapping to controls first. You end up with gaps where requirements have no policy.
- Enforcing everything immediately. Start in audit/warn mode. Switch to enforce after confirming no false positives.
- Not retaining audit evidence. Compliance checks whose results are not stored provide no audit value.
- Relying solely on admission control. Admission only applies to new resources. Pre-existing non-compliant resources require audit-mode scanning.
- Treating compliance as code as one-time setup. Review and update policies quarterly, aligned with your audit cycle.