- Add PUT /v1/policy/rule endpoint for updating policy rules; expose full policy CRUD through the web UI with a dedicated policy page - Add certificate revoke, delete, and get-cert to CA engine and wire REST + gRPC routes; fix missing interceptor registrations - Update ARCHITECTURE.md to reflect v2 gRPC as the active implementation, document ACME endpoints, correct CA permission levels, and add policy/cert management route tables - Add POLICY.md documenting the priority-based ACL engine design - Add web/templates/policy.html for policy management UI Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.2 KiB
Metacrypt Policy Engine
Metacrypt includes a priority-based access control policy engine that governs which authenticated users may perform which operations on which engine resources. This document explains the policy model, rule structure, evaluation algorithm, and how to manage rules via the API and web UI.
Overview
Every request to POST /v1/engine/request is evaluated against the policy
engine before being dispatched to the underlying cryptographic engine. The
policy engine enforces a default-deny posture: unless a matching allow rule
exists, non-admin users are denied.
Admin bypass: Users with the admin role in MCIAS always pass policy
evaluation unconditionally. Policy rules only affect non-admin users.
Rule Structure
A policy rule is a JSON object with the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Unique identifier for the rule |
priority |
integer | Yes | Evaluation order; lower number = higher priority |
effect |
"allow" or "deny" |
Yes | Decision when this rule matches |
usernames |
[]string | No | Match specific usernames (case-insensitive). Empty = any |
roles |
[]string | No | Match any of these roles (case-insensitive). Empty = any |
resources |
[]string | No | Glob patterns for the resource path. Empty = any |
actions |
[]string | No | "read" or "write". Empty = any |
Resource Paths
Resources follow the pattern engine/<mount>/<operation>. For example:
engine/pki/list-certs— list certificates on thepkimountengine/pki/issue— issue a certificate on thepkimountengine/transit/*— any operation on thetransitmount
Glob patterns use standard filepath matching (* matches within a path
segment; ** is not supported — use engine/pki/* to match all operations on
a mount).
Actions
Operations are classified as either read or write:
| Action | Operations |
|---|---|
read |
list-issuers, list-certs, get-cert, get-root, get-chain, get-issuer |
write |
All other operations (issue, renew, sign-csr, create-issuer, delete-issuer, etc.) |
Evaluation Algorithm
- If the caller has the
adminrole → allow immediately (bypass all rules). - Load all rules from the barrier.
- Sort rules by
priorityascending (lower number = evaluated first). - Iterate rules in order; for each rule, check:
- If
usernamesis non-empty, the caller's username must match one entry. - If
rolesis non-empty, the caller must have at least one matching role. - If
resourcesis non-empty, the resource must match at least one glob pattern. - If
actionsis non-empty, the action must match one entry. - All specified conditions must match (AND logic within a rule).
- If
- The first matching rule wins; return its
effect. - If no rule matches → deny (default deny).
Managing Rules via the REST API
All policy endpoints require an authenticated admin token
(Authorization: Bearer <token>).
List Rules
GET /v1/policy/rules
Returns a JSON array of all policy rules.
Create a Rule
POST /v1/policy/rules
Content-Type: application/json
{
"id": "allow-users-read-pki",
"priority": 10,
"effect": "allow",
"roles": ["user"],
"resources": ["engine/pki/*"],
"actions": ["read"]
}
Returns 201 Created with the created rule on success.
Get a Rule
GET /v1/policy/rule?id=allow-users-read-pki
Update a Rule
PUT /v1/policy/rule?id=allow-users-read-pki
Content-Type: application/json
{ ... updated rule fields ... }
Delete a Rule
DELETE /v1/policy/rule?id=allow-users-read-pki
Managing Rules via the Web UI
Navigate to Policy in the top navigation bar (visible to admin users only).
The policy page shows:
- A table of all active rules with their ID, priority, effect, and match conditions.
- A Create Rule form (expandable) for adding new rules.
- A Delete button on each row to remove a rule.
Managing Rules via gRPC
The PolicyService gRPC service (defined in proto/metacrypt/v2/policy.proto)
provides equivalent operations:
| RPC | Description |
|---|---|
CreatePolicy |
Create a new rule |
ListPolicies |
List all rules |
GetPolicy |
Get a rule by ID |
DeletePolicy |
Delete a rule by ID |
Common Patterns
Allow users to read PKI resources
{
"id": "allow-users-read-pki",
"priority": 10,
"effect": "allow",
"roles": ["user"],
"resources": ["engine/pki/*"],
"actions": ["read"]
}
Allow a specific user to issue certificates
{
"id": "allow-alice-issue",
"priority": 5,
"effect": "allow",
"usernames": ["alice"],
"resources": ["engine/pki/issue"],
"actions": ["write"]
}
Deny guests all access to a mount
{
"id": "deny-guests-transit",
"priority": 1,
"effect": "deny",
"roles": ["guest"],
"resources": ["engine/transit/*"]
}
Allow users read-only access to all mounts
{
"id": "allow-users-read-all",
"priority": 50,
"effect": "allow",
"roles": ["user"],
"actions": ["read"]
}
Role Summary
| Role | Default access |
|---|---|
admin |
Full access to everything (policy bypass) |
user |
Denied by default; grant access via policy rules |
guest |
Denied by default; grant access via policy rules |
Storage
Policy rules are stored in the encrypted barrier under the prefix
policy/rules/<id>. They are encrypted at rest with the master encryption key
and are only accessible when the service is unsealed.