OPA — Fine-Grained Access Control¶
Open Policy Agent (OPA) provides policy-as-code authorization for AKKO. It integrates with Trino to enforce row-level security, column masking, and attribute-based access control (ABAC) — all defined in Rego policies.
How It Works¶
OPA evaluates every Trino query against Rego policies before execution:
- Authorization — Can this user access this catalog/schema/table?
- Row filtering — Which rows should this user see?
- Column masking — Should sensitive columns be redacted?
Policy Structure¶
Policies are defined in Rego and deployed as a ConfigMap (see helm/akko/charts/akko-opa/templates/configmap.yaml). Three policy files handle different aspects:
| File | Purpose |
|---|---|
trino_access.rego |
Operation-level allow/deny per role |
column_masking.rego |
PII column masking for user/viewer |
row_filter.rego |
Row-level security (e.g., viewers see active accounts only) |
package trino
import rego.v1
# Default deny — every access must be explicitly allowed
default allow := false
# Admins can do everything
allow if {
"akko-admin" in input.context.identity.groups
}
# Standard users (compliance): SELECT only, PII masked (see column_masking.rego)
allow if {
"akko-user" in input.context.identity.groups
input.action.operation in [
"ExecuteQuery", "AccessCatalog", "FilterCatalogs",
"FilterSchemas", "FilterTables", "FilterColumns",
"SelectFromColumns", "ShowSchemas", "ShowTables",
"ShowColumns", "ShowCreateTable", "ShowFunctions", "ShowStats"
]
}
RBAC Roles¶
OPA policies map to five Keycloak RBAC roles:
| Role | Access Level |
|---|---|
akko-admin |
Full access, no masking |
akko-engineer |
DDL + DML (CREATE, INSERT, SELECT), no masking |
akko-analyst |
SELECT only, full PII visibility (no masking) |
akko-user |
SELECT only, PII columns masked |
akko-viewer |
SELECT only, row-filtered, PII masked |
For the full access matrix and policy details, see the RBAC Administration Guide.
Trino Integration¶
Trino is configured to delegate access control to OPA:
access-control.name=opa
opa.policy.uri=http://akko-akko-opa:8181/v1/data/trino/allow
opa.policy.row-filters-uri=http://akko-akko-opa:8181/v1/data/trino/rowFilters
opa.policy.column-masking-uri=http://akko-akko-opa:8181/v1/data/trino/columnMask
Configuration¶
Kubernetes (Helm)¶
OPA is lightweight — it requires minimal resources and adds negligible latency to queries.
Adding Policies¶
- Create or edit Rego files in the OPA policies directory
- Policies are mounted via ConfigMap using
subPathper file (not directory mount, to avoid symlink loops) - OPA hot-reloads policies — no restart required
Testing Policies
Use opa eval locally to test policies before deploying:
Troubleshooting¶
Policy Evaluation Denied Unexpectedly¶
Symptoms: Trino queries fail with Access Denied for users who should have access. The OPA decision log shows "result": false for requests that should be allowed.
Cause: The Rego policy logic is incorrect, the user's group memberships from Keycloak are not being passed correctly in the OPA input, or a more restrictive rule is overriding the default allow = true.
Solution:
# Check OPA decision logs
kubectl logs -n akko deploy/akko-akko-opa --tail=100
# Query OPA directly to debug the decision
kubectl exec -n akko deploy/akko-akko-opa -- curl -s -X POST \
http://localhost:8181/v1/data/trino/allow \
-d '{"input":{"context":{"identity":{"user":"alice","groups":["akko-admin"]}},"action":{"operation":"SelectFromColumns"},"resource":{"table":{"catalogName":"iceberg","schemaName":"banking","tableName":"customers"}}}}' | python3 -m json.tool
# Verify the user's groups are correctly mapped from Keycloak
kubectl exec -n akko deploy/akko-trino-coordinator-0 -- \
curl -s http://localhost:8080/v1/info | python3 -m json.tool
Trino Integration Failure (Bundle Not Loading)¶
Symptoms: Trino starts but all queries return Access Denied. OPA logs show Bundle download failed or policy not found. The OPA /health endpoint returns unhealthy.
Cause: The policy ConfigMap is not mounted correctly, the Rego files are empty (0-byte due to misconfigured .Files.Get), or OPA cannot parse the policy files due to syntax errors.
Solution:
# Check OPA pod health
kubectl get pods -n akko -l app.kubernetes.io/name=akko-opa
# Verify policy files are mounted and non-empty
kubectl exec -n akko deploy/akko-akko-opa -- ls -la /policies/
kubectl exec -n akko deploy/akko-akko-opa -- cat /policies/trino.rego
# Check OPA logs for policy compilation errors
kubectl logs -n akko deploy/akko-akko-opa --tail=50 | grep -i "error\|compile\|parse\|bundle"
# Verify the ConfigMap contains the policies
kubectl get cm -n akko -l app.kubernetes.io/name=akko-opa -o yaml | head -50
# If files are 0-byte, check the Helm chart uses .Files.Get (not .Values)
Role Mapping from Keycloak JWT¶
Symptoms: OPA policies that check input.context.identity.groups never match. Users authenticated via Keycloak are treated as having no groups, even though they have roles assigned.
Cause: Trino is not extracting groups from the Keycloak JWT token correctly. The groups claim may be nested under realm_access.roles or resource_access instead of a top-level groups field.
Solution:
# Decode a JWT token to inspect its structure
# First, get a token from Keycloak
kubectl exec -n akko deploy/akko-keycloak -- \
curl -s -X POST http://localhost:8080/realms/akko/protocol/openid-connect/token \
-d "client_id=trino&client_secret=<secret>&grant_type=client_credentials" \
| python3 -c "import sys,json,base64; t=json.load(sys.stdin)['access_token'].split('.')[1]; print(json.dumps(json.loads(base64.urlsafe_b64decode(t+'==')),indent=2))"
# Verify Trino's group extraction config
kubectl exec -n akko deploy/akko-trino-coordinator-0 -- cat /etc/trino/config.properties | grep -i group
# Check OPA input by enabling decision logging
kubectl exec -n akko deploy/akko-akko-opa -- curl -s http://localhost:8181/v1/data/trino/allow \
-X POST -d '{"input":{"context":{"identity":{"user":"alice","groups":[]}}}}'
Policy Syntax Errors in Rego¶
Symptoms: OPA pod starts but policies are not loaded. Logs show rego_parse_error or rego_type_error. All Trino queries fail with access denied.
Cause: The Rego policy file contains syntax errors (missing imports, wrong operators, undefined variables, or incompatible future.keywords usage).
Solution:
# Check OPA logs for parse/compile errors
kubectl logs -n akko deploy/akko-akko-opa --tail=50 | grep -i "error\|parse\|compile"
# Validate the policy locally before deploying
kubectl exec -n akko deploy/akko-akko-opa -- opa check /policies/trino.rego
# Test the policy with a sample input
kubectl exec -n akko deploy/akko-akko-opa -- opa eval \
-d /policies/trino.rego \
-i /dev/stdin "data.trino.allow" <<< '{"context":{"identity":{"user":"alice","groups":["akko-admin"]}}}'
# If the policy is broken, fix the Rego file in the Helm chart and redeploy
# The ConfigMap will be updated and OPA hot-reloads automatically