Build an AI Kubernetes Admission Controller with Claude API and OPA
Combine OPA Gatekeeper's policy enforcement with Claude API's reasoning to catch risky Kubernetes manifests that static rules miss — and explain the violation in plain English instead of a cryptic denial message.
OPA Gatekeeper is great at "this field must equal that value" checks. It is bad at "this deployment looks risky because the image tag is latest, there is no resource limit, and it mounts the host network — taken together that is a red flag." Claude can reason about the combination; OPA can block the request. This tool wires them together.
Architecture
kubectl apply → API server → ValidatingWebhookConfiguration
↓
FastAPI webhook service
↓
Claude API reasons over the full manifest
↓
allow / deny + human-readable reason
Setup
pip install fastapi uvicorn anthropic kubernetesWebhook Service
import base64
import json
import anthropic
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
client = anthropic.Anthropic()
REVIEW_PROMPT = """You are a Kubernetes security reviewer. Review this manifest
being submitted to the cluster and decide if it should be ALLOWED or DENIED.
Deny if you see combinations of risk, not just single flags:
- image tag "latest" combined with no resource limits
- hostNetwork or hostPID set to true without an obvious system-level reason
- privileged: true or allowPrivilegeEscalation: true
- containers running as root (no runAsNonRoot) in a namespace that isn't kube-system
- secrets mounted as environment variables instead of volumes (softer flag)
Manifest:
{manifest}
Respond with ONLY valid JSON: {{"allowed": true/false, "reason": "one sentence, specific"}}"""
def review_with_claude(manifest: dict) -> dict:
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=300,
messages=[{
"role": "user",
"content": REVIEW_PROMPT.format(manifest=json.dumps(manifest, indent=2))
}]
)
text = response.content[0].text.strip()
if text.startswith("```"):
text = text.split("```")[1].replace("json", "", 1).strip()
return json.loads(text)
@app.post("/validate")
async def validate(request: Request):
body = await request.json()
admission_request = body["request"]
uid = admission_request["uid"]
manifest = admission_request["object"]
# Skip system namespaces — Claude should not gate kube-system
namespace = manifest.get("metadata", {}).get("namespace", "default")
if namespace in ("kube-system", "kube-public"):
return admission_response(uid, allowed=True)
verdict = review_with_claude(manifest)
return admission_response(uid, allowed=verdict["allowed"], reason=verdict["reason"])
def admission_response(uid: str, allowed: bool, reason: str = "") -> JSONResponse:
return JSONResponse({
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": uid,
"allowed": allowed,
"status": {"message": reason} if reason else {},
},
})Register the Webhook
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: claude-manifest-reviewer
webhooks:
- name: review.claude.devopsboys.com
clientConfig:
service:
name: claude-reviewer
namespace: security
path: "/validate"
caBundle: <base64-ca-cert>
rules:
- apiGroups: ["apps", ""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments", "pods"]
failurePolicy: Ignore # Never block the cluster if the webhook is down
timeoutSeconds: 5
admissionReviewVersions: ["v1"]failurePolicy: Ignore is not optional here — an LLM call can time out or the API can rate-limit you. If that happens, you want deployments to go through, not the whole cluster to freeze.
What This Catches That Gatekeeper Rego Misses
Manifest: image "myapp:latest", no resource limits, runAsUser not set
Claude verdict:
{
"allowed": false,
"reason": "Image tag 'latest' with no resource limits and no runAsUser means
an untested image can run as root with unbounded memory — combine a pinned
tag, resource limits, and runAsNonUser: true before resubmitting."
}
A Rego rule catching "latest" alone gives a flood of false positives on dev namespaces. Claude reasoning over the combination is closer to what a human reviewer would flag.
Guardrails to Add Before Production
- Cache verdicts per image digest for 10 minutes — most CI pipelines redeploy the same image many times
- Set a hard 3-second timeout and fail open on timeout
- Log every denial with the full manifest for audit — never let an LLM decision be a black box
- Run this alongside Gatekeeper, not instead of it — Gatekeeper handles the deterministic rules, Claude handles judgment calls
More AI DevOps tools? Read our Build AI deployment validator with Claude API and OPA and Build AI Kubernetes resource optimizer with Claude API.
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
AI Agents for Zero-Trust Policy Generation: Where This Is Heading in 2026
Writing least-privilege IAM policies and NetworkPolicies by hand means either over-permissioning out of laziness or spending hours tracing what a service actually calls. AI agents that observe real traffic and generate tight zero-trust policies from it are becoming a practical alternative in 2026.
Build an AI Kubernetes Deployment Readiness Checker with Claude API
Build a Python CLI tool using Claude API that analyzes Kubernetes YAML manifests before deployment — catches missing resource limits, root containers, and security issues with a go/no-go score.
Build an AI Kubernetes Secret Rotation Assistant with Claude API
Secret rotation gets skipped because nobody wants to be the one who breaks production by rotating a credential something still depends on. Build a tool that maps secret usage across your cluster and safely sequences rotation with Claude API.