Controlling AI Agent Authority with OPA and Cedar: A Practical Guide to Policy-as-Code
(AI agent authorization · fine-grained access control · Policy-as-Code)
An AI agent is an autonomous system in which an LLM directly selects and executes tools. How do you control "how far" these agents, which query databases, call external APIs, and access file systems on behalf of users, can go? If you are giving instructions via prompts, that is not control. This is because a single prompt injection attack can allow an agent to execute unauthorized actions. In December 2025, OWASP released the Agentic Top 10 threat list specifically for autonomous AI agents, classifying prompt injection, tool misuse, and identity abuse as top-tier risks.
Security in multi-agent systems begins not with trusting agents, but with a structure that verifies all agent actions externally. This article introduces a method for enforcing permissions outside the agent layer using the Policy-as-Code methodology. Focusing on two policy engines, Open Policy Agent (OPA) and Cedar, we examine an architecture that declares "who can use which tools, under what conditions" in code and enforces it automatically.
This article is primarily aimed at backend and full-stack developers who are deploying an LLM agent to production for the first time. It covers how to write Rego policies, Cedar's type validation features, criteria for selecting the two tools, and even common mistakes made in production environments.
Key Concepts
What is Policy-as-Code
Policy-as-Code (PaC) is a methodology that declares access control rules as human-readable code, versions them in Git like infrastructure code, and tests them in CI/CD pipelines. While traditional access control methods were scattered throughout application code as conditional statements like if user.role == "admin", PaC delegates these decisions to a dedicated policy engine.
Why is PaC necessary in a multi-agent system? Because AI agents autonomously select and execute tools, controlling permissions through the agent's internal logic can be easily bypassed due to prompt injection or model misjudgment. Placing the policy engine outside the agent guarantees blocking before execution, even if the agent becomes compromised.
The key components can be summarized as follows:
| Concept | Role |
|---|---|
| Policy-as-Code | Express access rules as declarative code · Version control · Testing |
| OPA (Open Policy Agent) | General-purpose policy engine, Rego language, infrastructure and service level control |
| Cedar | AWS Development, Application-Level Granular Accreditation (FGAC), Built-in Type Verification |
| Agent Sidecar Pattern | Architecture where the policy engine intercepts all tool calls |
| RBAC / ABAC | Role-based / Attribute-based Access Control Models |
OPA and Rego: Universal Policy Engines
Open Policy Agent (OPA) is a CNCF Graduated project that enables the integration of policies across the entire stack—from Kubernetes Admission Controller to Envoy Proxy and API Gateway—into a single engine. It can be easily embedded into applications via HTTP REST APIs or Go libraries, and policies are written using a declarative query language called Rego.
Rego is a logic programming language derived from Datalog. It operates using AND semantics, where conditions listed within a allow { ... } block are allowed only when all conditions are true. While this may feel unfamiliar at first to developers accustomed to procedural languages, it is a very powerful way to declaratively express complex policies.
Cedar: A type-valid policy language
Cedar is a purpose-oriented policy language released as open source by AWS in 2023, specialized for Fine-Grained Authorization (FGAC) at the application level. It is based on a ternary model of Principal - Action - Resource and is the first open-source application-level authorization language to include built-in Automated Reasoning.
Formal Verification: A technique that guarantees the behavior of a policy in advance under all input conditions through mathematical proof. Cedar has this feature built-in, allowing you to verify the fact that "this policy can never allow deletion operations without administrator privileges" before code execution.
Agent Sidecar Pattern
The agent sidecar pattern operates in the following flow.
[사용자 요청]
↓
[에이전트 (LLM + 도구 선택)]
↓ 도구 호출 시도
[정책 엔진 (OPA / Cedar)] ← 정책 저장소 (Git)
↓ 허용 / 거부
[실제 도구 실행 (API, DB, 파일 시스템)]The agent does not execute tools directly; instead, it first queries the policy engine, asking, "Is it permissible to execute this action?" The actual tool is executed only when the policy engine grants permission. Even if the agent is compromised by prompt injection, the policy engine exists outside the agent's code, so an attacker cannot bypass the policy layer even if they manipulate the agent's internal logic.
OPA vs Cedar: When and What to Choose
The two tools have different strengths. Although they can solve the same problem, they operate on different layers, so establishing selection criteria in advance can reduce unnecessary trial and error.
| Criteria | Select OPA | Select Cedar |
|---|---|---|
| Scope | Infrastructure-wide integration including K8s, Envoy, API GW, etc. | Granular authorization within applications |
| Performance requirements | Allows general API latency | Sub-millisecond hotpass processing required |
| Policy Verification Method | Unit Test Based | Formal Verification Required |
| Cloud Environment | Vendor Neutral Multi-cloud | AWS Environment Optimization, Amazon Verified Permissions Integration |
| Ecosystem Maturity | Rich Plugins & Community | Growing Rapidly but Small Compared to OPA |
| Policy Language Familiarity | Need to learn logical Rego | Intuitive syntax similar to SQL |
Practical Guide: OPA is a natural starting point if you are already using Kubernetes or Envoy, or if you wish to integrate your entire infrastructure into a single engine. Cedar is suitable if you want to build new AI agent applications on AWS or mathematically verify the potential for policy errors. Since the two tools are not substitutes but complements operating at different layers, a mixed configuration using OPA for infrastructure policies and Cedar for agent tool authorization is frequently used in practice.
Practical Application
Example 1: Implementing Access Control for Payment Agent Tools with Cedar
AWS Bedrock AgentCore is an access control service for agent tools that uses Cedar. You can create policies to restrict payment agents to processing only transactions below a certain amount.
// 결제 에이전트는 $1000 미만 거래만 처리 가능 (verified 사용자)
permit (
principal == Agent::"payment-agent",
action == Action::"process_payment",
resource == Tool::"payment-api"
)
when {
context.amount < 1000 &&
context.user_role == "verified"
};
// $1000 이상 $10000 미만 구간:
// 별도 permit 규칙이 없으므로 Cedar의 deny-by-default에 의해 자동 거부됩니다.
// 이 구간을 허용하려면 별도 permit 블록(예: 관리자 승인 조건)을 추가하세요.
// 고액 결제($10000 이상)는 명시적으로 거부
forbid (
principal == Agent::"payment-agent",
action == Action::"process_payment",
resource == Tool::"payment-api"
)
when {
context.amount >= 10000
};| Syntax Elements | Semantics |
|---|---|
principal |
Policy Principal — Which agent the rule applies to |
action |
Allowed actions |
resource |
Resource to access |
when { ... } |
Allow Condition — Apply only when all conditions are true |
forbid |
Explicit Denial — Takes precedence over permit |
deny-by-default: Cedar allows only when permit is explicitly declared, and denies by default in all other cases. In the example above, the $1000~$9999 section is automatically denied without a separate permit block, ensuring safety even if an allow rule is accidentally omitted.
Example 2: Controlling Multi-Agent Deployment with OPA + Rego
In systems where multiple agents perform various tasks in different environments, OPA is suitable for controlling the behavior of each agent based on roles. The policy below is an example of checking the role when DeployAgent requests a production VM deployment.
package agent.deploy
import future.keywords.if
# 기본은 거부
default allow := false
# DevOps 역할만 프로덕션 VM 배포 허용
allow if {
input.agent == "DeployAgent"
input.action == "deploy_vm"
input.environment == "production"
input.user.role == "devops"
}
# 스테이징 환경은 developer 역할도 배포 가능
allow if {
input.agent == "DeployAgent"
input.action == "deploy_vm"
input.environment == "staging"
input.user.role in {"developer", "devops"}
}
# 감사 로그용 이유 반환
reason := "production deployment requires devops role" if {
input.environment == "production"
input.user.role != "devops"
not allow
}The OPA REST API returns the following JSON structure for the above policy query.
// 허용된 경우
{ "result": true }
// 거부된 경우 (reason 규칙 포함 시)
{
"result": false,
"reason": "production deployment requires devops role"
}On the agent side, an HTTP request is sent to the OPA to receive a decision before executing the tool. It is important to explicitly implement the fail-secure principle (default rejection in case of OPA server failure).
import httpx
async def check_policy(agent_id: str, action: str, context: dict) -> bool:
"""OPA 정책 엔진에 허용 여부를 질의합니다.
OPA 서버 장애 시 fail-secure 원칙에 따라 False(거부)를 반환합니다.
"""
payload = {
"input": {
"agent": agent_id,
"action": action,
**context
}
}
try:
async with httpx.AsyncClient(timeout=2.0) as client:
response = await client.post(
"http://opa-service:8181/v1/data/agent/deploy/allow",
json=payload
)
response.raise_for_status()
result = response.json()
return result.get("result", False)
except (httpx.HTTPError, httpx.TimeoutException):
# OPA 서버 다운 또는 4xx/5xx 응답 시 기본 거부 (fail-secure)
return False
async def deploy_vm(vm_config: dict, user_context: dict):
allowed = await check_policy(
agent_id="DeployAgent",
action="deploy_vm",
context={
"environment": vm_config["environment"],
"user": user_context
}
)
if not allowed:
raise PermissionError("정책 엔진이 이 작업을 허용하지 않았습니다.")
await execute_deployment(vm_config)| Code Element | Role |
|---|---|
default allow := false |
Default denial without explicit permission |
input.* |
Context data sent by the agent |
input.user.role in {...} |
Check for multiple roles based on set inclusion |
reason Rule |
Returns rejection reason to support debugging |
response.raise_for_status() |
Exception on HTTP error → Default denial handling in except |
Example 3: Centralized control of tool calls with MCP Gateway + OPA Sidecar
In environments using MCP (Model Context Protocol), an OPA sidecar can be placed in front of the MCP server to centrally evaluate all tool calls. The flow is as follows: Agent's tool call request → MCP gateway intercept → Query OPA for permission → Forward to the actual MCP server only if permitted.
package mcp.tools
import future.keywords.if
import future.keywords.in
default allow := false
# 허용된 도구 목록 (에이전트별)
allowed_tools := {
"research-agent": {"web_search", "read_file", "summarize"},
"code-agent": {"read_file", "write_file", "execute_code"},
"admin-agent": {"read_file", "write_file", "delete_file", "manage_users"}
}
# 에이전트가 해당 도구를 사용할 권한이 있는지 확인
allow if {
permitted := allowed_tools[input.agent_id]
input.tool_name in permitted
}
# 민감 도구는 추가 조건 필요
allow if {
input.tool_name == "execute_code"
input.agent_id == "code-agent"
input.sandbox_enabled == true
input.user_confirmed == true
}This policy file can be deployed by loading it with the --bundle option when running the OPA server, or by synchronizing it in real-time with a Git repository via OPAL. When the MCP Gateway receives each tool call request, it sends JSON containing the agent ID and tool name to POST http://opa-service:8181/v1/data/mcp/tools/allow and determines whether to allow it based on the boolean value in the result field.
Using this pattern, you can configure a centralized governance layer that handles audit logs, authentication, and policy enforcement from a single gateway.
Example 4: Third-party Governance Tools — Execution Ring-based Isolation of Microsoft Agent Governance Toolkit
While the previous Examples 1–3 focused on writing OPA/Cedar policy code, this example covers a governance layer one level higher. The Agent Governance Toolkit, released as open source (MIT) by Microsoft in April 2026, supports OPA, Cedar, and YAML rules and provides the concept of a Dynamic Execution Ring that isolates agents based on trust levels. Inspired by CPU privilege levels, this structure is effective in minimizing the radius of compromise between agents.
# agent-governance-policy.yaml
agent_rings:
- ring: 0
name: "trusted-internal"
trust_score_min: 800
allowed_actions: ["*"] # 모든 작업 허용
- ring: 1
name: "verified-partner"
trust_score_min: 600
allowed_actions:
- "read_data"
- "write_data"
- "call_api"
forbidden_actions:
- "delete_data"
- "manage_permissions"
- ring: 2
name: "standard-agent"
trust_score_min: 400
allowed_actions:
- "read_data"
- "call_api"
forbidden_actions:
- "write_data"
- "delete_data"
- "manage_permissions"
- ring: 3
name: "untrusted-external"
trust_score_min: 0
allowed_actions:
- "read_public_data"
require_human_approval: true
sandbox_required: true
trust_score_rules:
- event: "policy_violation"
delta: -100
- event: "successful_task_completion"
delta: +10
- event: "anomalous_behavior_detected"
delta: -200| Ring Level | Target Agent | Trust Score | Key Features |
|---|---|---|---|
| Ring 0 | Internal Trust Agent | 800+ | Allow All Actions |
| Ring 1 | Verified Partner Agent | 600+ | Read/Write Allowed, Delete Disabled |
| Ring 2 | General Agent | 400+ | Limited Read/API Access Only |
| Ring 3 | External untrusted agent | 0+ | Read public data, human approval required |
Pros and Cons Analysis
Advantages
| Item | Content |
|---|---|
| Agent Contamination Isolation | Policy Engine Blocks Before Execution Even If Agent is Contaminated by Prompt Injection |
| Auditability | All policy rulings are logged, enabling full tracking of agent behavior |
| Policy-independent deployment | Update only the policy without redeploying the application |
| Format Validation (Cedar) | Automatically detects policy conflicts and risk conditions in advance |
| Ecosystem Integration | Seamless integration with existing infrastructure such as Kubernetes, Envoy, and API Gateway |
| Standardized Governance | Consistent Policy Implementation Across Multiple Agents and Services |
Disadvantages and Precautions
| Item | Content | Response Plan |
|---|---|---|
| Rego Learning Curve | Datalog-family syntax may feel unfamiliar to procedural language developers | Step-by-step learning recommended with OPA Playground and Unit Test features |
| Policy deployment delay | If policy changes are not propagated in real-time, agents may operate with the old policy | Configure Git-based real-time synchronization with OPAL |
| Agent chain trust propagation | Identity and authority scope may be lost at each step in the A→B→C call chain | Explicitly passing identity context via JWT claims |
| OPA Community Trends | Possibility of Governance Structure Changes Following Joining of Apple's Core Contributors | Periodic Monitoring of Community Trends as the Open Source OPA Itself Will Be Maintained |
| AWS Ecosystem Dependence (Cedar) | Cedar Flexibility Limited in Multi-cloud Environments | Choose OPA If Vendor Neutrality Is Needed |
| Latency of complex policies | Heavy Rego policies cause increased response times | Use Cedar recommended for hotpaths (42–60 times faster than Rego based on Teleport Benchmark) |
OPAL (Open Policy Administration Layer): An open-source tool that synchronizes OPA or Cedar policies in real-time based on a Git repository. Policy changes are immediately pushed to each agent instance, resolving deployment delay issues.
The Most Common Mistakes in Practice
- Design for making authorization decisions internally within the agent: The judgment that "this agent is trusted, so internal logic is sufficient" is vulnerable to prompt injection. If an attacker manipulates the agent's system prompt, they can bypass internal inspection logic. It is recommended to delegate all tool call authorization decisions to the policy engine layer.
- Fail-open design on OPA server failure: If you simply propagate an exception or return
Truewhen unable to connect to the policy engine, all restrictions disappear the moment the OPA goes down. As shown in Example 2, a design that catches network errors and explicitly returns a default denial (fail-secure) is a fundamental principle of secure code. - Missing Identity Context in the Agent Chain: If Agent A does not pass the original user's identity and permission scope when calling Agent B, Agent B may perform operations that exceed the actual allowed scope. A design is required to explicitly propagate the identity context in the form of a JWT token throughout the call chain.
- Insufficient Audit Logs: If policy allow/deny decisions are not logged, post-hoc analysis is impossible. It is recommended to keep structured logs for all decisions that include the agent ID, request context, decision result, and timestamp.
In Conclusion
Security in multi-agent systems begins not with trusting agents, but with a structure that externally verifies all agent actions. OPA excels in infrastructure-wide integration and expressive policy writing, while Cedar is strong where application-level granular authorization and format verification are required. Whichever is chosen, the key is the architectural principle of placing the policy adjudication layer outside the agents.
Step-by-step guide to get started right now:
- Diagnosing the Current Agent System: It is recommended to create a list of tools executed by your agent and first identify the single tool that could cause the greatest damage if allowed without restriction. This tool will become the target for the first policy creation in the subsequent step.
- Configuring OPA Local Environment: You can launch an OPA server locally using the
docker run -p 8181:8181 openpolicyagent/opa run --servercommand and write and test Rego syntax directly in OPA Playground. It is recommended to write a simpleallowpolicy for the tools identified in Step 0. - Cedar Policy Writing Practice: Install the Cedar library from cedar-policy GitHub and organize the list of tools used by the agent into a Principal-Action-Resource model. If you are using an AWS environment, you can also visually manage policies in the Amazon Verified Permissions console.
- Configuring Real-time Policy Synchronization with OPAL: It is recommended to launch the OPA + OPAL stack using
docker-composeand connect a GitHub repository as the policy source. You can experience policies automatically updating without restarting the agent when you commit policy changes to the repository. The Quickstart example in the official OPAL documentation is a suitable starting point.
Next Post: How to Build a Policy-as-Code CI/CD Pipeline by Integrating OPAL with GitHub Actions — Covers the entire process from automated testing of policy changes and staging environment verification to production rollout.
Reference Materials
- OPA vs Cedar vs Zanzibar: 2025 Policy Engine Guide | osohq
- Introducing the Agent Governance Toolkit | Microsoft Open Source Blog
- Agent Governance Toolkit GitHub | microsoft/agent-governance-toolkit
- Agent Governance Toolkit: Architecture Deep Dive | Microsoft Tech Community
- Secure AI Agents with Policy in Amazon Bedrock AgentCore | AWS ML Blog
- Understanding Cedar Policies | Amazon Bedrock AgentCore Docs
- Why Open Policy Agent is the Missing Guardrail for Your AI Agents | CodiLime
- Policy Engine Showdown: OPA vs. OpenFGA vs. Cedar | Permit.io
- MCP Access Control: OPA vs Cedar | Natoma
- Security Benchmarking Authorization Policy Engines | Teleport
- AI Agents, the Model Context Protocol, and Authorization Guardrails | Cerbos
- Open Policy Agent Official Documentation
- Cedar Policy Language | cedar-policy GitHub