Implementing Per-Tool RBAC and Real-Time PII Blocking with Cloudflare DLP + OPA
This article is intended for backend and platform engineers familiar with the basic concepts of JWT authentication and HTTP APIs.
The era of AI agents accessing external tools is rapidly dawning. While Model Context Protocol (MCP) has enabled LLMs to call file systems, databases, and external APIs just like functions, serious security gaps exist behind this. According to a Knostic investigation, over 1,800 unauthenticated MCP servers were discovered exposed to the public internet (arXiv 2511.20920), and AI security incidents surged by 56.4% year-over-year, with nearly half involving the leakage of customer PII. It is only in March 2025 that OAuth 2.0 authentication will be added to the MCP specification.
This article answers two key questions. First, how do you declare in code which user roles can use which MCP tools? Second, what structure is needed to detect and block sensitive data hidden within AI traffic in real time? By combining Open Policy Agents (OPA) with Cloudflare AI Gateway DLP, you can directly implement the entire flow of writing, testing, and deploying granular RBAC policies per MCP tool using Rego code, as well as blocking PII in prompts and responses in real time.
Key Concepts
The Current State of MCP Security
MCP is an open standard designed by Anthropic, serving as a standard interface through which AI agents interact with external systems via tools named file_read, db_write, and secret_fetch. OAuth 2.0 authentication was added to the specification only in March 2025, and in June of the same year, MCP servers were officially classified as OAuth 2.0 resource servers, standardizing tool-level scope control. While the specification is maturing rapidly, compatibility and security verification between implementations are still ongoing.
There are two core challenges in MCP security: server access control (who can use which tools) and data leakage prevention (preventing sensitive information from entering or exiting the LLM). OPA and Cloudflare DLP are responsible for these two challenges, respectively.
OPA and Policy-as-Code — Including Rego Basics
OPA (Open Policy Agent) is a CNCF graduation project and a general-purpose policy engine that completely separates policy logic from application code. It writes policies using a declarative language called Rego and supports three deployment modes: HTTP API, sidecar, and inline library.
What is Policy-as-Code? It is a method of managing access control rules in separate declarative files (Rego, YAML, etc.) rather than in the application code, allowing policies to be modified without code deployment and versions to be managed via Git.
For those new to Rego, understanding the two key evaluation rules first makes the code much easier to read later.
Rego Evaluation Rules (Two Things You Must Know)
- Default Negation (Closed World Assumption): If it starts with
default allow = false, all requests not explicitly allowed are denied. - Multiple
allowblocks are OR relationship: When bothallow { 조건A }block andallow { 조건B }block exist, if either one is true, the entireallowbecomestrue.
When requesting policy evaluation from OPA on a Python server, use the REST API's /v1/data/<패키지_경로> endpoint. For example, a policy declared as package mcp.authz is queried via http://opa:8181/v1/data/mcp/authz, and the response's result.allow field contains true/false.
Cloudflare AI Gateway DLP
Cloudflare AI Gateway is an intelligent proxy situated between AI model providers (OpenAI, Anthropic, Gemini, etc.) and clients. Its built-in DLP engine scans both prompts entering the LLM and responses outputting it in real time, and a major update in August 2025 added detection capabilities based on AI context analysis to address false positive issues with the existing regular expression method.
| Category | Detected Item Example |
|---|---|
| Finance·PII | Credit Card Number, SSN, Resident Registration Number |
| Government Identifier | Passport Number, Driver's License Number |
| Medical Information | Patient ID, Diagnosis Code (HIPAA Eligible) |
| Developer Assets | API Key, Source Code, Secret |
| AI Prompt Protection | ChatGPT·Claude·Gemini Prompt Injection Patterns |
Integrated Architecture 3-Layer
The diagram below illustrates the layers based on traffic flow. Although the OAuth identity layer is shown at the very bottom, it is actually a preceding step that issues a token before the connection is initiated. The processing order upon request arrival is Cloudflare (transport) → OPA (policy).
┌──────────────────────────────────────────────────────┐
│ AI 에이전트 / MCP 클라이언트 │
└────────────────────┬─────────────────────────────────┘
│ HTTP/SSE (Bearer JWT 포함)
┌────────────────────▼─────────────────────────────────┐
│ 레이어 1: Cloudflare AI Gateway (전송 레이어) │
│ ├─ DLP Firewall: 프롬프트·응답 민감 데이터 탐지 │
│ ├─ Guardrails: 유해 콘텐츠 분류 │
│ └─ 레이트 리미팅, 감사 로그 │
└────────────────────┬─────────────────────────────────┘
│
┌────────────────────▼─────────────────────────────────┐
│ 레이어 2: OPA (정책 레이어) │
│ ├─ JWT 클레임 파싱 │
│ ├─ 도구 이름·사용자 역할·리소스 범위 평가 │
│ └─ allow / deny 결정 반환 │
└────────────────────┬─────────────────────────────────┘
│
┌────────────────────▼─────────────────────────────────┐
│ [선행] OAuth 2.0 신원 레이어 (연결 전 토큰 발급) │
│ ├─ Keycloak / Entra ID / Auth0 │
│ └─ 서명된 JWT 발급, 도구별 스코프 포함 │
└──────────────────────────────────────────────────────┘Practical Application
Example 1: Implementing per-tool RBAC for MCP tools with OPA Rego
It is based on the MCP Gateway pattern released by Red Hat in December 2025. In Keycloak, each MCP tool (file_read, db_write, secret_fetch) is registered as an OAuth 2.0 client role, and the list of allowed tools is contained in the resource_access claim of the JWT.
Default per-tool RBAC policy:
# policies/mcp/authz.rego
package mcp.authz
default allow = false
# JWT resource_access 클레임에 요청 도구가 포함된 경우 허용
allow {
tool := input.tool_name
server_id := input.server_id
roles := input.token.resource_access[server_id].roles
roles[_] == tool
}ABAC Extension Policy Including Sensitivity Levels and Time-Based Approach:
# policies/mcp/authz_extended.rego
package mcp.authz
import future.keywords.in
default allow = false
# 관리자는 모든 도구 허용
allow {
"admin" in input.token.realm_access.roles
}
# analyst 역할은 민감도 레벨 2 이하 도구만 허용
allow {
"analyst" in input.token.realm_access.roles
tool_sensitivity[input.tool_name] <= 2
}
# power_user는 민감도 3 이하 도구를 업무 시간(09~18시)에만 허용
# 참고: 테스트 가능성을 위해 현재 시각을 input.current_hour로 주입합니다.
allow {
"power_user" in input.token.realm_access.roles
tool_sensitivity[input.tool_name] <= 3
input.current_hour >= 9
input.current_hour < 18
}
# 도구별 민감도 레벨 매핑
tool_sensitivity := {
"file_read": 1,
"db_read": 2,
"db_write": 3,
"secret_fetch": 4,
"admin_exec": 5,
}OPA Policy Unit Tests (Including Time-Based Conditions):
# policies/mcp/authz_test.rego
package mcp.authz_test
import data.mcp.authz
# analyst가 file_read(민감도 1) 호출 → 허용
test_analyst_can_read_files {
authz.allow with input as {
"tool_name": "file_read",
"server_id": "mcp-server-files",
"current_hour": 10,
"token": {
"realm_access": {"roles": ["analyst"]},
"resource_access": {}
}
}
}
# analyst가 secret_fetch(민감도 4) 호출 → 거부
test_analyst_cannot_fetch_secrets {
not authz.allow with input as {
"tool_name": "secret_fetch",
"server_id": "mcp-server-secrets",
"current_hour": 10,
"token": {
"realm_access": {"roles": ["analyst"]},
"resource_access": {}
}
}
}
# power_user가 업무 시간(10시)에 db_write(민감도 3) 호출 → 허용
test_power_user_allowed_in_business_hours {
authz.allow with input as {
"tool_name": "db_write",
"server_id": "mcp-server-db",
"current_hour": 10,
"token": {
"realm_access": {"roles": ["power_user"]},
"resource_access": {}
}
}
}
# power_user가 업무 시간 외(22시)에 db_write 호출 → 거부
test_power_user_blocked_outside_hours {
not authz.allow with input as {
"tool_name": "db_write",
"server_id": "mcp-server-db",
"current_hour": 22,
"token": {
"realm_access": {"roles": ["power_user"]},
"resource_access": {}
}
}
}# 정책 테스트 실행
opa test policies/ -vExample 2: Integrating OPA Interceptors into a Python MCP Server
The code below is a pattern for inserting OPA policy evaluations before the tool dispatch layer of the actual MCP SDK. By configuring it with explicit middleware functions instead of a decorator approach, conflicts with MCP server handler signatures were avoided.
# mcp_server/auth_interceptor.py
import httpx
from typing import Any
OPA_URL = "http://opa:8181/v1/data/mcp/authz"
async def check_opa_policy(
tool_name: str,
token_claims: dict,
server_id: str,
current_hour: int,
) -> bool:
"""
OPA REST API(/v1/data/mcp/authz)에 정책 평가를 요청합니다.
- 로컬 사이드카: 네트워크 홉 없이 sub-ms 수준
- 원격 HTTP API: 약 5~15ms (timeout=0.5는 원격 호출 기준의 여유값)
"""
payload = {
"input": {
"tool_name": tool_name,
"server_id": server_id,
"current_hour": current_hour,
"token": token_claims,
}
}
async with httpx.AsyncClient() as client:
response = await client.post(OPA_URL, json=payload, timeout=0.5)
result = response.json()
# OPA 응답 구조: {"result": {"allow": true/false}}
return result.get("result", {}).get("allow", False)
async def authorized_tool_call(
tool_name: str,
arguments: dict,
token_claims: dict,
server_id: str,
current_hour: int,
) -> Any:
"""
OPA 정책 평가 후 허용된 경우에만 도구를 실행합니다.
MCP SDK 핸들러에서 직접 호출하는 방식으로 시그니처 충돌을 방지합니다.
"""
allowed = await check_opa_policy(tool_name, token_claims, server_id, current_hour)
if not allowed:
raise PermissionError(
f"도구 '{tool_name}' 호출이 정책에 의해 거부되었습니다."
)
return await dispatch_tool(tool_name, arguments)
async def dispatch_tool(tool_name: str, arguments: dict) -> Any:
"""실제 도구 구현을 디스패치합니다."""
if tool_name == "file_read":
return read_file(arguments["path"])
elif tool_name == "db_write":
return write_to_db(arguments["query"], arguments["data"])
else:
raise ValueError(f"알 수 없는 도구: {tool_name}")# mcp_server/server.py — FastMCP 또는 공식 Python SDK와 연동 예시
from datetime import datetime, timezone
from mcp.server import Server
from mcp.types import Tool, TextContent
from auth_interceptor import authorized_tool_call
server = Server("mcp-server-files")
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(name="file_read", description="파일을 읽습니다", inputSchema={...}),
Tool(name="db_write", description="DB에 씁니다", inputSchema={...}),
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
# JWT 파싱은 별도 미들웨어(Bearer 토큰 검증)에서 처리 후 context로 전달
token_claims = server.get_context().token_claims
current_hour = datetime.now(timezone.utc).hour
result = await authorized_tool_call(
tool_name=name,
arguments=arguments,
token_claims=token_claims,
server_id="mcp-server-files",
current_hour=current_hour,
)
return [TextContent(type="text", text=str(result))]Example 3: Cloudflare AI Gateway DLP Configuration (Terraform IaC)
Managing DLP profiles and Gateway policies with Terraform enables policy auditing through code reviews and Git history.
Note: The example below is based on the Cloudflare Terraform provider v4.x. Since the rule_settings block structure in cloudflare_gateway_policy may vary depending on the provider version, it is recommended to check the schema of the current version in the official Terraform Registry documentation before applying it.
# cloudflare_dlp.tf
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
# MCP 트래픽용 커스텀 DLP 프로파일
resource "cloudflare_dlp_profile" "mcp_sensitive_data" {
account_id = var.cloudflare_account_id
name = "MCP Sensitive Data Profile"
type = "custom"
description = "MCP AI 트래픽에서 PII 및 자격증명 탐지"
# 신용카드 번호 탐지 (사전 정의 패턴)
entry {
name = "Credit Card Numbers"
enabled = true
type = "predefined"
}
# API 키 커스텀 패턴 (sk-로 시작하는 OpenAI 키)
entry {
name = "API Keys and Secrets"
enabled = true
type = "custom"
pattern {
regex = "sk-[a-zA-Z0-9]{48}"
# context 필드로 주변 키워드를 조건에 추가하면 오탐을 줄일 수 있습니다
context = "(?i)(api[_-]?key|secret|token)"
operator = "matchCount"
count = 1
}
}
# 소스 코드 탐지
entry {
name = "Source Code"
enabled = true
type = "predefined"
}
}
# AI Gateway에 DLP 정책 연결
resource "cloudflare_gateway_policy" "mcp_dlp_policy" {
account_id = var.cloudflare_account_id
name = "Block MCP Sensitive Data"
description = "AI 프롬프트/응답의 민감 데이터 차단"
enabled = true
precedence = 10
filters = ["http"]
action = "block"
rule_settings {
block_page_enabled = true
block_page_reason = "민감한 데이터가 감지되어 요청이 차단되었습니다."
dlp_profile {
id = cloudflare_dlp_profile.mcp_sensitive_data.id
}
}
# AI Gateway 엔드포인트만 대상으로 지정
traffic = "http.request.uri matches \".*gateway.ai.cloudflare.com.*\""
}Prompt and Response Bidirectional Scan Flow:
[사용자 → SSN 포함 프롬프트 전송]
│
▼
Cloudflare AI Gateway
┌──────────────────────────────────┐
│ DLP Firewall │
│ → SSN 패턴 감지 (###-##-####) │
│ → Action: BLOCK │
│ → Logpush: S3/Splunk로 이벤트 │
└──────────┬───────────────────────┘
│ (차단 — LLM에 도달하지 않음)
▼
[사용자에게 차단 응답 반환]
[정상 프롬프트]
AI Gateway → LLM → 응답 반환
│
[응답에 소스 코드 포함 시]
DLP Firewall → BLOCK
[클라이언트 전달 차단]Pros and Cons Analysis
Advantages
| Item | Content |
|---|---|
| Policy-as-Code | OPA Rego policies can be version-managed with Git and tested/deployed with opa test, allowing permission modifications without code changes. |
| Groundbreaking Tool-based RBAC | You can apply role, attribute, time, and environment-based access control at the individual tool level rather than at the MCP server level |
| Real-time DLP | Prevents data leaks proactively through prompt-and-response scanning, making it advantageous for meeting compliance requirements such as GDPR and HIPAA |
| Centralized Governance | Since all AI traffic passes through a single gateway, consistent policy enforcement and audit log acquisition are possible |
| Framework Independence | OPA is not dependent on any specific language or framework, so it integrates seamlessly across various stacks such as Python, TypeScript, and Go. |
Disadvantages and Precautions
| Item | Content | Response Plan |
|---|---|---|
| Integration Complexity | Direct integration between Cloudflare DLP and OPA is not provided, so custom middleware implementation is required | We recommend reviewing the Envoy ext_authz filter + OPA sidecar pattern, or the agentgateway open source. |
| Rego Learning Curve | Rego is a declarative language, so the initial learning curve is high for developers with an imperative language background. | We recommend considering the YAML-based Cerbos as an alternative or starting with OPA Playground. |
| DLP False Positives | Regular expression-based detection does not consider context, so it can block legitimate data | We recommend enabling AI context analysis and adding surrounding keywords to the context field in the conditions |
| Latency Added | OPA evaluation (local sidecar sub-ms, remote HTTP 5–15ms) and DLP scans are added to all requests | It is recommended to enable OPA bundle caching and apply DLP detection scope only to endpoints handling sensitive data |
| Policy Sprawl | Rego policy files become more complex as the number of tools increases | It is recommended to modularize into a package hierarchy (mcp.authz, mcp.tools.*) and integrate opa test into CI |
| Cloudflare Vendor Lock-in | DLP and Gateway functions are dependent on the Cloudflare platform | In a multi-cloud environment, it is recommended to consider using a platform-independent open-source DLP layer such as LLM Guard in parallel |
What is ext_authz? It is an External Authorization filter for Envoy Proxy, a mechanism that delegates inbound requests to an external policy server (such as an OPA) to determine whether to allow or deny them. It is a key integration point of the Red Hat MCP Gateway architecture.
The Most Common Mistakes in Practice
- If OPA is embedded inline on each MCP server — policies are distributed per server, resulting in a loss of consistency. It is recommended to unify policy sources using a centralized OPA cluster or a sidecar pattern.
- Applying DLP detection scope to all HTTP traffic — increases false positives and latency. We recommend focusing application only on AI Gateway endpoints and specific MCP tool calls that handle sensitive data.
- If you hardcode the JWT claim structure into the Rego policy — you must modify all policy files when the IdP changes. If you separate the JWT claim parsing logic into a separate
token.regohelper package, you only need to modify one place when replacing the IdP.
In Conclusion
So far, we have explored how to declare and test RBAC policies per MCP tool using OPA Rego, how to connect OPA interceptors to a Python MCP server, and how to manage Cloudflare DLP profiles in code using Terraform. By combining these three layers, you can immediately build your own security infrastructure that declares granular RBAC policies per MCP tool in code and blocks PII from AI traffic in real time.
Here are 3 steps you can start right now.
- Configuring OPA Local Environment and Creating the First Policy (~10 min) — Run OPA with
brew install opaordocker run openpolicyagent/opa, then pasteauthz.regofrom Example 1 into OPA Playground and change theinputvalue to directly check the allow/deny behavior. - Create a DLP profile in the Cloudflare AI Gateway free tier (~15 minutes) — You can activate the "Credit Card Numbers" predefined profile in the Cloudflare Zero Trust dashboard →
Gateway→DLP Profilesand connect it to the AI Gateway endpoint to check if it is blocked when a card number is included in the test prompt. - Add OPA Interceptor to Existing MCP Server (~30 min) — Refer to the
authorized_tool_callfunction in Example 2 and try applying it first to the handlers of the most sensitive tools (secret_fetch,db_write). Including policy unit tests withopa test policies/ -vin the CI pipeline ensures a safety net for policy changes.
Next Post: How to Deploy OPA Bundle Servers to Kubernetes and Automatically Deploy Rego Policies with GitOps Workflows — Guide to Building a CI/CD Pipeline for MCP Tool Policies
Reference Materials
- Cloudflare AI Gateway DLP Official Documentation
- Cloudflare AI Gateway DLP Setup Guide
- Cloudflare AI Gateway Guardrails Official Documentation
- Cloudflare AI Gateway August 2025 Major Update Blog
- Cloudflare AI Prompt Protection Announcement Blog
- Cloudflare DLP AI Context Analysis Accuracy Improvement
- Cloudflare DLP Profile Official Documentation
- OPA Official Website
- OPA Official Document
- CNCF OPA Security Deployment Best Practices
- Red Hat: MCP Gateway Advanced Authentication & Authorization (OPA Rego Integration)
- Red Hat: MCP Security Authentication and Authorization Implementation Guide
- MCP Official Security Best Practices
- MCP Official Authorization Specification (2025-06-18)
- Cerbos: MCP Server Fine-Grained Authorization
- Cerbos: AI Agent Dynamic Authorization Guide
- Microsoft Agent Governance Toolkit GitHub
- MCP Security Risk, Control, and Governance Paper (arXiv)
- agentgateway: MCP Authorization specification compliant implementation
- LLM Prompt Injection Attack Monitoring (Datadog)