Back to Discover

kube-guard

plugin

andresleecom

Guardrails for AI agents on Kubernetes: a Claude Code PreToolUse hook that classifies every kubectl/helm command by blast radius and allow/ask/denies it.

View on GitHub
0 starsMITSynced Aug 2, 2026

Install to Claude Code

/plugin marketplace add andresleecom/kube-guard

README

πŸ›‘οΈ kube-guard

Give your Claude Code agent kubectl β€” with a seatbelt.

A PreToolUse hook that classifies every kubectl/helm command by blast radius and allows, asks, or denies it before it runs. Reads flow through; production deletes don't.

License: MIT Version CI Zero dependencies Cross-platform Clones

kube-guard demo: kubectl get pods is allowed, kubectl apply asks for confirmation, kubectl delete namespace prod is denied as destructive on a protected context


The problem

An AI agent with kubectl access is one hallucinated command away from kubectl delete namespace prod, a drain, an apply to the wrong context, or a get secret -o yaml that leaks credentials into the transcript. Permission prompts help β€” until you run in acceptEdits or --dangerously-skip-permissions and rubber-stamp everything.

"Your AI agent should not have direct kubectl access." β€” and yet you want it to help.

The solution

kube-guard is a pure Claude Code plugin (hook + skill + command, zero dependencies) that sits in front of the agent's shell. Every Bash command is parsed; any kubectl/helm invocation is classified and gated:

ClassExamplesDefault verdict (strict)
READget, describe, logs, top, rollout status, helm listβœ… allow
WRITEapply, scale, patch, edit, rollout restart, helm upgrade⚠️ ask (confirm)
DESTRUCTIVEdelete, drain, taint, replace --force, helm uninstallπŸ›‘ deny
HIGH-RISKexec, run, cp, port-forward, config view, get secret -o yamlπŸ›‘ deny

Plus cross-cutting guards: protected contexts/namespaces (anything matching prod, production, *-prod, kube-system, …) escalate every mutation to deny, and the guard fails closed β€” if it can't verify a command (eval, pipe-to-shell, subshells, intra-word quoting like k'ubectl', unknown verbs), it asks or denies rather than guessing.

A few things the classifier is deliberately careful about:

  • --dry-run is a safe preview. apply/delete/scale … --dry-run=server|client mutate nothing, so they're recognised as no-ops and allowed (but --dry-run=none really applies, so it isn't).
  • Secret dumps are hard to sneak past. get secret -o yaml is blocked and so are -oyaml (no space), comma lists (secrets,configmaps), fully-qualified names (secret.v1.core/x), --template/--go-template, and helm get values/manifest/all.
  • RBAC privilege escalation is gated. kubectl auth reconcile, create clusterrolebinding … --clusterrole=cluster-admin, and apply --prune are escalated, not waved through as ordinary writes.
  • When it blocks, it explains. A denied command comes back with the reason and a concrete next step (--dry-run=server, /klease, -o name, …) so the agent self-corrects instead of retrying blind.
agent β†’ kubectl delete ns prod   β†’  πŸ›‘ DESTRUCTIVE on protected context β€” denied (logged)
agent β†’ kubectl apply -f d.yaml  β†’  ⚠️ WRITE β€” asks you to confirm
agent β†’ kubectl get pods         β†’  βœ… READ β€” allowed

Install

/plugin marketplace add andresleecom/kube-guard
/plugin install kube-guard@kube-guard

Requires kubectl on your PATH (which you already have). Zero npm dependencies.

Why it's different

It gates execution, even when you've turned permissions off:

  • A deny/ask from kube-guard applies regardless of permission mode β€” even with --dangerously-skip-permissions, a protected-context delete still stops.
  • It catches kubectl anywhere in a command (cd x && kubectl delete …, env-prefixes, pipes), not just as the first word.
  • It resolves your current context so guards work even when the command omits --context.
  • Every decision is written to a private, gitignored audit log (.claude/kube-guard/audit.jsonl), with secrets redacted.
kube-guardk8sgptkagentKubeShark (skill)
Gates command execution (allow/ask/deny)βœ…βŒ (read-only diag)❌ (RBAC only)❌ (advisory)
Protected-context / blast-radius guardβœ…βŒβŒβŒ
Per-context and per-namespace policiesβœ…βŒβŒβŒ
Per-resource allow/denylist (never delete nodes/CRDs/…)βœ…βŒβŒβŒ
Temporary context leasing (prod for 1 cmd / N min)βœ…βŒβŒβŒ
Blocks secret dumps & execβœ…n/a❌❌
Works in IDE, no in-cluster agentβœ…βœ…βŒβœ…
Audit logβœ…βŒpartial❌
Zero dependenciesβœ…βŒβŒβœ…

Configuration

Config is layered (later wins): plugin defaults β†’ ~/.claude/kube-guard.config.json (global, every project) β†’ <project>/.claude/kube-guard.config.json β†’ KUBE_GUARD_MODE env. Set a posture once, globally, and it holds everywhere.

Levels (postures)

Every command is judged at the level of its target context (the --context flag, or your live current context):

LevelReadsWritesDestructiveHigh-risk
readonlyallowdenydenydeny
strict (default)allowaskdenydeny
standardallowaskaskdeny
auditallowallowallowallow (logged)

Multiple clusters β†’ per-context policies

Different clusters, different postures. Map context globs to levels:

