Standards-Based Integration Architecture
flowchart LR HRIS[HRIS
source of record] --> IGA[IGA-Lite
governance / JML] REQ[Self-service
access request]:::req --> IGA CMDB[CMDB-Lite
CI / device trust] -.enriches.-> IGA IGA -->|F3 · RFC| ITSM{ITSM-Lite
change gate} ITSM -->|Duo push · approver-group RBAC
serialized · SoD| CAB([CAB approver]):::cab CAB -->|approve / deny · F4| ITSM ITSM -->|SCIM / Admin API| DUO[Duo] ITSM -->|SCIM / LDAPS| AD[AD / Entra] PAM[Privileged access broker
JIT elevation + vault]:::pam -->|JIT time-boxed| AD subgraph SIG[Signaling plane — SSF] TX[SSF
transmitter] end IGA -->|lifecycle → RISC| TX IGA -->|grant applied → assurance-level-change| TX CMDB -->|posture → CAEP| TX TX -->|CAEP / RISC| DUO TX -->|CAEP / RISC| CII[CII / Oort] TX -->|CAEP / RISC| APPS[Apps / RPs] PAM -->|JIT expiry / check-in → session-revoked| TX TX -->|CAEP / RISC| XDR[Cisco XDR
detect + respond]:::xdr CII -.identity vector.-> XDR XDR -.session-kill / quarantine.-> DUO classDef req fill:#160f22,stroke:#3b3170,color:#e6edf3; classDef cab fill:#201a10,stroke:#a98f2a,color:#f0f6fc; classDef xdr fill:#25161a,stroke:#7a3038,color:#e6edf3; classDef pam fill:#1a1224,stroke:#6b4a9e,color:#e6edf3;
One identity truth governs every system, and everything integrates through open standards — not bespoke connectors. HRIS is the source of who-is-who; every downstream system (Duo, AD/Entra, and apps) is granted through the same provisioning standard and re-evaluated through the same signaling standard. Add a system by speaking the standard, not by writing a one-off integration.
Two planes, two standards
Identity integration splits cleanly into a slow, stateful provisioning plane and a fast, event-driven signaling plane. Governance lives on the first; real-time reaction lives on the second. Every hook into the fabric uses one or both.
SCIM 2.0
The governed, slow path — establishes who has what. HRIS → IGA reconcile → ITSM change gate → grant/revoke on Duo and AD/Entra. Inbound SCIM to IGA-Lite is already ITSM-gated; outbound writes are the Admin-API / directory legs.
SSF — OpenID Shared Signals
The real-time path — broadcasts what just changed. One transmitter, many receivers; each re-evaluates in real time. CAEP carries session/security events, RISC carries account lifecycle. This is the "everyone integrates the same way" layer.
The Change Approval Board — how the gate approves
The ITSM gate doesn't auto-approve grants — it requires a human CAB decision, authenticated by a Duo push. The push goes only to members of the approver group — the RBAC source of truth, resolved from AD and stamped into every RFC — so approver identity is governed, not hardcoded.
| Mechanism | What it does | Why |
|---|---|---|
| Duo-push approval | a change in awaiting_approval sends an async Duo push; the approver's allow/deny on-device is the decision (F4) | phishing-resistant, on-device CAB approval |
| group RBAC | iga resolves the approver group members → Duo usernames → RFC approvers[]; itsm pushes only to them | who-may-approve is an AD group, governed like any entitlement |
| Serialized pushes | at most one push outstanding per approver; the next sends only after the current resolves | a multi-grant persona never fires simultaneous pushes (Duo anti-fraud) |
| SoD routing | a privileged group grant routes as normal + SOD-PRIV, not the auto-approvable standard | segregation of duties on privileged access |
| Grant → SSF | on an applied grant, iga fans a CAEP assurance-level-change to CII + downstream receivers | a posture increase re-evaluates downstream in real time |
Requests arrive two ways — birthright (persona-derived) and self-service (the /request intake) — and both flow through the same gate: RFC → Duo-push CAB → approve → apply → assurance-level-change. Every decision lands on the tamper-evident audit spine and the Grafana Change Approval Board row.
Where SSF fits — and how
You don't have to build the hard part. SSF/CAEP-capable signaling is a native product capability — Duo and CII speak these shared signals directly (SSF 1.0 / CAEP 1.0). Session events (CAEP) and account-lifecycle events (RISC) flow from one transmitter to many receivers, and the SCIM→RISC leg maps lifecycle changes to RISC events. So SSF isn't something to add — it's the spine that turns the fabric from a set of point integrations into a broadcast bus.
The role each standard plays
| Layer | Standard | Carries | Fabric hook | Status |
|---|---|---|---|---|
| Provisioning | SCIM 2.0 | account create·update·deactivate·delete | inbound → IGA (ITSM-gated); outbound → Duo/AD | live (inbound gated) |
| Session signals | SSF · CAEP | session-revoked · credential-change · assurance-level-change · device-compliance-change | SSF transmitter → receivers (Duo, CII, apps) | native (Duo · CII) |
| Lifecycle signals | SSF · RISC | account-disabled · account-purged · account-enabled · identifier-changed/recycled | IGA lifecycle → RISC leg → transmitter | native |
Lane: the SSF/CAEP stack is standards plumbing only — this fabric carries signals over open standards.
Duo on the lab's AD — and how CII sees it
Open standards cover the new systems; the lab's legacy AD is covered by Duo's on-prem protection surfaces, and Cisco Identity Intelligence ingests AD directly.
| Surface | Protects / ingests | How |
|---|---|---|
| Duo · Windows Logon | Interactive console + RDP logon | Duo Authentication for Windows Logon (credential provider) |
| Duo · Legacy AD Protection | Legacy on-prem apps that auth via AD (Kerberos / NTLM) | Sub-auth filter DLL on the DCs → Duo MFA (Push / phone) · fail-open configurable |
| Duo · Command-Line Protection | Remote non-interactive: PS-Remoting · SSH · SFTP | MFA on command-line logons (user must pre-exist in Duo) |
| CII · AD integration | AD users / groups / attributes · stale + guest · password hygiene | Lightweight PowerShell → SCIM to Cisco Identity Intelligence (no DC agent) |
Windows Logon + Legacy AD + Command-Line Protection close the MFA gap across every AD logon path — interactive, legacy-app, and remote shell — while the CII AD ingest folds on-prem AD into the same identity-intelligence plane (Oort · Astrix · BHE) as cloud and SaaS.
Credential vaulting — a secrets vault, CyberArk-style
Governance decides who may hold a credential; a vault holds the secret itself and brokers every checkout. The lab runs a secrets vault as its PAM/vault tier — the same split CyberArk draws between PVWA (people) and CCP / AAM (machines):
Vault app + op CLI
People retrieve elevated and break-glass credentials from a scoped secrets vault — the CyberArk PVWA equivalent. Checkout is Duo-gated, per-vault access follows the tier model, and use is audited. The break-glass secret lives here dormant, not in a config file.
Vault Connect API + Service Accounts
Services and CI fetch secrets programmatically from a self-hosted vault Connect API (the API the consoles already health-check) and Service Accounts — no secret in the image, none in git. This is the CyberArk CCP / AAM / Conjur equivalent: the machine authenticates, the vault releases only the secret it's entitled to.
| CyberArk workflow | secrets-vault equivalent | Fabric use |
|---|---|---|
| PVWA — human credential checkout | vault app + op CLI | elevated / break-glass retrieval · Duo-gated · audited |
| CPM — rotation / managed accounts | vault item rotation | service-account secrets rotated in the vault, not hand-edited |
| CCP / AAM / Conjur — app-to-app | vault Connect API + Service Accounts | consoles & CI fetch op:// refs at runtime — no secret in image/git |
This closes the loop with the ownership model: a service account's identity is governed in the NHI registry while its secret is vaulted in a secrets vault — human-checkout for break-glass, machine-fetch via Connect for the account itself. personal lab; secrets never land in the repo.
Egress & ingress — the network boundary
The fabric runs behind an enterprise network boundary that mirrors the identity planes: nothing is implicitly trusted, every crossing is mediated and logged. Outbound is forced through a tiered Secure Web Gateway (servers locked to an allowlist; workstations browse behind a threat-intel denylist); inbound arrives only through the Cloudflare edge — there are no open inbound ports on the lab. The Cisco customer-telemetry lane and the personal lane stay direct by design — a hard lane boundary, never proxied.
Egress — outbound through the tiered Squid SWG
A forward proxy at the lab gateway filters on the CONNECT / SNI host — no TLS interception, so cert-pinned APIs work untouched — and is tiered by listener port to match the healthcare-SWG standard (HIPAA / HITRUST / NIST 800-207) without breaking normal browsing:
default-deny + allowlist
The container fabric (HTTPS_PROXY=squid:3128), host scripts, and infra VMs reach only enumerated destinations. Servers never browse the open web — arbitrary server egress is the #1 exfiltration / C2 signal, so it's the tightest lane.
default-allow − threat-intel denylist
GPO-PAC workstations (fail-closed) browse the open web on 80/443 — but anonymizers, ad-hoc tunnels, and anon file-drop (exfil) are blocked, CONNECT is TLS-only, and every request is logged. Standard browsing works; known-bad doesn't.
flowchart LR WS[Workstations · GPO PAC
fail-closed]:::src --> BROWSE FAB[Service containers
HTTPS_PROXY → SWG]:::src --> SVC subgraph SWG[Forward SWG · tiered by port · no TLS intercept] SVC[Service lane
default-deny + allowlist] BROWSE[User browse lane
default-allow − denylist] end SVC -->|allowlisted only| NET[Allowlisted internet
LLM · Duo · Graph · Intune
CII · registries · PKI · services]:::ok SVC -->|off-allowlist| DENY[TCP_DENIED]:::deny BROWSE -->|standard 80/443| WEB[Open web]:::ok BROWSE -->|known-bad · tunnels · exfil| BLOCK[denylist → TCP_DENIED]:::deny SWG -.per-request JSON log.-> SPL[SIEM
egress detections]:::src CISCO[Cisco lane]:::direct -.DIRECT · lane boundary.-> NET classDef src fill:#1b2838,stroke:#3d6ea5,color:#e6edf3; classDef ok fill:#101b16,stroke:#2c623a,color:#e6edf3; classDef deny fill:#25161a,stroke:#7a3038,color:#e6edf3; classDef direct fill:#201a10,stroke:#a98f2a,color:#f0f6fc;
Ingress — inbound through the Cloudflare edge
No inbound ports. Every request hits the Cloudflare edge — real certs, WAF, and CF Access → Duo SSO in front of every console — then rides a cloudflared tunnel down to a loopback origin on the internal host. The portal itself is static on CF Pages; a reverse proxy only terminates TLS for internal-name services.
flowchart LR CLIENT[Internet clients
browser · agents · SIEM]:::src --> EDGE subgraph EDGE[Cloudflare edge] ACC[CF Access → Duo SSO
consoles · Grafana] PAGES[CF Pages · static
identity portal] TUN[Tunnels · cloudflared
infra · scim · ssf] end EDGE --> ORIGIN subgraph ORIGIN[Internal origin host] ORIG[tunnel origins → loopback
consoles · SSF receiver] NPM[Reverse proxy 80 / 443
TLS for internal names] end ORIGIN --> SVC[Internal + fronted services
Splunk · consoles · Grafana
Uptime-Kuma · Portainer · gateways · SSF]:::ok SPLK[Splunk web
own Duo SAML · app-layer]:::direct -.no CF Access double-gate.-> SVC classDef src fill:#1b2838,stroke:#3d6ea5,color:#e6edf3; classDef ok fill:#101b16,stroke:#2c623a,color:#e6edf3; classDef direct fill:#201a10,stroke:#a98f2a,color:#f0f6fc;
Lane: Personal / Lab. Egress is the tiered SWG (service allowlist + browse denylist, no TLS-bump yet — payload DLP is the next step); ingress is zero-inbound-port via the Cloudflare edge. A forward proxy governs only outbound — inbound "no unapproved traffic" is the host firewall (default-block) + the CF Zero-Trust edge. The Cisco customer-telemetry lane and the personal lane are deliberately excluded from both — a hard lane boundary, direct and out of scope.