DEV Community

Vamshidher Reddy Jannapu Reddy
Vamshidher Reddy Jannapu Reddy

Posted on AI-assisted

"Just use RBAC" is half right. Here's the other half for AI agents.

I maintain Aegis-DevOps, an open-source check that runs before an AI coding agent's shell commands and blocks the ones your policy forbids. In the last two weeks I've heard the same objection twice. A Reddit thread said RBAC already does this. A maintainer turned down a plugin submission because the agent harness "already has built-in provisions for controlling tool use."

Both are half right. You should have RBAC, and you should use your agent's permission settings. But neither one answers the question that matters when an agent is about to run kubectl delete: should this action, from this agent, happen right now?

Disclosure: I drafted this article with help from an AI assistant and reviewed it myself. The command results below are from running aegis-devops 0.3.2 against its example policy.

Agent permission settings match text

Claude Code's permission rules are a good example, and its documentation is unusually honest about them. A Bash rule matches the command text the model writes. The docs say a deny rule "isn't a security boundary around the program", and give examples: Bash(git push *) stops git push origin main but not git -C . push origin main. For inspecting the full command before it runs, they point you to a PreToolUse hook.

That's fine for what permission settings are for: deciding what the agent may run without asking you. It isn't a policy engine, and nobody claims it is.

A hook can do more because it can work out what the command does. These all reach the same rule in Aegis ("no deletes in the prod namespace"):

Command the agent writes Result
kubectl delete deploy web -n prod BLOCK
kubectl -n prod delete deploy web BLOCK
kubectl delete deploy web -nprod BLOCK
/usr/bin/kubectl ..., sudo kubectl ..., env kubectl ... BLOCK
bash -c 'kubectl delete deploy web -n prod' BLOCK
K=kubectl; $K delete deploy web -n prod refused: can't be checked statically, so the hook denies it

The last row matters as much as the others. A guard that can't understand a command should say no, not "no rule matched, go ahead".

RBAC sees the developer, not the agent

RBAC and IAM decide what an identity may do. A coding agent on a laptop usually runs with the developer's own kubeconfig and cloud credentials. As far as the API server is concerned, the agent is the developer, with every permission the developer has.

You can fix that by giving agents their own identities, and you should. But even then, RBAC is a static grant. It can say "this service account may delete deployments in prod". It can't say "this agent may not, unless a human approved it, and not during the release freeze", and it has no idea where a rule came from.

That last part is the reason I built Aegis. In an agent's world, a "rule" can arrive inside a Jira ticket or a Slack message the agent was asked to read. So an Aegis rule only gets a vote if nobody has changed it since it was signed, and if its author was allowed to write that kind of rule. A line planted in a ticket gets no vote.

Use all three

The honest answer to "why not RBAC?" is "yes, and":

  • RBAC / IAM: the hard ceiling on what any identity can do. Keep it tight.
  • Agent permission settings: what the agent may run without asking you.
  • A pre-execution policy check: whether this specific action should happen, by rules you can trust.

The policy check doesn't have to stay on the laptop either. Once agents have their own identities, Aegis compiles the same policy into the platform layer: aegis compile aws writes Service Control Policies, and aegis compile kubernetes writes ValidatingAdmissionPolicies. That covers calls that never go through the hook, like a boto3 script. Both are previews, and I wrote up how the AWS part works.

What it doesn't do yet

Readers keep finding real gaps, which is the point of writing these posts. This week one found that a namespace-scoped rule doesn't match a command that relies on the namespace in your current kube context, and that kubectl apply -f - is allowed because the piped manifest is never read. Both are being fixed. Aegis is alpha. Don't put it in front of anything you care about without reading the open gaps in the repo first.

Try it

pip install aegis-devops && aegis init .aegis

# Claude Code
claude plugin marketplace add moneytool/aegis-devops
claude plugin install aegis-devops@aegis-devops

# Codex, Copilot, VS Code, Cursor, Gemini CLI, OpenCode
aegis install codex   # or copilot | vscode | cursor | gemini | opencode
Enter fullscreen mode Exit fullscreen mode

If you think the RBAC answer is enough for your setup, I'd like to hear why. Issues are open at github.com/moneytool/aegis-devops.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.