Imagine you’re managing a busy e-commerce platform with hundreds of APIs handling tasks like customer orders, and payment processing. One day, a small mistake in the settings accidentally gives a junior employee access to sensitive financial data. This leads to chaos! Now, think of a system that not only stops such errors but also ensures every API request is checked and authorized carefully.
This is where Kong and Cerbos step in. Kong manages API traffic smoothly, while Cerbos makes sure every request follows strict access rules. Together, they make API security simple and reliable, so you can focus on growing your business without worrying about security issues
With APIs powering many systems, having secure and scalable access control is important. It helps protect sensitive data and keeps systems strong and reliable. This article explores how to integrate Cerbos with Kong, a popular API gateway.
Kong is an open-source API gateway and microservices management platform designed to handle high-performance API traffic. As a reverse proxy, it efficiently routes requests, balances load, enforces security, and monitors traffic, helping in smooth API operations.
Kong supports a wide range of plugins to extend its functionality, such as rate limiting, authentication, and enhanced security features. Its architecture is built for scalability, making it a reliable choice for managing complex API infrastructures. Kong integrates effortlessly with modern systems, offering developers the tools needed for simple operations, improved performance, and secure management of APIs.
With its flexibility, Kong empowers organizations to efficiently manage and protect their APIs while supporting the needs of growing, dynamic environments.
Cerbos is a modern, open-source platform designed to simplify and enhance access control for applications. It provides developers with the ability to create, test, and deploy fine-grained authorization policies without embedding them directly into the application code. This separation of concerns allows for greater flexibility and ease of maintenance, enabling updates to policies without redeploying the application itself.
It supports dynamic changes, role-based access control, and advanced conditional rules. Cerbos ensures that access permissions remain secure and adaptable to evolving requirements. Its scalability and efficiency make it an ideal solution for organizations managing complex systems or large user bases.
By separating access control rules from the main application code, Cerbos makes development easier and faster. It also boosts security and ensures compliance, providing a flexible and strong solution for managing who can access what in modern software systems.
Combining Cerbos with Kong creates a strong and efficient solution for API security and access control. Kong is a powerful API gateway that handles tasks like routing traffic, checking user identities, limiting request rates, and balancing loads. This makes it a great foundation for managing APIs. Cerbos adds to this by providing detailed access control, allowing developers to create specific rules for who can access what based on the situation.
This integration offers several key benefits. First, it improves security by keeping all access rules in one place, ensuring they are applied consistently across all APIs. This reduces the risk of unauthorized access. Cerbos also separates access rules from the main application code, making it easier to manage policies and update them quickly without needing to redeploy the application.
Together, Kong and Cerbos provide a flexible, secure, and scalable solution, making them a great choice for modern systems that need reliable and efficient access control.
Below is the architecture demonstrating the integration:
The integration of Cerbos with Kong establishes a secure and efficient framework for managing API access control. This architecture helps streamline request processing while ensuring robust authorization.
Kong acts as the API gateway, managing incoming API requests. It handles essential tasks such as routing, authentication, rate limiting, and request validation. By serving as the entry point, Kong ensures that only authenticated requests are processed further.
Cerbos serves as the authorization engine, evaluating fine-grained policies for each request. Once Kong authenticates a request, it forwards relevant details, such as the user’s role, request context, and intended action, to Cerbos. Cerbos processes this information against predefined policies to determine whether access should be granted or denied.
Backend services are the systems responsible for processing authorized requests. Only requests that pass Cerbos’ authorization checks are routed to these services, ensuring secure resource access.
The integration of Cerbos with Kong follows a structured workflow to ensure secure and efficient API request handling. Each step in this process is designed to enforce robust authentication and authorization mechanisms while maintaining optimal performance.
Here is the detailed workflow:
Client Request: A client initiates an API call, sending a request to the Kong API gateway. This request contains information such as the resource being accessed and the intended action.
Initial Authentication: Kong validates the request by applying authentication mechanisms like JWT, API keys, or OAuth. This ensures the request originates from an authorized source before any further processing.
Data Forwarding to Cerbos: Once authenticated, Kong extracts relevant information from the request, such as user roles, resource details, and the intended action. This data is sent to Cerbos through its API for an authorization evaluation.
Authorization Decision by Cerbos: Cerbos processes the provided data against predefined fine-grained access policies. It determines whether the request aligns with the authorization rules and returns an explicit decision: allow or deny.
Final Processing by Kong: Based on Cerbos’ response, Kong either forwards the request to the appropriate backend service or blocks it. This ensures that only authorized requests proceed to the next stage.
Now, let's set up Kong first.
Step 1: Setting Up Kong with Docker
a. Run Kong in a Docker container
Kong offers a Docker image, making it easy to run in any environment. To start Kong quickly, run this command:
docker run -d --name kong \
-e "KONG_DATABASE=off" \
-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-p 8000:8000 \
-p 8001:8001 \
kong:latest
The 8000 and 8001 ports are for API traffic (Proxy) and for the Admin API (used to configure Kong), respectively.
Pro Tip: It is always good to use Docker Compose for Kong, as it simplifies the process of automating container management, making it easy to add services or plugins, and storing Kong’s configuration in a declarative way.
So, then let’s set up Kong with Docker Compose:
a. Create the Directory Structure
Start by creating a directory to organize your files:
kong-setup/
├── docker-compose.yml # Docker Compose configuration
└── kong.yml # Kong's declarative configuration
b. Write the Docker Compose File
The docker-compose.yml
defines the Kong service. Create this file with the following content:
version: '3.3'
services:
kong:
image: kong:latest
container_name: kong
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /usr/local/kong/declarative/kong.yml
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stderr
KONG_ADMIN_ERROR_LOG: /dev/stderr
volumes:
- ./kong.yml:/usr/local/kong/declarative/kong.yml
ports:
- "8000:8000" # Public Proxy
- "8001:8001" # Admin API
restart: unless-stopped
c. Write the Kong Configuration
The kong.yml
file defines Kong’s services and routes. Here’s an example configuration to route requests to a test service like httpbin:
_format_version: "3.0"
services:
- name: httpbin-service
url: http://httpbin.org
host: httpbin.org
routes:
- name: httpbin-route
paths:
- /httpbin
service: httpbin-service
This configuration creates a service (httpbin-service) pointing to https://httpbin.org and sets up a route (/httpbin) for clients to access the service.
Step 2: Running Kong with Docker Compose
Navigate to your project directory:
cd kong-setup
Start the Kong service:
docker-compose up -d
Verify that Kong is running:
docker ps
Step 3: Testing the Kong API Gateway
Now, send a request to the /httpbin route to see if Kong correctly forwards the request:
curl http://localhost:8000/httpbin/get
You’ll get a similar output:
Now, you’ve successfully tested Kong by routing requests to an external service like httpbin.
In this setup, Kong acts as the API gateway, routing client requests to backend services. Cerbos evaluates whether the request is authorized based on pre-defined policies. In this section set up both Kong and Cebos together. We do this using Docker Compose:
The directory structure should be:
Step 1: Write the Dockerfile
The Dockerfile builds a custom Kong container with the cerbos-auth plugin:
# Use Kong Gateway as the base image
FROM kong/kong-gateway:3.0
# Ensure Kong is running as root to install custom plugins
USER root
# Copy the custom plugin to the image
COPY ./kong/plugins /usr/local/share/lua/5.1/kong/plugins
# Enable the plugin in the environment
ENV KONG_PLUGINS=bundled,cerbos-auth
# Set Kong to run as the kong user
USER kong
# Expose ports for Kong Gateway
EXPOSE 8000 8443 8001 8444
Step 2: Write the Docker Compose File
The docker-compose.yml
orchestrates the Kong and Cerbos containers:
version: '3.3'
services:
kong:
build:
context: .
dockerfile: Dockerfile
container_name: kong
environment:
KONG_DATABASE: "off" # Use DB-less mode
KONG_DECLARATIVE_CONFIG: /usr/local/kong/declarative/kong.yml
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stderr
KONG_ADMIN_ERROR_LOG: /dev/stderr
KONG_ADMIN_LISTEN: "0.0.0.0:8001, 0.0.0.0:8444 ssl"
volumes:
- ./kong.yml:/usr/local/kong/declarative/kong.yml
ports:
- "8000:8000" # Public Proxy
- "8001:8001" # Admin API
restart: unless-stopped
cerbos:
image: cerbos/cerbos:latest
container_name: cerbos
ports:
- "3592:3592" # Cerbos API port
volumes:
- ./cerbos/policies:/policies
restart: unless-stopped
Step 3: Write the Kong Configuration
Define routes, services, and the custom plugin in kong.yml
:
_format_version: "3.0"
services:
- name: httpbin-service
url: http://httpbin.org
host: httpbin.org
routes:
- name: httpbin-route
paths:
- /httpbin
service: httpbin-service
plugins:
- name: cerbos-auth
config:
cerbos_url: http://cerbos:3592
Step 4: Create the Cerbos Policy
Write an access control policy in cerbos/policies/access_policy.yaml
:
apiVersion: "api.cerbos.dev/v1"
resourcePolicy:
version: "default"
resource: "example_resource"
rules:
- actions: ["read"]
effect: "EFFECT_ALLOW"
roles: ["user", "admin"] # Specify roles allowed to perform "read"
condition:
match:
expr: request.resource.attr.type == "public"
- actions: ["write"]
effect: "EFFECT_DENY"
roles: ["user", "admin"] # Specify roles for which "write" is explicitly denied
Step 5: Implement the Custom Plugin
Create the plugin handler in kong/plugins/cerbos-auth/handler.lua
:
local http = require "resty.http"
local cjson = require "cjson"
local CerbosAuthHandler = {
PRIORITY = 1000,
VERSION = "1.0.0"
}
-- HTTP to Cerbos action mapping
local http_to_cerbos_action = {
GET = "read",
POST = "create",
PUT = "update",
PATCH = "update",
DELETE = "delete"
}
function CerbosAuthHandler:access(conf)
-- Extract request details
local user = kong.request.get_header("X-User") or "anonymous"
local method = kong.request.get_method() -- HTTP method (e.g., GET, POST)
local path = kong.request.get_path() -- Requested path (e.g., /httpbin)
-- Map HTTP method to Cerbos action
local action = http_to_cerbos_action[method]
if not action then
kong.response.exit(400, { message = "Unsupported HTTP method: " .. method })
end
-- Construct payload for Cerbos
local payload = {
requestId = ngx.var.request_id or "default-request",
principal = {
id = user,
roles = { "user" }
},
resources = {
{
resource = {
kind = "example_resource",
id = path,
attr = {
type = "public"
}
},
actions = { action }
}
}
}
-- Make a request to Cerbos
local httpc = http.new()
local res, err = httpc:request_uri(conf.cerbos_url .. "/api/check/resources", {
method = "POST",
body = cjson.encode(payload),
headers = { ["Content-Type"] = "application/json" }
})
-- Handle connection errors
if not res then
kong.response.exit(500, { message = "Failed to connect to Cerbos", error = err })
end
-- Parse Cerbos response
local cerbos_response, decode_err = cjson.decode(res.body)
if not cerbos_response then
kong.response.exit(500, { message = "Failed to parse Cerbos response", error = decode_err })
end
-- Extract resource response
local resource_response = cerbos_response.results and cerbos_response.results[1]
if not resource_response then
kong.log.err("Invalid or missing Cerbos response results: ", res.body)
kong.response.exit(500, { message = "Invalid response from Cerbos" })
end
-- Check the action result
local action_result = resource_response.actions and resource_response.actions[action]
if not action_result or action_result ~= "EFFECT_ALLOW" then
kong.response.exit(403, { message = "Access Denied" })
end
-- Continue the request if authorized
kong.log.debug("Request authorized by Cerbos for user: ", user)
end
return CerbosAuthHandler
Also, include schema.lua for plugin validation in kong/plugins/cerbos-auth/schema.lua
:
local typedefs = require "kong.db.schema.typedefs"
return {
name = "cerbos-auth",
fields = {
{ config = {
type = "record",
fields = {
{ cerbos_url = typedefs.url({ required = true }) },
},
},
},
},
}
Step 6: Start the Services
Run the containers:
docker-compose build
docker-compose up -d
Run docker ps to check if containers are running or not:
Check resource policy in Cerbos:
curl -X POST http://localhost:3592/api/check/resources \
--header 'Content-Type: application/json' \
--data-raw '{
"requestId": "abc-123",
"principal": {
"id": "user-123",
"roles": ["user"]
},
"resources": [
{
"resource": {
"kind": "example_resource",
"id": "resource-1",
"attr": {
"type": "public"
}
},
"actions": ["read", "write"]
}
]
}'
You will get a response like this:
Step 7: Test the Integration
Now, let’s test the GET Request (Allowed) via
curl -X GET http://localhost:8000/httpbin/get -H "X-User: user-123"
This request is a GET method to /httpbin/get
with the header X-User: user-123
. The plugin maps GET to the read action and checks with Cerbos. Since the policy allows read actions for users with the user role on public resources, the request is authorized, and the response from httpbin is returned.
and POST Request (Denied) via
curl -X POST http://localhost:8000/httpbin/post -H "X-User: user-123"
This request is a POST method to /httpbin/post
with the header X-User: user-123
. The plugin maps POST to the create action and checks with Cerbos. The policy explicitly denies write actions (which includes create) for users with the user role, resulting in a 403 Access Denied response.
Through this setup, we've combined Kong's API management capabilities with Cerbos's policy-driven authorization. The custom plugin ensures that each API request is evaluated against defined policies, allowing or denying access based on user roles and resource attributes. This approach provides a scalable and secure method to manage API access control.
Here are some best practices that you can keep in mind while integrating Cerbos with Kong:
When setting up Kong and Cerbos, start by designing clear and specific authorization policies. Create rules that cover all scenarios, like allowing users to edit only their own data or restricting access to sensitive APIs. Test these policies using Cerbos’ built-in tools (like the Cerbos Playground) to ensure they work as intended before deploying them.
To handle high traffic and ensure smooth performance, enable caching in Kong using plugins like the Proxy Cache to store authorization results and reduce repeated checks to Cerbos. Also, turn on Cerbos’ decision caching to speed up repeated requests. Monitor both systems closely by enabling logging in Kong (with plugins like File Log or HTTP Log) and audit logging in Cerbos to track authorization decisions.
Automate policy updates by storing them in version control (like Git) and using CI/CD pipelines to deploy changes safely. Always secure communication between Kong and Cerbos with HTTPS/TLS to protect sensitive data, and rotate API keys or tokens regularly. For scalability, run multiple Cerbos instances behind a load balancer and keep Kong lightweight by offloading authorization checks to Cerbos. This approach keeps your API security strong, scalable, and easy to maintain over time.
Integrating Cerbos into your application enhances security by centralizing and simplifying access control. It allows you to define and manage permissions separately from your core application code, making it easier to maintain and update access policies without altering your application's logic. Cerbos supports various access control models, including Role-Based Access Control (RBAC), Attribute-Based Access Control (ABAC), and Policy-Based Access Control (PBAC) providing flexibility to meet diverse authorization needs. Its open-source nature and active community support ensure continuous improvement and adaptability to evolving security requirements. Join the Cerbos Slack Community to ask any questions and star the Cerbos Github repository for resources and examples.
Book a free Policy Workshop to discuss your requirements and get your first policy written by the Cerbos team
Join thousands of developers | Features and updates | 1x per month | No spam, just goodies.