// ~/.claude/kube-guard.config.json
{
  "defaultMode": "strict",
  "contextPolicies": [
    { "match": ["*prod*", "*live*", "my-prod-cluster"], "level": "readonly" },
    { "match": ["*stag*"],                              "level": "strict"   },
    { "match": ["kind-*", "minikube", "*dev*"],         "level": "audit"    }
  ],
  // optional: give specific namespaces their own posture (composed strictest
  // with the context level, so a guarded namespace holds even on a lax cluster)
  "namespacePolicies": [
    { "match": ["prod", "*-prod"], "level": "readonly" }
  ],
  // optional: tighten-only rules per resource kind (never loosen the verdict)
  "resourceRules": [
    { "kinds": ["node", "namespace", "pvc", "*.cattle.io"],
      "verbs": ["delete", "drain", "taint"], "verdict": "deny" }
  ],
  "protectedNamespaces": ["kube-system", "prod", "*prod*"],
  "allowExec": false,        // set true to downgrade exec/cp/run from deny β†’ ask
  "allowSecretRead": false   // set true to downgrade secret dumps β†’ ask
}

Unlisted contexts fall back to defaultMode. Config is accumulated across layers for the protection arrays (contextPolicies/namespacePolicies/protected*), so a global prod guard can't be silently dropped by a project config. Legacy protectedContexts (a flat list) still works and maps to readonly. Run /kube-guard doctor to print the effective config and catch typo'd keys/levels (which would otherwise weaken the posture silently).

  • namespacePolicies β€” same shape as contextPolicies, but matched against the target namespace; the effective posture is the stricter of the context level and the namespace level.
  • resourceRules β€” { kinds, verbs, verdict } rules that can only tighten a verdict (e.g. "never delete nodes/namespaces/PVCs/CRDs"), never loosen it. With no rules configured, behaviour is unchanged.

Switch & lease (multi-cluster, safely)

  • /kctx β€” list contexts with their level and switch safely; kube-guard confirms when you enter a guarded cluster and lets dev/local through freely. Solves the "I thought I was on staging but I was on prod" footgun.
  • /klease β€” the context leash: temporarily relax a readonly (prod) cluster for one command or N minutes, then it auto-reverts. Lease only the exception you need:
    # prod, but writable (with confirmation) for the next single command:
    node "$CLAUDE_PLUGIN_ROOT/scripts/lease.mjs" my-prod-cluster --once --level strict
    

Inspect and operate with /kube-guard:

  • /kube-guard β€” show the active policy and recent decisions.
  • /kube-guard doctor β€” validate the config + install (Node/kubectl, effective config, current context's level, active leases, and warnings for typo'd keys/levels).
  • /kube-guard summarize β€” roll up the audit log (counts by verdict/class, denies by context, top denied commands; supports --since 24h, --deny-only, --context <glob>).

Or dry-run a single command through the classifier without executing it: node "$CLAUDE_PLUGIN_ROOT/scripts/explain.mjs" "kubectl delete ns prod".

FAQ

Does it slow down every Bash command? Negligibly β€” the hook fast-exits unless the command contains kubectl/helm.

What if kube-guard itself errors? It fails closed: an internal error returns ask, never a silent allow.

Can the agent bypass it? Obfuscation (eval, | sh, $(...), backtick command-substitution, base64, unknown verbs) is treated as unverifiable and denied/asked β€” and relevance is decided after tokenization, so intra-word quoting (k'ubectl' delete) can't hide the tool name either.

Can I let the agent preview risky changes? Yes β€” --dry-run=server/client is treated as a read-only preview and allowed, even on a readonly context. That's the safe way to let it show you a diff before you lease the real change.

Does it send anything anywhere? No. Everything is local; the only outbound calls are the kubectl reads you'd run anyway.

Windows? Yes β€” pure Node.js hooks, no bash/jq.

Security

See SECURITY.md for the threat model. kube-guard is a guardrail, not a sandbox: it reduces blast radius but does not replace Kubernetes RBAC. Combine it with least-privilege credentials.

Roadmap

  • GitOps remediation: propose fixes as reviewable PRs instead of mutating the cluster.
  • Cost / right-sizing recommendations with dollar impact.
  • Incident loop: triage β†’ fix β†’ PR β†’ postmortem.
  • require dry-run + diff before apply; guard Write/Edit of dangerous manifests.
  • Blast-radius preview before destructive ops (count affected resources).
  • Per-context policies & multi-cluster awareness (v0.2.0).
  • Context leasing β€” temporary, auto-reverting prod access (v0.2.0).
  • Per-namespace policies & per-resource-kind rules.
  • --dry-run awareness β€” previews are recognised and allowed (the enabling half of "require dry-run").
  • Config validation + /kube-guard doctor; queryable audit log via /kube-guard summarize.

Contributing

Issues and PRs welcome. The plugin is deliberately small and dependency-free β€” the classifier (scripts/classify.mjs) is pure and covered by node --test, which runs in CI across {Windows, macOS, Linux} Γ— Node {18, 20, 22}. Add a failing case to test/classify.test.mjs first. See CONTRIBUTING.md for the house rules.

License

MIT Β© Andres Lee

Rendered live from andresleecom/kube-guard's GitHub README β€” not stored, always reflects the source repo.

1 Plugin

NameDescriptionCategorySource
kube-guardContext safety + guardrails for AI agents on Kubernetes: per-context policies, safe switching, temporary leases, and allow/ask/deny on every kubectl/helm command.security./

0 Comments

Login required
Log in to post a comment or update on this repo.

No comments yet β€” be the first to share an update.