Cerbos - An Open Source User Authorization Solution

Introduction⌗
Recently, due to business needs for permission management and user authorization, I researched multiple open source solutions as well as custom-built permission management options, and ultimately found this open source user authorization solution called Cerbos.
Cerbos is developed in Go, supports both RESTful and gRPC calls, and provides SDKs for common languages. However, these weren’t the main reasons for choosing it, as other open source projects also offer similar support. What ultimately made me choose Cerbos among many open source solutions were:
- Stateless authorization as a service
- Support for multiple backend storage options (for storing authorization policies)
- Simple deployment and user-friendly documentation
Comparison⌗
During my research, I reviewed documentation and source code from many open source projects, including:
- Oso: https://www.osohq.com
- OPA: https://www.openpolicyagent.org
- OPAL: https://www.opal.ac
- Ladon: https://github.com/ory/ladon
- Permify: https://permify.co
- OpenFGA: https://openfga.dev
- Authzed: https://authzed.com
- Pomerium: https://www.pomerium.com
- Shield: https://raystack.github.io/shield
- Speedle: https://speedle.io
- Warrant: https://warrant.dev
- Cerbos: https://cerbos.dev
Project | Language | Stateless | Library | Service | Dynamic Policy |
---|---|---|---|---|---|
Oso | Rust | Yes | Yes | No | No |
OPA | Go | Yes | Yes | Yes | No |
OPAL | Python | Yes | No | Yes | Yes |
Ladon | Go | Yes | Yes | No | Yes |
Permify | Go | No | No | Yes | Yes |
OpenFGA | Go | No | No | Yes | Yes |
Authzed | Go | No | No | Yes | Yes |
Pomerium | Go | No | No | Yes | Yes |
Shield | Go | No | No | Yes | Yes |
Speedle | Go | No | No | Yes | Yes |
Warrant | Go | NO | No | Yes | Yes |
Cerbos | Go | Yes | No | Yes | Yes |
Why do I value statelessness? Because this approach is simplest for developers, eliminating the need to maintain relationships between users and resources or users and roles/permissions in other databases. Only Oso, OPA, OPAL, Ladon, and Cerbos support statelessness. Oso can only be used as a library and doesn’t support languages like PHP.
OPA can be used as a library in Go projects or as a standalone service, but it doesn’t support dynamic policy updates. However, OPA’s policy writing syntax is the most powerful and flexible!
OPAL adds a Python wrapper on top of OPA, using a Python-developed service to implement dynamic Policy file updates.
Finally, there’s Cerbos, which supports local storage, Git, SQLite, MySQL, and other common databases, but only for storing policies and role definitions! The policies can be defined in YAML or JSON format, making it much more convenient for those who want to manage policies and roles through an Admin API!
Configuration⌗
---
server:
cors:
allowedOrigins:
- example.com
allowedHeaders:
- X-RequestID
adminAPI:
enabled: true
adminCredentials:
username: {ADMIN_USER}
passwordHash: {BASE64_ENCODE_HASH_PASS}
httpListenAddr: ":3592"
grpcListenAddr: ":3593"
metricsEnabled: true
logRequestPayloads: true
storage:
driver: disk
git:
protocol: ssh
url: [email protected]:services/policies.git
subDir: policies
branch: master
checkoutDir: /etc/cerbos/policies
updatePollInterval: 60s
ssh:
user: git
privateKeyFile: ${HOME}/.ssh/id_rsa
sqlite3:
dsn: ":memory:"
disk:
directory: /policies
watchForChanges: true
telemetry:
disabled: true
engine:
defaultPolicyVersion: "default"
auxData:
jwt:
keySets:
- id: zitadel
remote:
url: https://passport.example.com/.well-known/openid-configuration
schema:
enforcement: reject
Deployment⌗
docker-compose.yaml
configuration is as follows:
services:
cerbos:
image: ghcr.io/cerbos/cerbos:latest
restart: always
container_name: cerbos
ports:
- 0.0.0.0:3592:3592
- 0.0.0.0:3593:3593
command: ["server", "--config=/config/config.yaml"]
volumes:
- ./cerbos/config:/config
- ./cerbos/policies:/policies
networks:
services:
name: services
external: true
Writing Policies⌗
The structure of the policies
directory is as follows:
.
├── _schemas
│ └── customer.json
└── crm
├── principals
└── resources
└── customer
├── customer.yaml
└── derivedRoles.yaml
The content of policies/_schemas/customer.json
is as follows:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"trackerID": {
"type": "string"
}
},
"required": [
"trackerID"
]
}
Note: _schemas must be in the root directory of
policies
!!!
The content of the policies/crm/resources/customer/customer.yaml
configuration file is as follows:
---
apiVersion: 'api.cerbos.dev/v1'
resourcePolicy:
version: 'default'
resource: 'crm:customer'
importDerivedRoles:
- customer_owner_role
rules:
- actions:
- '*'
roles:
- admin
effect: EFFECT_ALLOW
- actions:
- view
- create
roles:
- user
- sales
effect: EFFECT_ALLOW
- actions:
- update
- delete
- transfer
- tracking
effect: EFFECT_ALLOW
roles:
- owner
condition:
match:
any:
of:
- expr: request.resource.attr.trackerID == ""
- expr: request.resource.attr.trackerID == request.principal.id
schemas:
resourceSchema:
ref: "cerbos:///customer.json"
The content of policies/crm/resources/customer/derivedRoles.yaml
is as follows:
---
apiVersion: "api.cerbos.dev/v1"
description: "Common dynamic roles used within the CRM"
variables:
flagged_resource: request.resource.attr.flagged
derivedRoles:
name: customer_owner_role
definitions:
- name: owner
parentRoles: ["user"]
condition:
match:
expr: request.resource.attr.trackerID == request.principal.id
The above policy restricts customer resources in the CRM:
ADMIN
can perform all Actions- Regular
user
andsales
roles can view and create customer information owner
canupdate
,delete
,transfer
, andtracking
customer information, but only if the user has no trackerID or the trackerID is their own- The configuration in
derivedRoles.yaml
allows derived roles - when a customer’s trackerID equals the user’s ID, that user hasowner
permissions for that customer resource
At this point, the user authorization model is complete. I’ll try to implement user authorization logic in other projects in the future!
I hope this is helpful, Happy hacking…