Skip to content

Commit ce2a9b6

Browse files
committed
List and table styles to opa-conftest-policy-as-code-infrastructure-guardrails article
1 parent ffdd55d commit ce2a9b6

2 files changed

Lines changed: 238 additions & 103 deletions

File tree

  • src/content/articles/opa-conftest-policy-as-code-infrastructure-guardrails

‎src/content/articles/opa-conftest-policy-as-code-infrastructure-guardrails/index.mdx‎

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ featured: true
1515

1616
A developer opens a pull request for a Terraform change. Sixteen hours later, a security review rejects it: the S3 bucket lacks encryption. The developer fixes it, waits another day for re-review. This cycle — write, wait, reject, fix, wait — drains velocity and breeds resentment toward security processes.
1717

18-
Shift-left advocates say to check policies earlier. But "earlier" often means CI, which still means waiting for pipelines after pushing code. Real shift-left means __before the commit__ — policy checks that run in seconds during `git commit`, catching violations while the context is fresh and the fix is trivial.
19-
20-
OPA (Open Policy Agent) and Conftest make this possible. OPA is a general-purpose policy engine that evaluates structured data against policies written in Rego. Conftest wraps OPA with ergonomic defaults for infrastructure files — it parses Terraform, Kubernetes YAML, and Dockerfiles into JSON that OPA can evaluate. Together, they provide fast, local policy enforcement that doesn't require cloud credentials or pipeline execution.
18+
Shift-left advocates say to check policies earlier. But "earlier" often means CI, which still means waiting for pipelines after pushing code. Real shift-left means _before the commit_ — policy checks that run in seconds during `git commit`, catching violations while the context is fresh and the fix is trivial.
2119

2220
<Callout type="warning">
2321
Policy adoption paradox: comprehensive policies with slow feedback get disabled. Minimal policies with fast feedback get expanded. Start with five critical policies — encryption enabled, no public access, resource limits, no privileged containers, no `:latest` tags — that run in under two seconds. Get adoption first, then add coverage.
2422
</Callout>
2523

24+
OPA (Open Policy Agent) and Conftest make this possible. OPA is a general-purpose policy engine that evaluates structured data against policies written in Rego. Conftest wraps OPA with ergonomic defaults for infrastructure files — it parses Terraform, Kubernetes YAML, and Dockerfiles into JSON that OPA can evaluate. Together, they provide fast, local policy enforcement that doesn't require cloud credentials or pipeline execution.
25+
2626
## Pre-commit Hooks for Instant Feedback
2727

2828
Pre-commit is where shift-left becomes real. Conftest integrates with the pre-commit framework to run policies against staged files before they're committed. The key is speed — pre-commit hooks that take more than a few seconds get disabled.
@@ -57,32 +57,33 @@ Pre-commit only runs against staged files by default, which keeps evaluation fas
5757
The pre-commit framework shown above is language-agnostic, but each ecosystem has its canonical approach:
5858
5959
<Table
60-
variant="vertical-column-delineation-table"
60+
variant="grid-and-accent-header-table"
61+
fullWidth={false}
6162
content={{
63+
figure: 'Pre-commit tools by ecosystem.',
6264
thead: {
6365
th: ['Ecosystem', 'Pre-commit Tool', 'Installation'],
6466
},
6567
tbody: {
6668
tr: [
6769
{
6870
th: 'Python',
69-
td: ['pre-commit', '`pip install pre-commit && pre-commit install`'],
71+
td: ['pre-commit', '<span class="font-mono">pip install pre-commit && pre-commit install</span>'],
7072
},
7173
{
7274
th: 'Node.js',
73-
td: ['Husky + lint-staged', '`npx husky init`'],
75+
td: ['Husky + lint-staged', '<span class="font-mono">npx husky init</span>'],
7476
},
7577
{
7678
th: 'Ruby/Rails',
77-
td: ['Overcommit', '`gem install overcommit && overcommit --install`'],
79+
td: ['Overcommit', '<span class="font-mono">gem install overcommit && overcommit --install</span>'],
7880
},
7981
{
8082
th: 'Go',
81-
td: ['Lefthook', '`go install github.com/evilmartians/lefthook`'],
83+
td: ['Lefthook', '<span class="font-mono">go install github.com/evilmartians/lefthook</span>'],
8284
},
8385
],
8486
},
85-
figure: 'Pre-commit tools by ecosystem.',
8687
}}
8788
/>
8889
@@ -95,15 +96,18 @@ Speed is non-negotiable: if policy checks take more than a few seconds, develope
9596
Four principles separate policies that get adopted from policies that get bypassed:
9697

