惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
大猫的无限游戏
大猫的无限游戏
Hugging Face - Blog
Hugging Face - Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Vercel News
Vercel News
人人都是产品经理
人人都是产品经理
G
Google Developers Blog
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
雷峰网
雷峰网
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Jina AI
Jina AI
博客园 - 叶小钗
D
DataBreaches.Net
D
Docker
月光博客
月光博客
博客园 - 司徒正美
Last Week in AI
Last Week in AI
有赞技术团队
有赞技术团队
腾讯CDC
酷 壳 – CoolShell
酷 壳 – CoolShell

Cerbos - All Posts

Authentik vs Keycloak: Self-hosted IdP comparison Mapping business requirements to authorization policy for automotive Fine-grained authorization for AI gateways EIC 2026: Stop counting agents, protect what they can touch Agent skill for writing authorization policies in Claude Desktop Identity security in 2026 EIC 2026 takeaways: the identity stack built for humans will not hold up for AI agents Already have authentication? Here's the authorization layer you still need. Tokens are authorization decisions: a guide to policy-driven token issuance What is a Runtime Authorization Platform It's a dimmer switch, not a kill switch. How CISOs are rethinking AI agent governance From maps to bitmaps (and from bitmaps to bitmaps) AuthZEN, Shared Signals, SCIM Events, IPSIE: Notes from the OpenID Enterprise Panel How do you update authorization policies without redeploying your application? IIW42 recap: Where agent authorization got real Cerbos PDP v0.52.0/v0.53.0: Engine performance, security hardening, and CEL path functions Authorization Management Platforms: what they do, how they work, and where they fit PocketOS AI coding agent deleted a production database in 9 seconds Non-Human Identity management still has a blind spot Supabase alternative in 2026: Best open source auth options Benefits of on-premise authorization: Why enterprises are moving toward self-hosted Authorization policies: How to write, test, and validate them (faster with AI) Agent skill for writing authorization policies How much does it cost to build authorization in-house? Why centralized authorization governance reduces incident response time OPA alternative Why AI agents make authorization a right now problem Modernizing legacy application authorization: why it’s your biggest security blind spot How to add authorization to legacy applications without code changes 5 authorization blind spots auditors find, and how to fix them
Row-level security for Apache Trino, powered by Cerbos Sy...
Alex Olivier · 2026-04-07 · via Cerbos - All Posts

Most systems that need authorization weren't built with Cerbos in mind. Trino has an OPA plugin. Kafka has its own authorizer interface. Kubernetes has admission webhooks. Envoy has ext_authz. Each one speaks a different protocol, expects a different request format, and returns a different response shape.

Cerbos Synapse exists to solve that problem. It's an orchestration layer that sits between systems that don't speak Cerbos and the Cerbos PDP. It handles the protocol translation, enriches the request with data from external sources, evaluates the policy, and returns the response in whatever format the calling system expects.

Trino is a good example of what this looks like in practice, because Trino's authorization needs go beyond simple allow/deny. It needs row-level filtering and column masking, both of which require user attributes that Trino doesn't have and policy outputs that go beyond a boolean.

Above is a 3 minute walkthrough of the whole thing in action.

What Cerbos Synapse orchestrates

Trino's OPA plugin makes 3 types of HTTP calls during query execution:

Trino operation What it asks What it expects back
Access control "Can this user run this query on this table?" {"result": true/false}
Row filtering "What rows should this user see?" A SQL WHERE clause
Column masking "How should each column be displayed?" SQL expressions per column

Synapse handles all 3. It exposes endpoints that speak Trino's OPA protocol, translates each request into a Cerbos CheckResources call, and formats the Cerbos response back into what Trino expects. Trino doesn't know it's talking to Cerbos. It thinks it's talking to OPA.

But Synapse is doing more than protocol translation. Before the PDP evaluates, Synapse enriches the request with user attributes fetched from your identity provider. Trino only sends a username. Synapse turns that into a fully attributed principal with department, clearance level, role, whatever your IdP knows about that user. Those attributes then drive the policy decisions: which rows to filter, which columns to mask, and how.

Here's the flow when an analyst runs SELECT * FROM marketing.users:

Analyst runs query
       │
       ▼
  Trino Engine
       │
       ├──▶ Access control check (SelectFromColumns)
       │         │
       │         ├── Synapse looks up user attributes (dept, clearance)
       │         ├── Cerbos PDP evaluates: ALLOW
       │         └── Returns {"result": true}
       │
       ├──▶ Row filter check
       │         │
       │         ├── Looks up user attributes
       │         ├── Cerbos PDP evaluates GetRowFilter
       │         └── Returns: clearance IN ('public', 'internal')
       │
       └──▶ Column masking check
                 │
                 ├── Checks each column against policy
                 ├── email → partial mask (first 2 chars visible)
                 ├── phone → last 4 digits only
                 ├── clearance → [REDACTED] (analyst lacks confidential clearance)
                 └── other columns → pass through

The analyst gets 5 rows instead of 8 (confidential users filtered out), with emails showing al***@example.com, phone numbers showing ***-***-0101, and the clearance column replaced with [REDACTED]. The query looks normal. The results are shaped entirely by Cerbos policy.

Table-level access control

Policies live in Cerbos Hub, versioned and distributed to every Synapse instance automatically. Start with who can query which tables.

apiVersion: api.cerbos.dev/v1
resourcePolicy:
  resource: trino
  version: default
  importDerivedRoles:
    - user_groups
  rules:
    - name: deny_destructive_writes
      derivedRoles: [admin, viewer, analyst]
      actions: [DeleteFromTable, DropTable]
      effect: EFFECT_DENY

    - name: allow_viewer_select_users
      derivedRoles: [viewer]
      actions: [SelectFromColumns]
      condition:
        match:
          all:
            of:
              - expr: R.attr.resource.table.schemaName == "marketing"
              - expr: R.attr.resource.table.tableName == "users"
      effect: EFFECT_ALLOW

    - name: allow_analyst_select
      derivedRoles: [analyst]
      actions: [SelectFromColumns]
      condition:
        match:
          all:
            of:
              - expr: R.attr.resource.table.schemaName == "marketing"
              - expr: R.attr.resource.table.tableName in ["users", "transactions"]
      effect: EFFECT_ALLOW

    - name: allow_admin_select
      derivedRoles: [admin]
      actions: [CreateTable, SelectFromColumns]
      effect: EFFECT_ALLOW

The viewer can query marketing.users. The analyst can query marketing.users and marketing.transactions. The admin can query anything. Nobody can DROP TABLE or DELETE FROM.

When a viewer tries to query the transactions table, Trino gets a 403:

Query failed: OPA server returned status 403

Same Cerbos policy language you'd use for any other resource in your stack. The Trino-specific parts (table names, schema names, operations like SelectFromColumns) come from the protocol translation layer. The policy author doesn't need to know anything about Trino's OPA protocol.

Row-level filtering

Access control is binary: you can query the table or you can't. Row filtering is where Synapse's orchestration gets more interesting. Different users see different subsets of the same table, based on attributes that Synapse fetched from the identity provider.

GetRowFilter rules use policy outputs to return SQL WHERE clauses:

    - name: row_filter_viewer_users
      derivedRoles: [viewer]
      actions: [GetRowFilter]
      condition:
        match:
          all:
            of:
              - expr: R.attr.resource.table.tableName == "users"
      effect: EFFECT_ALLOW
      output:
        when:
          ruleActivated: |
            "department = '" + P.attr.department + "'"

    - name: row_filter_analyst_users
      derivedRoles: [analyst]
      actions: [GetRowFilter]
      condition:
        match:
          all:
            of:
              - expr: R.attr.resource.table.tableName == "users"
      effect: EFFECT_ALLOW
      output:
        when:
          ruleActivated: |
            "clearance IN ('public', 'internal')"

The viewer is in the marketing department, so their filter becomes department = 'marketing'. Out of 8 rows in the users table, they see 4. The analyst has internal clearance, so their filter excludes confidential rows: they see 5 out of 8. The admin has no GetRowFilter output, so no filter is applied. All 8.

The filter expression is built dynamically from principal attributes. P.attr.department is the value Synapse fetched from the identity provider. Change someone's department in the IdP, and their row filter changes on the next query. No policy update required.

Column masking

Column masking controls what data looks like even when you're allowed to see the row. The policy returns SQL expressions that Trino wraps around column values before returning results.

    - name: mask_email_for_viewer
      derivedRoles: [viewer]
      actions: [GetColumnMask]
      condition:
        match:
          all:
            of:
              - expr: R.attr.resource.column.columnName == "email"
      effect: EFFECT_ALLOW
      output:
        when:
          ruleActivated: |
            "'***@' || substring(email, position('@' in email) + 1)"

    - name: mask_email_for_analyst
      derivedRoles: [analyst]
      actions: [GetColumnMask]
      condition:
        match:
          all:
            of:
              - expr: R.attr.resource.column.columnName == "email"
      effect: EFFECT_ALLOW
      output:
        when:
          ruleActivated: |
            "substr(email, 1, 2) || '***@' || substring(email, position('@' in email) + 1)"

Same column, different masks depending on role:

User Email column Phone column
admin alice@example.com 555-0101
analyst al***@example.com ***-***-0101
viewer ***@example.com ***-***-0101

Role-based masking is the easy case though. The more interesting pattern is attribute-based masking.

Attribute-based masking

The clearance column in the users table contains values like public, internal, and confidential. Whether you can see those values depends on your own clearance level.

    - name: mask_clearance_for_non_confidential
      derivedRoles: [viewer, analyst]
      actions: [GetColumnMask]
      condition:
        match:
          all:
            of:
              - expr: R.attr.resource.column.columnName == "clearance"
              - expr: P.attr.clearance != "confidential"
      effect: EFFECT_ALLOW
      output:
        when:
          ruleActivated: |
            "'[REDACTED]'"

