Skip to content

Instantly share code, notes, and snippets.

@jlmitch5
Last active May 12, 2026 17:45
Show Gist options
  • Select an option

  • Save jlmitch5/d5553c6726fe88945f4c2d99f9486714 to your computer and use it in GitHub Desktop.

Select an option

Save jlmitch5/d5553c6726fe88945f4c2d99f9486714 to your computer and use it in GitHub Desktop.

Policy-Based Authorization in Ansible Automation Platform's Automation Orchestrator

How the Automation Orchestrator's authorization model helps enterprises manage multi-tenancy across air-gapped environments


The Problem: Governing Automation Across Isolated 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.

How the Orchestrator's Authorization Model Addresses This

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:

1. Policies as Portable Artifacts, Not Database State

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.

2. Projects as Multi-Tenancy Boundaries

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.

3. Attribute-Based Access Control (ABAC) via Labels

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=high can be accessed by workflows across multiple teams without duplicating it.
  • Policies can express cross-cutting constraints: "only users labeled clearance=high can execute workflows labeled env=prod" — regardless of project membership.
  • Negative conditions prevent accidental access: "allow workflow:read on any resource EXCEPT those labeled env=prod" using resource_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.

4. Deny-First with Explainable Decisions

The OPA engine enforces deny-by-default with a clear evaluation order:

  1. If any deny policy matches the action, scope, and conditions → denied
  2. If no deny matched AND any allow policy matches → allowed
  3. 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-can and what-can-i APIs make access reviews across isolated environments repeatable and automatable.

5. Built-In Roles as Policy Bundles (Not Hardcoded Logic)

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.

6. Identity Provider Flexibility with OIDC Claim Mapping

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.

Putting It Together: A Multi-Environment Consistency Pattern

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:

  1. Define a label convention — e.g., env, domain, sensitivity, team — and document it as organizational policy.
  2. Author policy bundles — express your access rules referencing those labels. Store them in version control alongside your automation content.
  3. Deploy policies to each environment — the same bundle works everywhere because it references labels and role names, not instance-specific IDs.
  4. Map local IdP claims to groups — each environment maps its IdP's org/team claims to the same group names using claim mapping configuration.
  5. Label resources consistently — when workflows and credentials carry the same labels across environments, the same policies produce consistent access decisions.
  6. Audit with introspection APIs — use can-i, who-can, and what-can-i in 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.


What This Doesn't Solve (Yet)

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

Learn More

For questions about how this applies to your environment, reach out to your Red Hat account team or TAM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment