Skip to content

Governance — RBAC

AKKO enforces a single, platform-wide RBAC model that flows from Keycloak through OPA into every service that touches data. This page is the governance-level summary. For the full operational guide (adding users, Rego examples, API scripts, testing), see Admin / RBAC.

One Model, Four Enforcement Points

flowchart LR
    KC[Keycloak realm akko<br/>5 realm roles + groups] --> JWT[JWT with groups claim]
    JWT --> S1[Service OIDC<br/>Superset, Airflow, Grafana, JupyterHub, MLflow]
    JWT --> OPA[OPA<br/>row filters + column masks]
    JWT --> LLM[LiteLLM<br/>per-role virtual keys]
    OPA --> TR[Trino]
    S1 --> TR
    LLM --> OLL[Ollama]

Four enforcement points consume the same identity:

  1. Service OIDC — Superset, Airflow, Grafana, JupyterHub, MLflow map groups claim to their internal roles.
  2. OPA — Trino delegates authorization, row filters and column masking decisions.
  3. Trino file rules — catalog/schema/table ACLs refreshed every 5 minutes.
  4. LiteLLM — virtual API keys scoped per role, so model routing and rate limits follow the user.

The Six Effective Roles

AKKO ships five realm roles in Keycloak, plus an implicit service-account role for non-human identities. Each role is the authoritative source of truth — mapped automatically to every downstream service.

Role Persona Data visibility PII Query power
akko-admin SRE / platform owner (Alice) all catalogs, all schemas clear DDL, DML, SELECT
akko-engineer Data engineer (Bob) raw, staging, analytics, sandbox clear DDL on designated schemas, INSERT, SELECT
akko-analyst Senior analyst (Carol) read-only all schemas clear SELECT only
akko-user Standard user / compliance (Eve) read-only all schemas masked (email, phone, ssn, dob, mrn) SELECT only
akko-viewer Executive / dashboard viewer (Dave) analytics, reporting, public masked SELECT, row-filtered
service-account CI jobs, dbt, Airflow DAGs project-scoped per project scoped per token

Service-Level Mapping

Service akko-admin akko-engineer akko-analyst akko-user akko-viewer
Trino DDL + DML DDL on raw/staging/analytics/sandbox SELECT SELECT + PII mask SELECT + row filter + PII mask
Superset Admin Alpha Gamma Gamma Public
Airflow Admin User Viewer Viewer Viewer
Grafana Admin Editor Viewer Viewer Viewer
JupyterHub admin panel + spawn spawn spawn spawn spawn
MLflow full full full full full
LiteLLM all models, 1000 rpm all models, 200 rpm all models, 100 rpm chat + embed, 50 rpm chat + embed, 20 rpm
OpenMetadata admin browse + edit browse + edit browse browse
MinIO root root root root root

MinIO, MLflow and Spark still use shared credentials; per-user enforcement is tracked in Stream 19 of the roadmap.

OPA — Row Filters and Column Masks

OPA is the authority for row-level security and column masking on top of Trino's file-based ACLs. Policies are hot-reloaded from a ConfigMap, no pod restart.

# package trino — excerpt

default allow := false

allow if { "akko-admin" in input.context.identity.groups }

# akko-user: SELECT-only
allow if {
    "akko-user" in input.context.identity.groups
    input.action.operation in {
        "ExecuteQuery","AccessCatalog","FilterCatalogs","FilterSchemas",
        "FilterTables","FilterColumns","SelectFromColumns",
        "ShowSchemas","ShowTables","ShowColumns","ShowStats"
    }
}

# Column masking for PII — applies to akko-user and akko-viewer
columnMask := {"expression": "'***MASKED***'", "identity": "mask_pii"} if {
    input.action.resource.column.columnName in {"email","phone","ssn","medical_record_number"}
    not "akko-admin"    in input.context.identity.groups
    not "akko-engineer" in input.context.identity.groups
    not "akko-analyst"  in input.context.identity.groups
}

# Row filter: viewers see only active accounts
rowFilters contains {"expression": "status = 'active'", "identity": "viewer_active_only"} if {
    "akko-viewer" in input.context.identity.groups
    input.action.resource.table.tableName == "accounts"
}

PII Tag Sync from OpenMetadata

A sidecar (opa-sync) polls OpenMetadata every 5 minutes and pushes PII column tags as OPA data. New columns tagged PII are masked automatically without a policy redeploy. See Security Flow.

LLM RBAC

LiteLLM enforces per-role virtual keys, routing and rate limits. See Admin / LLM RBAC for the full matrix.

Role Models allowed Rate limit Max context
akko-admin all 1000 rpm 128 k
akko-engineer all 200 rpm 64 k
akko-analyst chat + embed 100 rpm 32 k
akko-user chat + embed 50 rpm 16 k
akko-viewer chat only 20 rpm 8 k

Trino AI functions — per-UDF enforcement

The 17 ai_* UDFs exposed by the Trino plugin bypass Trino's native OPA ExecuteFunction check (plugin UDFs don't flow through the access-control SPI). RBAC is therefore enforced at the AI Service layer:

  1. The Trino plugin forwards X-Trino-User + X-Akko-Ai-Function: ai_<name> on every HTTP call.
  2. AI Service resolves the forwarded user's realm role via the Keycloak Admin API (cached 5 min) and checks the per-function matrix.
  3. Denials return HTTP 403 → SQL NULL (fail-closed); every decision is logged to Loki with audit_type=AI_RBAC and counted in Prometheus.

Full 17×5 matrix: see AI / Trino functions. Source of truth: helm/akko/charts/akko-ai-service/values.yaml, aligned with helm/akko/charts/akko-opa/templates/configmap.yaml.

Enterprise Federation

When global.auth.mode=enterprise, the seed demo users are disabled and Keycloak federates either:

  • a corporate LDAP (LLDAP or external),
  • or an upstream OIDC provider (Azure AD, Okta, Google Workspace, KeyCloak, Authentik),
  • or SAML (ADFS, PingFederate).

See Admin / External Identity Provider and Admin / Enterprise User Federation.

Audit Trail

Three log streams land in Grafana for forensic review:

Source Format Examples
Keycloak events JSON via keycloak-event-listener-http LOGIN, LOGOUT, UPDATE_PASSWORD, TOKEN_REFRESH
OPA decision log JSON console decision log {user, input, result, policy}
Trino query log JSON event listener {queryId, user, sql, bytes_scanned, duration}
ADEN audit_log JSON to Loki aden_query_received, aden_opa_denied, aden_pii_masked

See Admin / Audit Playbook.

Troubleshooting (Top 5)

Symptom Check Fix
User sees no data in Superset groups claim in JWT, Superset role mapping Re-login; verify Keycloak role assignment
Column still visible in clear Role is admin/engineer/analyst Expected — masking only applies to user/viewer
OAuth2-Proxy rejects user emailVerified is false Enable in Keycloak admin UI
Airflow / Grafana role stuck after change Token cached 5 min User must log out and back in
New OPA rule not picked up ConfigMap not synced kubectl rollout restart deploy/akko-akko-opa (rare) or wait 30 s