AI Functions RBAC Enforcement¶
AKKO ships 21 Trino scalar functions (ai_sentiment, ai_pii, ai_embed, ai_parse_document, etc.) that forward calls to the AI Service. Because Trino's OPA authorizer does not fire ExecuteFunction on plugin scalar UDFs, RBAC is enforced in the AI Service middleware, not in OPA directly.
Enforcement flow¶
Trino query: SELECT ai_sentiment('hello') FROM ...
│
▼
Trino plugin (trino-ai-functions) forwards:
Authorization: Bearer ${AKKO_AI_SERVICE_TOKEN}
X-Trino-User: <caller>
X-Akko-Ai-Function: ai_sentiment
│
▼
AI Service middleware (docker/ai-service/app.py):
1. Validate Bearer token against Secret akko-trino-ai
2. Resolve caller's realm role via Keycloak admin API (cached 300s)
3. Check the RBAC matrix: is ai_sentiment allowed for this role?
4. ALLOW → execute + metrics + audit log
DENY → return 403 + metrics + audit log
RBAC matrix (17 text functions + 4 unstructured)¶
| Function | akko-admin | akko-engineer | akko-analyst | akko-steward | akko-viewer |
|---|---|---|---|---|---|
ai_sentiment |
✓ | ✓ | ✓ | ✗ | ✗ |
ai_classify |
✓ | ✓ | ✓ | ✗ | ✗ |
ai_summarize |
✓ | ✓ | ✓ | ✗ | ✗ |
ai_translate |
✓ | ✓ | ✗ | ✗ | ✗ |
ai_entities |
✓ | ✓ | ✗ | ✗ | ✗ |
ai_anomaly |
✓ | ✓ | ✗ | ✗ | ✗ |
ai_sql |
✓ | ✓ | ✗ | ✗ | ✗ |
ai_risk |
✓ | ✓ | ✗ | ✗ | ✗ |
ai_pii |
✓ | ✓ | ✗ | ✓ | ✗ |
ai_sensitivity |
✓ | ✓ | ✗ | ✓ | ✗ |
ai_language |
✓ | ✓ | ✓ | ✗ | ✗ |
ai_keywords |
✓ | ✓ | ✓ | ✗ | ✗ |
ai_ask |
✓ | ✓ | ✗ | ✗ | ✗ |
ai_embed |
✓ | ✓ | ✗ | ✗ | ✗ |
ai_parse_document |
✓ | ✓ | ✓ | ✗ | ✗ |
ai_transcribe |
✓ | ✓ | ✓ | ✗ | ✗ |
ai_describe_image |
✓ | ✓ | ✓ | ✗ | ✗ |
ai_ocr |
✓ | ✓ | ✓ | ✗ | ✗ |
ai_cache_clear (admin) |
✓ | ✗ | ✗ | ✗ | ✗ |
ai_cb_reset (admin) |
✓ | ✗ | ✗ | ✗ | ✗ |
ai_stats |
✓ | ✓ | ✗ | ✗ | ✗ |
Viewers get zero AI functions — any call returns HTTP 403.
Activation¶
Enforcement is gated by two Helm values. Both default to false for dev
convenience; production deployments MUST set them to true.
akko-ai-service:
serviceToken:
enabled: true # Trino plugin must present a shared Bearer token
secretName: akko-trino-ai
secretKey: service-token
rbac:
enabled: true
keycloakUrl: "http://akko-akko-keycloak:8080"
keycloakRealm: akko
userRoleCacheTtlSeconds: 300
The akko-trino-ai Secret is auto-generated by helm/akko/templates/secrets.yaml
and reused across upgrades via lookup. The same token is injected into the
Trino deployment as AKKO_AI_SERVICE_TOKEN so the plugin can authenticate.
Testing¶
# As viewer (dave) → expected 403
kubectl exec -n akko svc/akko-trino -- \
trino --user dave --execute "SELECT ai_sentiment('test')"
# Error: Access Denied: role 'akko-viewer' not permitted for function 'ai_sentiment'
# As analyst (carol) → expected success
kubectl exec -n akko svc/akko-trino -- \
trino --user carol --execute "SELECT ai_sentiment('test')"
# => POSITIVE
# Admin function → viewer denied
kubectl exec -n akko svc/akko-trino -- \
trino --user alice --execute "SELECT ai_cache_clear()"
# => OK
kubectl exec -n akko svc/akko-trino -- \
trino --user bob --execute "SELECT ai_cache_clear()"
# Error: admin-only function
Playwright E2E test: tests/playwright/rbac-ai-functions.spec.ts verifies the
full matrix against a live cluster.
Observability¶
Prometheus counters (exposed by AI Service /metrics):
akko_ai_rbac_allowed_total{function,role}akko_ai_rbac_denied_total{reason,function,role}
Audit log (structured JSON in stdout → logs layer/logs layer):
{
"audit_type": "AI_RBAC",
"decision": "DENY",
"user": "dave",
"role": "akko-viewer",
"function": "ai_sentiment",
"reason": "role_not_permitted",
"timestamp": "2026-04-19T14:22:07Z"
}
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| All calls succeed from viewer | rbac.enabled=false or serviceToken.enabled=false |
Flip both to true in values-<env>.yaml, helm upgrade |
| All calls return 401 | Secret akko-trino-ai missing or Trino not reading it |
Check kubectl describe deploy/akko-trino-coordinator for env AKKO_AI_SERVICE_TOKEN |
| All calls return 403 for valid roles | Keycloak admin creds invalid → fallback default_role |
Check AI Service logs for kc_admin_login_failed, rotate creds |
| Audit log empty | logs layer (or logs layer) not collecting stdout | Verify promtail/vector targets AI Service pods |