Quick Start :: Cerbos Authorization Management Platform // Documentation
Quick Start
Cerbos is an open-source authorization engine. Policies written in YAML declare which actions a principal (the user or service making a request) may perform on a resource (the thing being accessed). At runtime, an application asks the Cerbos PDP "can principal P do action A on resource R?" and the PDP evaluates the applicable policies to return an allow or deny decision.
Synapse is a proxy that sits in front of Cerbos. It exposes the same Cerbos API, so calling applications remain unchanged. Before each request reaches the PDP, Synapse can look up data from other systems, add attributes to principals or resources, rewrite requests, or modify responses. Lookups that would otherwise live in every application are centralized in the proxy.
This guide covers running Synapse locally and writing a first proxy extension in Starlark. The extension looks up user data from an external API and attaches it to the principal before the embedded Cerbos PDP evaluates a policy. Other extension types are covered in their own guides.
The finished setup runs Synapse in front of an embedded Cerbos PDP. Each CheckResources request is intercepted, the principal is enriched with attributes fetched from a public test API, and a policy evaluates the enriched request. The client sends the same call it would send to Cerbos directly; no application code changes.
Synapse processes each Cerbos API request in three stages:
- The caller sends a
CheckResourcesorPlanResourcesrequest. - Synapse runs the configured proxy extensions, which may modify the request (for example, by adding attributes to the principal) and later modify the response.
- The augmented request is handed to the PDP, which evaluates the relevant policies and returns a decision.
Before you begin
You will need:
Docker
A Synapse licence. If you do not already have one, sign up at cerbos.dev/workshop. Your licence credentials are issued together with the URL of the Cerbos distribution repository, substitute that URL wherever
CERBOS_DISTRIBUTION_REPOappears in this guide.curland a JSON formatter such aspython3 -m json.toolorjq(optional, for reading responses).
Log in to the Cerbos distribution repository:
$ docker login CERBOS_DISTRIBUTION_REPO --username=YOUR_LICENCE_USER --password=YOUR_LICENCE_KEY
Create a working directory
Create a directory that will hold the configuration, policies and extension code:
$ mkdir -p synapse-quickstart/policies synapse-quickstart/extensions
$ cd synapse-quickstart
The policies directory will hold the Cerbos policy files loaded by the embedded PDP. The extensions directory will hold the Starlark source for the proxy extension written later in this guide.
Write a policy
Create policies/invoice.yaml with a single resource policy that allows an employee to view an invoice when the invoice belongs to the same company as the principal:
apiVersion: api.cerbos.dev/v1
resourcePolicy:
resource: invoice
version: default
rules:
- actions:
- view
effect: EFFECT_ALLOW
roles:
- employee
condition:
match:
expr: request.resource.attr.company == request.principal.attr.company
The condition compares a company attribute on the resource to a company attribute on the principal. Neither attribute is built in to Cerbos: the caller is expected to send them as part of the request, or an extension must inject them. This guide takes the second approach.
Start Synapse
Create config.yaml with the minimum configuration needed to run Synapse with an in-process PDP that loads policies from disk:
server:
listenAddress: ":3594"
pdp:
inProcess:
storage:
driver: "disk"
disk:
directory: /policies
audit:
enabled: true
backend: file
file:
path: stdout
Start the container with the three bind mounts for the config, policies and extensions directories:
$ docker run --rm --name synapse-qs -p 3594:3594 \
-v $(pwd)/config.yaml:/config/config.yaml:ro \
-v $(pwd)/policies:/policies:ro \
-v $(pwd)/extensions:/extensions:ro \
CERBOS_DISTRIBUTION_REPO/synapse/synapse:latest \
server --conf.path=/config/config.yaml --log.level=debug
Make a check request
In a new terminal, from the synapse-quickstart directory, create check-request.json:
{
"requestId": "qs-001",
"principal": {
"id": "1",
"roles": ["employee"]
},
"resources": [
{
"actions": ["view"],
"resource": {
"kind": "invoice",
"id": "inv-42",
"attr": {
"company": "Romaguera-Crona"
}
}
},
{
"actions": ["view"],
"resource": {
"kind": "invoice",
"id": "inv-99",
"attr": {
"company": "Other Inc"
}
}
}
]
}
Send the request:
$ curl -s -X POST http://localhost:3594/api/check/resources \
-H 'Content-Type: application/json' \
-d @check-request.json
Response
{
"requestId": "qs-001",
"results": [
{ "resource": { "id": "inv-42", "kind": "invoice" }, "actions": { "view": "EFFECT_DENY" } },
{ "resource": { "id": "inv-99", "kind": "invoice" }, "actions": { "view": "EFFECT_DENY" } }
]
}
Both resources are denied. The policy condition references request.principal.attr.company, and the request did not supply it, so the comparison fails for every resource.
Add a proxy extension
Create extensions/enrich_principal.star:
load("http", "http")
def augment_check_request(req):
return _augment(req)
def augment_plan_request(req):
return _augment(req)
def _augment(req):
if not hasattr(req, "principal"):
return req
resp = http.get("https://jsonplaceholder.typicode.com/users/" + req.principal.id)
if resp.status_code != 200:
return req
user = resp.json()
req.principal.attr = {
"company": user["company"]["name"],
}
return req
Register the extension by replacing the contents of config.yaml with:
server:
listenAddress: ":3594"
pdp:
inProcess:
storage:
driver: "disk"
disk:
directory: /policies
audit:
enabled: true
backend: file
file:
path: stdout
extensions:
proxyExtensions:
enrichPrincipal:
extensionURL: /extensions/enrich_principal.star
required: true
Restart and verify
Stop the running container and start it again with the same docker run command as before. The extension is loaded at startup.
Send the same request again:
$ curl -s -X POST http://localhost:3594/api/check/resources \
-H 'Content-Type: application/json' \
-d @check-request.json
Response
{
"requestId": "qs-001",
"results": [
{ "resource": { "id": "inv-42", "kind": "invoice" }, "actions": { "view": "EFFECT_ALLOW" } },
{ "resource": { "id": "inv-99", "kind": "invoice" }, "actions": { "view": "EFFECT_DENY" } }
]
}
The decision for inv-42 has flipped to EFFECT_ALLOW...
Build a test suite to ensure repeatability
Manually restarting the container and sending requests via curl becomes cumbersome very quickly. Build test suites using the built-in testing framework to automate the process and codify the requirements of your extension.
Create a test suite for your extension by adding a new file named extensions/enrich_principal_test.star with the following contents.
test_suite = struct(
name = "Test principal enrichment",
synapse_config = testing.load_synapse_config("/config/config.yaml")
)
def test_enrichment(context):
request = struct(
requestId = "qs-001",
principal = struct(
id = "1",
roles = ["employee"]
),
resources = [
struct(
actions = ["view"],
resource = struct(
kind = "invoice",
id = "inv-42",
attr = {
"company": "Romaguera-Crona"
}
)
),
struct(
actions = ["view"],
resource = struct(
kind = "invoice",
id = "inv-99",
attr = {
"company": "Other Inc"
}
)
)
]
)
have = context.check_resources(request)
return testing.assert(have.results[0].actions["view"] == "EFFECT_ALLOW" and have.results[1].actions["view"] == "EFFECT_DENY")
Run the test suite.
$ docker run \
--rm --name synapse-test \
-v $(pwd)/config.yaml:/config/config.yaml:ro \
-v $(pwd)/policies:/policies:ro \
-v $(pwd)/extensions:/extensions:ro \
CERBOS_DISTRIBUTION_REPO/synapse/synapse:latest \
test /extensions
Adapt the extension to your own API
The extension above targets a public demonstration API. Retargeting it at an internal service is typically a URL change. Production integrations require additional handling:
- Authentication. Do not hardcode credentials in the Starlark source...
- Reachability. The HTTP call runs inside the Synapse container...
- TLS. If the internal API uses a private CA...
- Failure handling. Decide whether an extension outage should fail the authorization decision...
Next steps
- Proxy extensions covers the full configuration reference...
- Starlark documents every module available to extensions...
- WASM describes the alternative runtime...
- Build test suites for your extensions using the built-in testing framework.
- Configuration is the authoritative reference for the Synapse configuration file...