9798
<List
98-
variant="check-icons-list"
99+
variant="timeline-list"
100+
classes={{
101+
wrapper: "sm:ml-6 sm:mr-12 mb-6",
102+
}}
99103
items={[
100104
{
101105
lead: 'Single responsibility.',
102-
text: 'Each rule should check one thing. A 50-line rule that validates images, resources, and labels produces confusing violations and resists modification. Split concerns into separate rules: `deny_unauthorized_image`, `deny_missing_resources`, `deny_missing_labels`. When a violation fires, developers know exactly what to fix.',
106+
text: 'Each rule should check one thing. A 50-line rule that validates images, resources, and labels produces confusing violations and resists modification. Split concerns into separate rules: <span class="font-mono">deny_unauthorized_image</span>, <span class="font-mono">deny_missing_resources</span>, <span class="font-mono">deny_missing_labels</span>. When a violation fires, developers know exactly what to fix.',
103107
},
104108
{
105109
lead: 'Actionable messages.',
106-
text: '"Policy violation" tells the developer nothing. "Container missing resource limits" is better. "Container \'nginx\' missing resource limits. Add `spec.containers[].resources.limits.cpu` and `memory`" is what they actually need. Include the resource name, the violation, and the fix path.',
110+
text: '"Policy violation" tells the developer nothing. "Container missing resource limits" is better. "Container \'nginx\' missing resource limits. Add <span class="font-mono">spec.containers[].resources.limits.cpu</span> and <span class="font-mono">memory</span>" is what they actually need. Include the resource name, the violation, and the fix path.',
107111
},
108112
{
109113
lead: 'Minimal false positives.',
@@ -130,24 +134,61 @@ Examples: "Deployment 'api-server': Missing required label 'team'. Fix: Add meta
130134

131135
Organize policies by technology, then resource type, then concern. This structure makes policies discoverable and enables selective evaluation:
132136

133-
```text
134-
policies/
135-
├── kubernetes/
136-
│ ├── pods/
137-
│ │ ├── privileged.rego
138-
│ │ ├── resources.rego
139-
│ │ └── images.rego
140-
│ └── common/
141-
│ └── helpers.rego
142-
├── terraform/
143-
│ └── aws/
144-
│ ├── s3.rego
145-
│ ├── iam.rego
146-
│ └── security_groups.rego
147-
└── data/
148-
└── allowed_registries.json
149-
```
150-
Code: Policy directory structure.
137+
<FileExplorer
138+
figure="Policy directory structure."
139+
data={[
140+
{
141+
type: 'folder',
142+
name: 'policies',
143+
children: [
144+
{
145+
type: 'folder',
146+
name: 'kubernetes',
147+
children: [
148+
{
149+
type: 'folder',
150+
name: 'pods',
151+
children: [
152+
{ type: 'file', name: 'privileged.rego', comment: 'Blocks privileged pod configurations' },
153+
{ type: 'file', name: 'resources.rego', comment: 'Requires pod resource settings' },
154+
{ type: 'file', name: 'images.rego', comment: 'Restricts approved container registries' },
155+
],
156+
},
157+
{
158+
type: 'folder',
159+
name: 'common',
160+
children: [
161+
{ type: 'file', name: 'helpers.rego', comment: 'Shared helpers for policy reuse' },
162+
],
163+
},
164+
],
165+
},
166+
{
167+
type: 'folder',
168+
name: 'terraform',
169+
children: [
170+
{
171+
type: 'folder',
172+
name: 'aws',
173+
children: [
174+
{ type: 'file', name: 's3.rego', comment: 'Checks S3 bucket guardrails' },
175+
{ type: 'file', name: 'iam.rego', comment: 'Checks unsafe IAM permissions' },
176+
{ type: 'file', name: 'security_groups.rego', comment: 'Checks security group exposure' },
177+
],
178+
},
179+
],
180+
},
181+
{
182+
type: 'folder',
183+
name: 'data',
184+
children: [
185+
{ type: 'file', name: 'allowed_registries.json', comment: 'Approved container registry allowlist' },
186+
],
187+
},
188+
],
189+
},
190+
]}
191+
/>
151192

152193
Conftest uses the `--namespace` flag to selectively evaluate policies. Run only Kubernetes policies on YAML files with `conftest test deployment.yaml --namespace kubernetes`. Run only Terraform AWS policies with `conftest test tfplan.json --namespace terraform.aws`. This is how namespace organization pays off — you can run subsets of policies based on context.
153194

0 commit comments

Comments
 (0)