The condition checks P.attr.clearance, an attribute Synapse enriched from the identity provider. If your clearance isn't confidential, the clearance column shows [REDACTED] regardless of your role.

This is where the orchestration pays off. The policy references an attribute (P.attr.clearance) that doesn't exist in Trino's request. Synapse fetched it from the IdP, attached it to the principal, and made it available to the PDP. The policy author writes P.attr.clearance != "confidential" and it just works, because Synapse handled the plumbing.

What the results look like

3 users, same query: SELECT id, name, email, phone, clearance FROM marketing.users

Admin (engineering, confidential clearance):

 id |  name   |       email        |  phone   |  clearance
----+---------+--------------------+----------+--------------
  1 | Alice   | alice@example.com  | 555-0101 | confidential
  2 | Bob     | bob@example.com    | 555-0102 | internal
  3 | Charlie | charlie@example.com| 555-0103 | public
  ...
(8 rows)

Analyst (analytics, internal clearance):

 id |  name   |         email          |    phone     |  clearance
----+---------+------------------------+--------------+------------
  2 | Bob     | bo***@example.com      | ***-***-0102 | [REDACTED]
  3 | Charlie | ch***@example.com      | ***-***-0103 | [REDACTED]
  5 | Eve     | ev***@example.com      | ***-***-0105 | [REDACTED]
  ...
(5 rows, confidential users excluded)

Viewer (marketing, public clearance):

 id |  name   |       email       |    phone     |  clearance
----+---------+-------------------+--------------+------------
  3 | Charlie | ***@example.com   | ***-***-0103 | [REDACTED]
  4 | Dana    | ***@example.com   | ***-***-0104 | [REDACTED]
  7 | Grace   | ***@example.com   | ***-***-0107 | [REDACTED]
  ...
(4 rows, marketing department only)

Same table. Same query. 3 different views. Every difference is driven by Cerbos policy, evaluated by the PDP, orchestrated by Synapse.

The operational model

Once this is running, the day-to-day is pretty clean.

Policies live in Cerbos Hub. Versioned, testable, and they go through the same review process as any other code change. Want to give the analyst access to a new table? PR the policy, get it reviewed, merge it, it's live across every Synapse instance. No Trino restart. No config pushes.

User attributes come from your identity provider. The data source is a connector, not a copy. When someone changes departments in Okta, their row filter changes on the next query. When someone's clearance level changes, their column masks change. No policy update needed. The policy already references P.attr.department and P.attr.clearance. The data is live.

Audit logs flow to Hub automatically. Every access control decision, every row filter applied, every column mask returned. Searchable, filterable, exportable. When the compliance team asks "what could the analytics team see last quarter," you pull a report.

Testing is built in. Cerbos Hub's test framework lets you write assertions against your Trino policies the same way you'd test any other Cerbos policy. Verify all 3 authorization layers before every deploy: which users can query which tables, what row filters are applied, and what column masks are returned.

Why this matters

Area Details
One policy language Trino's OPA plugin expects Rego. Kafka's authorizer expects its own format. Envoy expects something else. With Synapse, you write Cerbos policies for all of them. Your team learns one policy language, uses one test framework, and manages one set of policies in Hub.
Data governance Row filtering and column masking are defined in policy, not in views, application code, or custom Trino plugins. One policy file governs who sees what across every table. Changes are auditable, testable, and versioned.
Compliance GDPR, HIPAA, SOC 2: they all want to know who accessed PII and what controls were in place. With Synapse, you have a timestamped log of every query authorization. Which user, which table, what filter was applied, what columns were masked, and what the policy decided.
Attribute-based access Role-based masking is a start. Attribute-based masking is what real data governance requires. A user's clearance level, department, or risk score can all factor into what they see. Those attributes can change without touching the policy. Synapse fetches the current values on every request.

The broader pattern

Trino is one system. The pattern is the same for any system that calls out to an external policy server but doesn't speak Cerbos natively.

Synapse's route extension system can implement any HTTP-based authorization protocol. Trino speaks OPA's protocol. Kafka speaks its own. Kubernetes admission control speaks another. Envoy has ext_authz. For each one, Synapse handles the protocol translation. The policy engine, the policy language, and the audit trail stay the same.

The data source layer is shared across all of them too. The same user profile lookup that enriches Trino authorization decisions enriches every other system Synapse orchestrates. Write the connector once, use it everywhere.

If your data platform runs Trino for analytics, Kafka for streaming, and application services behind Envoy, you can govern all 3 from one policy store in Hub, with one audit trail, using one set of tools your team already knows. That's what Synapse orchestrates.

Get started

If you're running Trino and want to bring Cerbos policy decisions into your data layer, we'd like to show you how Synapse fits into your stack. Reach out to the Cerbos team, or explore Cerbos Hub.