Groups are an identity-plane object, not a gateway object. 17 AD security groups in OU=Groups,OU=Lab — tiered T0–T4 — reach Duo via the AD Duo Directory Sync (the Auth-Proxy path) — a distinct sync from Entra Connect's AD → Entra — and serve every gateway. What is per-gateway is the policy binding, authored once in mcp-authz-intent.yaml and rendered to each PDP. The same 17 groups govern all three planes — MCP tools, LLM providers, and private apps; a new gateway or plane renders the same intent to its PDP, it never mints a new group set.
Source of truth → sync → decision (PDP) → enforcement (PEP) → resources. Policy-as-code drives the PDPs; the leaver signal pulls tokens back out.
flowchart TB HRIS["HRIS-lite → IGA → ITSM gate
onboarding drives AD"] subgraph SOT["AD on-prem · lab.local (ADUC) — source of truth"] ADG["Users + 17 SG-* groups
OU=Groups,OU=Lab · tiered T0–T4"] end subgraph PROXY["Auth-proxy host (SYNC01) — two sync engines, side by side"] ECN["Entra Connect
AD users+groups → Entra"] DDS["AD Duo Auth Proxy
Duo Directory Sync · [ADDS]"] end ENT["Entra ID · testenvir010"] DUO["Duo directory
31 AD-synced (incl. 17 SG-*) · 5 Entra · 1 Google"] subgraph PAC["Policy-as-code · SSOT"] INT["mcp-authz-intent.yaml"]-->RND["render_duo.py
(render_secureaccess.py →)"] end subgraph PDP["Decision · Duo cloud Authorization API (true PDP)"] DAC["ext_authz connectors
agentgw + envoy · LIVE"] CSA["Cisco Secure Access
managed SSE · PLANNED"] end subgraph PEP["Enforcement · gateways (PEP) — one job each"] AGW["agentgateway 0.12.0 · LIVE
aigw-agentgw · MCP tools"] ENV["Envoy AI Gateway
LLM providers · repurposing"] SAG["Cisco Secure Access
private apps (ZTNA) · PLANNED"] end subgraph RES["Resources"] MCP["MCP servers
github · ms-learn · splunk"] LLM["LLM providers
Anthropic · OpenAI · Ollama"] APPS["Private apps
-lite consoles + fabric API"] end HRIS-->ADG ADG-->|Entra path|ECN-->ENT ADG-->|AD→Duo path · SG-* groups|DDS-->DUO ENT-.->|Entra→Duo sync|DUO DUO-->DAC ENT-.->CSA RND-->DAC RND-->CSA DAC-->AGW DAC-->ENV CSA-->SAG AGW-->MCP ENV-->LLM SAG-->APPS LEAVER["HRIS leaver → IGA → Duo session-revoked (SSF/CAEP)"]-.->|pulls token|AGW LEAVER-.->ENV LEAVER-.->SAG
HRIS onboarding drives AD on-prem. The auth-proxy host (SYNC01) runs two sync engines side by side: Entra Connect pushes AD users + groups into Entra ID, and the AD Duo Auth Proxy (Duo Directory Sync "Grayskull Technology [ADDS]") pushes them into Duo. The 17 SG-* groups reach Duo on the AD path — not through Entra. Duo's directory today holds 31 groups from the AD sync (incl. all 17 SG-*), 5 from a Microsoft Entra sync, and 1 from Google — three inbound directory syncs converging on one Duo, which the gateways' authorization then reads.
Two checks on every call: authenticate the Duo JWT, then authorize the tool against the sponsor's groups. Default-deny.
sequenceDiagram
participant C as Agent / MCP client
participant G as Gateway (PEP)
participant D as Duo Authorization Connector (PDP)
C->>G: tools/call (Bearer Duo JWT)
G->>G: mcpAuthentication — validate iss/aud/JWKS
G->>D: ext_authz Check (token + target_tool)
D->>D: sponsor groups ∩ allowedTools(group, server)
alt authorized
D-->>G: ALLOW (allowedTools)
G-->>C: tool result
else not in any group's bundle
D-->>G: DENY
G-->>C: 403 (tool hidden / refused)
end
| AD group | Plane | Tier | Provisioning | Scope |
|---|---|---|---|---|
| SG-MCP-MSLearn | MCP | T0 | birthright | ms-learn ×3 |
| SG-MCP-GitHub-Read | MCP | T1 | birthright | github read ×26 |
| SG-MCP-GitHub-Write | MCP | T2 | requestable | github write ×14 |
| SG-MCP-GitHub-Merge | MCP | T3 | JIT · .adm | merge_pull_request · SoD ≠ Write |
| SG-MCP-GitHub-Admin | MCP | T3 | JIT · .adm | create_repository, delete_file |
| SG-MCP-AppSec | MCP | T4 | JIT+alert · .adm | run_secret_scanning |
| SG-MCP-Splunk-Read | MCP | T1 | birthright | splunk metadata ×6 |
| SG-MCP-Splunk-Query | MCP | T3 | JIT · .adm | run_query, run_saved_search |
| SG-MCP-Splunk-IAMData | MCP | T4 | JIT+alert · .adm | get_user_info, get_user_list |
| SG-LLM-Local | LLM | T0 | birthright | Ollama (no egress) |
| SG-LLM-External | LLM | T2 | requestable | Anthropic/OpenAI · DLP-gated |
| SG-LLM-Frontier | LLM | T3 | JIT | frontier models · budget-bound |
| SG-AGW-Fabric-Read | API | T1 | requestable | GET · hris/iga/itsm/cmdb/pacs |
| SG-AGW-Fabric-Operator | API | T3 | JIT · .adm | control endpoints |
| SG-MCP-AgentGW-Admin | admin | T3 | JIT · .adm | agentgateway console/config |
| SG-MCP-EnvoyGW-Admin | admin | T3 | JIT · .adm | Envoy admin (planned) |
| SG-MCP-SecureAccess-Admin | admin | T3 | JIT · .adm | Secure Access admin (planned) |
Groups = f(roles × tiers) — independent of gateway count and tool count. A new tool is auto-tiered by the onboarding rubric into an existing group; a new gateway renders the same intent to its PDP. T3/T4 bind to an elevated .adm identity, so neither a human on their daily account nor an agent acting on-behalf-of them can reach destructive or data-exfil tools. Compliance maps clean: NIST 800-53 AC-2/3/5/6, 800-171/CMMC 3.1.x, ISO 27001 A.5/A.8, SOC 2 CC6, HIPAA §164.312.