Security as Code
Encode security policy as version-controlled, testable artifacts that
Security policy that lives in a wiki gets ignored. Security policy that lives as code in the build pipeline gets enforced. The shift from policy-as-document to policy-as-code is the central move in modern DevSecOps. ## Key Points - **Open Policy Agent (OPA)**. General-purpose policy engine using the Rego language. Integrates with Kubernetes (Gatekeeper, Kyverno), Terraform, CI pipelines, and many SaaS products via webhook. - **Cedar**. Authorization-focused, simpler grammar than Rego, designed by AWS. Native to AWS Verified Permissions but usable elsewhere. - **In CI** — pre-merge: SAST policies, IaC policies, secrets policies, dependency policies. The PR check fails if a policy is violated. - **At deploy time** — in your CI/CD: artifact-signing policy, image-scanning policy, deployment policy. Deploy is rejected if violated. - **At runtime** — in Kubernetes (admission controller), in service mesh (Envoy filter), in API gateway (request validation). The request is denied at the moment of execution. - **In audit** — read-only checks against running infrastructure. They don't block; they alert when a violation is discovered post-fact. - **Documented in the policy or in a metadata system.** The exception is data, not code. - **Time-bounded.** Exceptions expire. The team renews them or fixes the underlying issue. - **Reviewed.** Exceptions are reviewed by the security team. A pile of standing exceptions is a signal the policy might be wrong. - **Logged.** When the exception is invoked, the system logs it. The audit trail is preserved. - Number of policy violations caught per week. - Mean time from violation introduction to detection.
skilldb get devsecops-pipeline-skills/Security as CodeFull skill: 143 linesSecurity policy that lives in a wiki gets ignored. Security policy that lives as code in the build pipeline gets enforced. The shift from policy-as-document to policy-as-code is the central move in modern DevSecOps.
The principle is the same as infrastructure-as-code: make the rules executable. Pull request introduces an over-permissive IAM role? The IaC scanner blocks it. Engineer tries to deploy a container running as root? The admission controller rejects it. New service requests an unencrypted connection? The mesh proxy refuses.
Policy Engines
Pick a policy engine. Two that are battle-tested:
- Open Policy Agent (OPA). General-purpose policy engine using the Rego language. Integrates with Kubernetes (Gatekeeper, Kyverno), Terraform, CI pipelines, and many SaaS products via webhook.
- Cedar. Authorization-focused, simpler grammar than Rego, designed by AWS. Native to AWS Verified Permissions but usable elsewhere.
Both express policies as small declarative documents that take an input (a request, a resource, a config) and return an allow/deny decision plus a reason. Both can run inline in the request path or in a CI gate.
For most organizations, OPA is the default. Larger ecosystem, more mature tooling, more existing policies to draw from.
Where Policies Live
The policy repo is its own thing. Versioned, reviewed, tested. Engineers don't modify policies casually; they treat them like production code.
Structure:
policies/
kubernetes/
no-privileged-containers.rego
resource-limits-required.rego
terraform/
no-public-s3-buckets.rego
encryption-required.rego
ci/
no-secrets-in-code.rego
sast-must-pass.rego
api/
auth-required.rego
rate-limit-required.rego
tests/
...mirror structure with test fixtures
Each policy is a small file with a single rule. Each policy has tests — input fixtures that should be allowed, input fixtures that should be denied. The test suite runs on every change to the policy repo.
This structure allows the security team to evolve policies confidently. Tests catch regressions; reviewed PRs document the rationale; the policies themselves become the spec for "what is allowed."
Enforcement Points
Policy gets enforced wherever the relevant decision is made:
- In CI — pre-merge: SAST policies, IaC policies, secrets policies, dependency policies. The PR check fails if a policy is violated.
- At deploy time — in your CI/CD: artifact-signing policy, image-scanning policy, deployment policy. Deploy is rejected if violated.
- At runtime — in Kubernetes (admission controller), in service mesh (Envoy filter), in API gateway (request validation). The request is denied at the moment of execution.
- In audit — read-only checks against running infrastructure. They don't block; they alert when a violation is discovered post-fact.
Each enforcement point has a different cost. CI is cheap (block before merge); admission controller is moderate (block before deploy); runtime is most expensive (the failure mode is a denied production request).
Choose enforcement points based on the policy's criticality. "Containers must not run as root" can be runtime-enforced; the cost of blocking a deploy is acceptable. "All resources must have a cost-center tag" can be audit-enforced; periodic noncompliance reports are sufficient.
Policy Authoring
Write policies that are precise, testable, and explainable.
Precise: the policy should match exactly what's prohibited, not a heuristic.
# good
deny[msg] {
input.kind == "Pod"
some i
input.spec.containers[i].securityContext.runAsUser == 0
msg := sprintf("Container %q runs as UID 0", [input.spec.containers[i].name])
}
# bad — overly broad
deny[msg] {
input.kind == "Pod"
not input.metadata.labels.approved
msg := "Pod not approved"
}
Testable: every policy comes with allow/deny test cases. The tests live with the policy.
Explainable: the deny message includes what was denied and why, in language an engineer can act on. "Container X runs as UID 0" is actionable; "Policy violation" is not.
Exception Handling
Every policy needs an exception process. Some legitimate workloads will violate a policy; the team needs a way to mark them acknowledged and proceed.
Exception design:
- Documented in the policy or in a metadata system. The exception is data, not code.
- Time-bounded. Exceptions expire. The team renews them or fixes the underlying issue.
- Reviewed. Exceptions are reviewed by the security team. A pile of standing exceptions is a signal the policy might be wrong.
- Logged. When the exception is invoked, the system logs it. The audit trail is preserved.
Common pattern: a policy admits an exempt annotation referencing a ticket. The annotation must be present, the ticket must be open, and the ticket must have approval. The annotation expires after a configured window.
Policy Telemetry
Track:
- Number of policy violations caught per week.
- Mean time from violation introduction to detection.
- Number of standing exceptions.
- Number of policies enforced.
- Coverage: what percentage of services have policies that gate them.
Trends matter more than levels. Increasing violations might mean engineers are pushing limits, or it might mean the policy set has grown. Increasing exceptions might mean the policies have gotten too strict.
Cultural Shift
Policy-as-code is partly a cultural shift. The team's relationship to security changes. Security is no longer an end-of-cycle review; it's a set of guardrails that engineers operate within. The security team's role shifts from gatekeeper to author of the guardrails.
This shift is sometimes resisted. Engineers used to working around security may find guardrails frustrating. Security teams used to reviewing every change may feel their role is diminished. Both reactions are normal; both are addressed by being explicit about the new model.
The new model:
- Engineers are responsible for satisfying the policies, not for asking permission.
- Security is responsible for the policies — they own the spec; they own the false-positive rate; they own the exception process.
- When a policy is wrong (too strict, too lenient, too noisy), the response is to fix the policy, not to disable it.
Anti-Patterns
Policy in a PDF. No automated enforcement. Engineers don't read it; auditors find out at audit time. Convert to code.
Single giant policy file. All rules in one document. Hard to test, hard to evolve. Decompose into per-rule files.
Policy without exceptions. Engineers can't ship around legitimate edge cases. They disable the engine or work around it. Build an exception process.
Untested policies. A policy update breaks unrelated workloads; nobody noticed. Tests prevent this.
Runtime enforcement only. Policies enforced only at runtime block deploys at the worst time — after the engineer has shipped. Catch them earlier in the pipeline.
Audit-only. Policies only check, never block. Engineers ignore the alerts. For critical policies, enforce; for advisory ones, audit; mix carefully.
Install this skill directly: skilldb add devsecops-pipeline-skills
Related Skills
SAST and DAST Integration in CI/CD
Integrate static and dynamic application security testing into the CI/CD
Security Monitoring and Detection
Build the detection layer that catches attacks in production — log
Software Supply Chain Security
Defend against supply-chain attacks: malicious dependencies, typosquats,
Threat Modeling in Design Reviews
Run a threat modeling session as part of a design review for any
Adversarial Code Review
Adversarial implementation review methodology that validates code completeness against requirements with fresh objectivity. Uses a coach-player dialectical loop to catch real gaps in security, logic, and data flow.
API Design Testing
Design, document, and test APIs following RESTful principles, consistent