mirror of
https://github.com/CloudNebulaProject/barycenter.git
synced 2026-04-10 13:10:42 +00:00
206 lines
7.2 KiB
Markdown
206 lines
7.2 KiB
Markdown
|
|
# KDL Policy Language
|
||
|
|
|
||
|
|
Barycenter's authorization policies are written in [KDL](https://kdl.dev/) (KDL Document Language), a node-based configuration language that provides a clean, readable syntax for expressing access control rules. Policy files use the `.kdl` extension and are loaded from the configured `policies_dir` directory at startup.
|
||
|
|
|
||
|
|
## Why KDL?
|
||
|
|
|
||
|
|
KDL strikes a balance between the simplicity of TOML and the expressiveness of HCL. Its node-based structure maps naturally to the concepts in an authorization policy: resources have child nodes for relations and permissions, roles contain permission lists, and grants are single-line declarations with named attributes.
|
||
|
|
|
||
|
|
Key advantages for policy authoring:
|
||
|
|
|
||
|
|
- **Readable**: Node names (`resource`, `role`, `grant`, `rule`) read as natural-language declarations.
|
||
|
|
- **Structured**: Child blocks group related configuration without deeply nested braces.
|
||
|
|
- **Comments**: KDL supports `//` line comments and `/* */` block comments for documenting policy intent.
|
||
|
|
- **Familiar**: The syntax will feel natural to anyone who has worked with CSS, HCL, or similar formats.
|
||
|
|
|
||
|
|
## The Four Node Types
|
||
|
|
|
||
|
|
Every policy file is composed of four types of top-level nodes. Each node type serves a distinct role in the authorization model:
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
graph LR
|
||
|
|
R["resource\n(types & permissions)"] --> Role["role\n(permission groups)"]
|
||
|
|
Role --> G["grant\n(role assignments)"]
|
||
|
|
R --> Rule["rule\n(ABAC conditions)"]
|
||
|
|
|
||
|
|
style R fill:#e8f4f8,stroke:#2980b9
|
||
|
|
style Role fill:#eaf5ea,stroke:#27ae60
|
||
|
|
style G fill:#fdf2e9,stroke:#e67e22
|
||
|
|
style Rule fill:#f5eef8,stroke:#8e44ad
|
||
|
|
```
|
||
|
|
|
||
|
|
### resource
|
||
|
|
|
||
|
|
A `resource` node declares a type of object in your system, along with the relations and permissions that apply to it. Resources are the foundation of the policy model -- they define _what_ can be acted upon and _what actions_ exist.
|
||
|
|
|
||
|
|
```kdl
|
||
|
|
resource "document" {
|
||
|
|
relations {
|
||
|
|
- "owner"
|
||
|
|
- "editor"
|
||
|
|
- "viewer"
|
||
|
|
}
|
||
|
|
permissions {
|
||
|
|
- "read"
|
||
|
|
- "write"
|
||
|
|
- "delete"
|
||
|
|
- "share"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
See [Resources and Permissions](./resources-permissions.md) for full syntax and examples.
|
||
|
|
|
||
|
|
### role
|
||
|
|
|
||
|
|
A `role` node groups permissions together and optionally inherits from other roles. Roles use fully-qualified permission names in the format `type:permission` to reference the permissions declared on resources.
|
||
|
|
|
||
|
|
```kdl
|
||
|
|
role "document_viewer" {
|
||
|
|
permissions {
|
||
|
|
- "document:read"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
role "document_editor" {
|
||
|
|
includes {
|
||
|
|
- "document_viewer"
|
||
|
|
}
|
||
|
|
permissions {
|
||
|
|
- "document:write"
|
||
|
|
- "document:share"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
See [Roles and Inheritance](./roles-inheritance.md) for details on composition and inheritance chains.
|
||
|
|
|
||
|
|
### grant
|
||
|
|
|
||
|
|
A `grant` node creates a relationship tuple that assigns a role to a principal on a specific resource instance. Grants are the data layer of the authorization model -- they express _who_ has _what role_ on _which object_.
|
||
|
|
|
||
|
|
```kdl
|
||
|
|
grant "document_editor" on="document/quarterly-report" to="user/alice"
|
||
|
|
grant "document_viewer" on="document/quarterly-report" to="group/accounting#member"
|
||
|
|
```
|
||
|
|
|
||
|
|
See [Grants and Relationship Tuples](./grants-tuples.md) for the full reference syntax and tuple indexing.
|
||
|
|
|
||
|
|
### rule
|
||
|
|
|
||
|
|
A `rule` node defines an attribute-based policy with a condition expression. Rules can allow or deny access based on properties of the request context, such as the current time, IP address, or custom attributes.
|
||
|
|
|
||
|
|
```kdl
|
||
|
|
rule "RestrictEditingToBusinessHours" effect="allow" {
|
||
|
|
permissions {
|
||
|
|
- "document:write"
|
||
|
|
}
|
||
|
|
principals {
|
||
|
|
- "group:contractors"
|
||
|
|
}
|
||
|
|
condition "request.time.hour >= 9 && request.time.hour < 17"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
See [ABAC Rules and Conditions](./abac-rules.md) for the complete rule syntax and condition language.
|
||
|
|
|
||
|
|
## File Organization
|
||
|
|
|
||
|
|
All `.kdl` files in the `policies_dir` directory are loaded and merged at startup. There is no required file naming convention, but a common pattern is to organize by resource type or domain:
|
||
|
|
|
||
|
|
```
|
||
|
|
policies/
|
||
|
|
resources.kdl # resource type definitions
|
||
|
|
roles.kdl # role definitions with inheritance
|
||
|
|
grants-team-a.kdl # grants for team A
|
||
|
|
grants-team-b.kdl # grants for team B
|
||
|
|
rules.kdl # ABAC rules and conditions
|
||
|
|
```
|
||
|
|
|
||
|
|
You can also put everything in a single file, or split it however makes sense for your organization. The engine merges all files into a single `AuthzState` before building its indexes.
|
||
|
|
|
||
|
|
### Loading Order
|
||
|
|
|
||
|
|
Files are loaded in alphabetical order, but the order does not affect evaluation semantics. All nodes are collected and indexed together. However, there are dependencies between node types:
|
||
|
|
|
||
|
|
| Node Type | May Reference |
|
||
|
|
|-----------|--------------|
|
||
|
|
| `resource` | Nothing (standalone declarations) |
|
||
|
|
| `role` | Permissions from `resource` nodes, other `role` nodes via `includes` |
|
||
|
|
| `grant` | `role` names, resource types and IDs |
|
||
|
|
| `rule` | Permissions from `resource` nodes |
|
||
|
|
|
||
|
|
If a role references a permission that does not exist on any resource, or a grant references an undefined role, the engine will log a warning at startup. Malformed references do not prevent loading but will never match during evaluation.
|
||
|
|
|
||
|
|
## Immutability
|
||
|
|
|
||
|
|
Policies are immutable after loading. The `AuthzState` structure that holds all resources, roles, rules, and tuple indexes is built once during startup and shared as read-only state across all request handlers.
|
||
|
|
|
||
|
|
To change policies:
|
||
|
|
|
||
|
|
1. Edit the `.kdl` files in `policies_dir`.
|
||
|
|
2. Commit the changes to version control.
|
||
|
|
3. Restart (or reload) the Barycenter service.
|
||
|
|
|
||
|
|
This design ensures that policy evaluation is lock-free and that all authorization decisions during a given process lifetime are consistent. It also makes policy changes fully auditable through your version control system.
|
||
|
|
|
||
|
|
## Example: Complete Policy File
|
||
|
|
|
||
|
|
Here is a minimal but complete policy file that demonstrates all four node types working together:
|
||
|
|
|
||
|
|
```kdl
|
||
|
|
// Define a resource type for virtual machines
|
||
|
|
resource "vm" {
|
||
|
|
relations {
|
||
|
|
- "owner"
|
||
|
|
- "viewer"
|
||
|
|
}
|
||
|
|
permissions {
|
||
|
|
- "start"
|
||
|
|
- "stop"
|
||
|
|
- "view_console"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Define roles with inheritance
|
||
|
|
role "vm_viewer" {
|
||
|
|
permissions {
|
||
|
|
- "vm:view_console"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
role "vm_admin" {
|
||
|
|
includes {
|
||
|
|
- "vm_viewer"
|
||
|
|
}
|
||
|
|
permissions {
|
||
|
|
- "vm:start"
|
||
|
|
- "vm:stop"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Assign roles to users and groups
|
||
|
|
grant "vm_admin" on="vm/prod-web-1" to="user/alice"
|
||
|
|
grant "vm_viewer" on="vm/prod-web-1" to="group/sre#member"
|
||
|
|
|
||
|
|
// Restrict stop operations to business hours
|
||
|
|
rule "AllowStopDuringBusinessHoursOnly" effect="deny" {
|
||
|
|
permissions {
|
||
|
|
- "vm:stop"
|
||
|
|
}
|
||
|
|
principals {
|
||
|
|
- "*"
|
||
|
|
}
|
||
|
|
condition "request.time.hour < 6 || request.time.hour >= 22"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
With this policy loaded, the check request `{ principal: "user/alice", permission: "vm:start", resource: "vm/prod-web-1" }` would be allowed (Alice has `vm_admin` which includes `vm:start`), while `{ principal: "user/alice", permission: "vm:stop", resource: "vm/prod-web-1", context: { "request.time.hour": 23 } }` would be denied by the ABAC rule.
|
||
|
|
|
||
|
|
## Further Reading
|
||
|
|
|
||
|
|
- [Resources and Permissions](./resources-permissions.md)
|
||
|
|
- [Roles and Inheritance](./roles-inheritance.md)
|
||
|
|
- [Grants and Relationship Tuples](./grants-tuples.md)
|
||
|
|
- [ABAC Rules and Conditions](./abac-rules.md)
|