How the Automation Orchestrator's authorization model helps enterprises manage multi-tenancy across air-gapped environments
Organizations operating multiple air-gapped environments face a compounding governance challenge. Each isolated instance needs consistent access control, but there's no shared identity plane to enforce it. Teams manually replicate RBAC configurations across environments, leading to drift, over-provisioned access, and audit gaps.
Common friction points:
- Configuration drift — Roles and permissions defined independently in each environment diverge over time. "Editor in staging" quietly becomes something different than "editor in production."
- No portable policy — Traditional RBAC is tightly coupled to the identity store. When environments can't share an identity provider, you maintain parallel permission structures with no guarantee of consistency.
- Rigid sharing boundaries — Hierarchical access models (org → project → resource) make it hard to share assets like credentials or workflow templates across organizational boundaries without duplicating them.
- Limited auditability — When access decisions are opaque, proving compliance across isolated environments requires manual cross-referencing of role assignments and resource ownership.
The Automation Orchestrator uses a policy-based authorization engine powered by Open Policy Agent (OPA) with deny-by-default evaluation. Rather than a traditional RBAC layer, the system expresses all access decisions — including built-in roles — as declarative policies evaluated by OPA's Rego engine.
Here's how the key design choices map to multi-tenancy in air-gapped scenarios:
Instead of storing permissions as rows in a database tied to specific user IDs and resource IDs, the system expresses authorization rules as policy statements that reference attributes — labels, metadata, group memberships — rather than instance-specific identifiers.
A policy statement looks like this:
{
"name": "workflow:read:dev-only",
"statements": [
{
"effect": "allow",
"actions": ["workflow:read"],
"scope": "any",
"conditions": {
"resource_labels": {"env": "dev"},
"user_labels": {"team": "platform"}
}
}
]
}Why this matters for air-gapped environments: This policy means "users labeled team=platform can read workflows labeled env=dev" — and it's meaningful in any environment. It doesn't depend on UUIDs, database sequences, or instance-specific state. Policy bundles can be version-controlled, reviewed, tested, and promoted across environments the same way you'd promote Ansible content today.
The Orchestrator uses projects as resource isolation boundaries. Resources (workflows, credentials, executions) belong to projects, and users can have different roles in different projects:
| Scope | Example | Isolation |
|---|---|---|
| System-level roles | admin, auditor, user |
Apply globally across all projects |
| Project-level roles | project-admin, project-user, project-auditor |
Apply only within a specific project |
Role assignments can target individual users or groups, and can be scoped globally or to a specific project. This means a user can be a project-admin in prod-network-automation but only a project-auditor in prod-security-scanning — within the same instance.
For air-gapped environments: Each instance can define the same project structure and role assignments. Because roles reference names (not UUIDs), the same assignment — "group network-ops gets project-admin in project network-automation" — works identically across environments.
Beyond role-based checks, policies support conditions that match against attributes on resources, users, and groups. All condition fields use AND logic — every specified condition must match:
| Condition | Matches Against |
|---|---|
resource_labels |
Key-value labels on the resource being accessed |
resource_labels_not |
Inverted — resource must NOT have these label values |
user_labels |
Labels on the requesting user |
user_metadata |
Metadata fields on the requesting user |
resource_metadata |
Metadata fields on the resource |
group_labels |
Labels on any group the user belongs to |
What this enables for multi-tenancy:
- A shared credential labeled
scope=cross-env, sensitivity=highcan be accessed by workflows across multiple teams without duplicating it. - Policies can express cross-cutting constraints: "only users labeled
clearance=highcan execute workflows labeledenv=prod" — regardless of project membership. - Negative conditions prevent accidental access: "allow workflow:read on any resource EXCEPT those labeled
env=prod" usingresource_labels_not. - When you replicate a policy bundle to an air-gapped environment, it works immediately as long as resources and users carry the same label conventions.
The OPA engine enforces deny-by-default with a clear evaluation order:
- If any
denypolicy matches the action, scope, and conditions → denied - If no deny matched AND any
allowpolicy matches → allowed - If neither matched → denied (default)
Every access decision returns which policy contributed to the outcome:
{
"allow": false,
"deny": true,
"denied_by": "credential:read:deny-contractors",
"denial_reason": "policy_deny"
}Built-in introspection APIs let administrators and auditors query the system:
| Endpoint | Purpose |
|---|---|
POST /authz/can-i |
"Can I perform this action on this resource?" — returns yes/no with the deciding policy |
POST /authz/who-can |
"Who can perform this action?" — lists all users with access |
POST /authz/what-can-i |
"What are all my permissions?" — returns the user's complete effective policy set |
GET /authz/resource-actions |
"What resource types and actions exist?" — the full permission catalog |
For air-gapped compliance:
- When an auditor asks "why can this user run this workflow?", the system answers with the specific policy name — not just "they're in the admin group."
- If the policy engine is unavailable (degraded state), the system fails closed — denying all access rather than falling back to permissive defaults.
- Audit logs are per-instance, so each air-gapped environment maintains its own tamper-evident record of authorization decisions.
- The
who-canandwhat-can-iAPIs make access reviews across isolated environments repeatable and automatable.
The system ships with seven built-in roles that cover common access patterns out of the box. Crucially, these aren't hardcoded — they're named bundles of the same policy statements that custom roles use:
System-level roles:
| Role | Description |
|---|---|
admin |
Full access to all resources and administrative operations |
auditor |
Read-only access with visibility into audit logs, roles, and policies |
user |
Standard CRUD on workflows, credentials, executions; can create projects |
authenticated |
Default baseline for all logged-in users (editable by admins) |
Project-level roles:
| Role | Description |
|---|---|
project-admin |
Full control within a project, including role and policy management |
project-user |
Standard CRUD and execution within a project |
project-auditor |
Read-only access within a project |
Practical implications:
- Day-one setup is simple: assign built-in roles to groups and you have a working access model.
- Customization without policy authoring: Admins can create custom roles by combining existing permission sets via the API — no Rego knowledge required.
- Portable definitions: The same role names and permission semantics work across all instances. There's nothing instance-specific in a role like
project-user.
The system authenticates users via OIDC and maps identity provider claims to internal groups. When AAP Gateway is the identity provider, this mapping is zero-touch — AAP organizations and teams automatically map to equivalent authorization groups.
For environments using different OIDC providers (or no shared provider), configurable claim mappings let you define how provider-specific claims translate to authorization groups.
Why this is critical for air-gapped environments:
- Each air-gapped instance can use its own identity provider (Active Directory, Keycloak, etc.).
- As long as the claim mappings produce the same group structure, the same policies produce the same access decisions.
- Groups are pre-created by administrators in the Orchestrator and mapped to IdP claims — the system doesn't blindly ingest everything from the identity provider, giving you explicit control over your authorization surface.
Consider an organization with three air-gapped environments (dev, staging, prod), each with its own identity provider:
┌─────────────────────────────────────────────────────────┐
│ Shared Governance Artifacts (version-controlled) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ Label Schema │ │ Policy │ │ Role │ │
│ │ env, domain, │ │ Bundles │ │ Definitions │ │
│ │ team, level │ │ (JSON/YAML) │ │ (name→policy) │ │
│ └─────────────┘ └──────────────┘ └───────────────┘ │
└─────────┬──────────────────┬──────────────────┬─────────┘
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ DEV │ │ STAGING │ │ PROD │
│ │ │ │ │ │
│ IdP: LDAP │ │ IdP: AD │ │ IdP: AAP │
│ │ │ │ │ Gateway │
│ Claim Map:│ │ Claim Map:│ │ │
│ ou→group │ │ group→grp │ │ Zero-touch│
│ │ │ │ │ mapping │
│ Same │ │ Same │ │ Same │
│ policies │ │ policies │ │ policies │
│ Same roles│ │ Same roles│ │ Same roles│
│ Same │ │ Same │ │ Same │
│ labels │ │ labels │ │ labels │
└───────────┘ └───────────┘ └───────────┘
The workflow:
- Define a label convention — e.g.,
env,domain,sensitivity,team— and document it as organizational policy. - Author policy bundles — express your access rules referencing those labels. Store them in version control alongside your automation content.
- Deploy policies to each environment — the same bundle works everywhere because it references labels and role names, not instance-specific IDs.
- Map local IdP claims to groups — each environment maps its IdP's org/team claims to the same group names using claim mapping configuration.
- Label resources consistently — when workflows and credentials carry the same labels across environments, the same policies produce consistent access decisions.
- Audit with introspection APIs — use
can-i,who-can, andwhat-can-iin each environment to verify that access decisions are consistent.
The result: Consistent governance across isolated environments, without requiring a shared identity plane or manual RBAC replication.
Transparency about scope:
| Capability | Status |
|---|---|
| Policy-based authorization with OPA | Implemented — deny-first Rego evaluation with ABAC conditions |
| Project-scoped multi-tenancy | Implemented — projects as isolation boundaries with scoped roles |
| Built-in and custom roles/policies | Implemented — 7 built-in roles, custom roles via API |
| Introspection APIs (can-i, who-can) | Implemented — full explainability for access decisions |
| OIDC authentication with claim mapping | Implemented — AAP Gateway zero-touch + configurable mappings |
| User-authored Rego policies | Post-GA — GA ships with built-in permission sets and API-composed custom roles |
| Node-level workflow permissions | Post-GA — architecture supports it without redesign |
| Resource-to-resource policies | Post-GA — e.g., "prod workflows can only use prod credentials" |
| Cross-service authorization | Future SDP — how Orchestrator workflows authenticate to other AAP components |
| Automated policy sync across instances | Not planned — bring your own GitOps pipeline for now |
- Ansible Automation Platform — Platform overview
- Open Policy Agent — The policy engine behind the authorization model
- OPA Rego Language — Policy language reference
For questions about how this applies to your environment, reach out to your Red Hat account team or TAM.