Last month I gave Claude access to one of our staging clusters. Within minutes it tried to kubectl exec into a pod and ran kubectl get secret -o yaml. Nothing bad happened — but it made me think: what if it had been production?
So I built kubectl-ro.
What it does
It's a thin wrapper around kubectl that only allows read-only commands. You use it exactly like kubectl:
kubectl-ro get pods -n kube-system # works
kubectl-ro logs deployment/my-app # works
kubectl-ro delete pod nginx # nope
# ✘ BLOCKED: 'delete' is a mutating command
That's it. If the command would change anything in your cluster, it gets blocked before kubectl ever sees it.
Why not just use RBAC?
You absolutely should use RBAC. But RBAC is server-side — it requires cluster admin setup, service accounts, role bindings. kubectl-ro is client-side. You install it, point your AI agent at it, and you're done. No cluster changes needed.
Think of it as a seatbelt, not a replacement for airbags.
It also protects secrets
This was the part that surprised me most. Even "read-only" kubectl can leak sensitive data:
kubectl get secret db-creds -o yaml # prints base64-encoded passwords
kubectl describe secret db-creds # same thing, different format
kubectl-ro blocks these. You can list secrets (names and types), but you can't extract values. In MCP mode, secret values are replaced with [REDACTED] automatically.
It works as an MCP server too
This is the part I'm most excited about. Run kubectl-ro serve and it becomes an MCP server with 20 read-only tools that any AI agent can use:
{
"mcpServers": {
"kubectl-ro": {
"command": "kubectl-ro",
"args": ["serve"]
}
}
}
Now your AI can list_pods, get_pod_logs, list_deployments, get_events — all the things you'd want it to see, nothing it shouldn't touch.
How the policy works
There's no config file. The policy is baked into the binary on purpose — you can't accidentally misconfigure it.
The logic is simple:
- Mutating commands (delete, apply, create, exec, scale, drain...) → always blocked
- Read commands (get, describe, logs, top, events...) → allowed, with secrets checks
- Unknown commands → blocked by default (fail-safe)
It also rejects arguments with control characters, which prevents a class of prompt injection attacks where an LLM hallucinates weird bytes.
Every action is logged
Everything goes to ~/.kubectl-ro/audit.log:
{"timestamp":"2026-03-29T13:04:36Z","action":"get pods","result":"allowed"}
{"timestamp":"2026-03-29T13:04:36Z","action":"delete pod x","result":"blocked","reason":"'delete' is a mutating command"}
So if something weird happens, you can see exactly what was attempted.
Try it
go install github.com/soyvural/kubectl-ro@latest
Or grab a binary from the releases page.
You can test the policy without running anything:
kubectl-ro --check get pods # prints: OK
kubectl-ro --check delete pod nginx # prints: BLOCKED
Put it on your PATH and it works as a kubectl plugin too: kubectl ro get pods.
The repo is at github.com/soyvural/kubectl-ro. It's MIT licensed, written in Go, and has zero external runtime dependencies.
If you're giving AI agents access to your clusters, I'd love to hear how you're handling the safety side. What's your approach?
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.