Authorization Guide
Sovra uses Open Policy Agent (OPA) for fine-grained authorization with customizable policies.
Overview
┌─────────────────────────────────────────────────────────────┐
│ Authorization Flow │
├─────────────────────────────────────────────────────────────┤
│ │
│ Authenticated Request │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Extract Context │ │
│ │ - User identity │ │
│ │ - Action │ │
│ │ - Resource │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ OPA Policy │◄── Rego policies │
│ │ Evaluation │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ │ │
│ ▼ ▼ │
│ Allow Deny │
│ │
└─────────────────────────────────────────────────────────────┘
Default Policy
Sovra includes a default RBAC policy that covers common access patterns:
Roles
| Role | Description | Permissions |
|---|---|---|
admin |
Organization administrator | Full access within org |
key_admin |
Key management admin | Create, rotate, revoke keys |
key_user |
Key consumer | Encrypt, decrypt, sign |
auditor |
Audit reader | Read audit logs |
federation_admin |
Federation manager | Manage federations |
Default Rules
# System accounts have full access
allow if input.user.type == "system"
# Admins can do anything within their org
allow if {
"admin" in input.user.roles
input.resource.org == input.user.org
}
# Users can read resources in their org
allow if {
input.action == "read"
input.resource.org == input.user.org
}
# Key operations require specific roles
allow if {
input.action in ["encrypt", "decrypt", "sign", "verify"]
"key_user" in input.user.roles
input.resource.org == input.user.org
}
allow if {
input.action in ["rotate", "revoke", "create"]
"key_admin" in input.user.roles
input.resource.org == input.user.org
}
# Auditors can read audit logs
allow if {
input.action == "read"
input.resource.type == "audit_event"
"auditor" in input.user.roles
input.resource.org == input.user.org
}
Custom Policies
You can extend or replace the default policy with custom Rego rules.
Policy Files
/etc/sovra/policies/
├── default.rego # Default RBAC rules
├── custom.rego # Your custom rules
└── data.json # Static data for policies
Configuration
authorization:
opa:
enabled: true
policy_path: /etc/sovra/policies/
decision_path: "authz/allow"
# Optional: External OPA server
# server_url: http://opa:8181
Example: Time-Based Access
package authz
import rego.v1
# Allow access only during business hours
allow if {
current_hour := time.clock(time.now_ns())[0]
current_hour >= 9
current_hour < 18
default_allow
}
default_allow if {
# ... existing rules
}
Example: IP-Based Restrictions
package authz
import rego.v1
# Allow only from trusted networks
allow if {
net.cidr_contains("10.0.0.0/8", input.request.ip)
default_allow
}
allow if {
net.cidr_contains("192.168.0.0/16", input.request.ip)
default_allow
}
Example: Attribute-Based Access
package authz
import rego.v1
# Users can only access workspaces with matching labels
allow if {
input.action == "read"
input.resource.type == "workspace"
# Check if user's team label matches workspace
user_team := input.user.attributes.team
user_team == input.resource.labels.team
}
Input Format
The authorization input has this structure:
{
"user": {
"id": "user-123",
"org": "org-456",
"type": "user",
"roles": ["key_user", "auditor"],
"attributes": {
"team": "security",
"department": "engineering"
}
},
"action": "encrypt",
"resource": {
"type": "key",
"id": "key-789",
"org": "org-456",
"workspace": "ws-abc",
"labels": {
"env": "production"
}
},
"request": {
"method": "POST",
"path": "/v1/keys/key-789/encrypt",
"timestamp": "2024-01-15T10:30:00Z"
}
}
Workspace Admission Tiers
In addition to OPA policies, Sovra enforces tiered admission control on encrypt/decrypt operations. The tier is determined by the workspace’s classification and CRK-protection status:
| Tier | Classification | Requirements |
|---|---|---|
| CONFIDENTIAL (auto) | CONFIDENTIAL | SSO group membership is sufficient |
| SECRET (explicit) | SECRET | Group membership AND explicit admission grant |
| CRK (explicit) | Any (CRK-protected) | Explicit admission grant only |
How It Works
Authorization for encrypt/decrypt is a three-stage check:
- Organization-level: The caller’s organization must be a workspace participant
- User-level tier check: Based on classification (see table above)
- OPA policy overlay: Optional workspace-specific OPA policies can further restrict access but never loosen tier rules
The Go-based tier logic is the security floor — it cannot be bypassed by OPA policies. OPA can only add further restrictions (time-based, role-based, etc.).
Managing Admissions
Admissions are managed via the API or CLI:
# Grant admission (required for SECRET and CRK-protected workspaces)
sovra workspace admission grant ws-123 \
--identity-id user-456 \
--identity-type user \
--org-id org-a
# List admissions
sovra workspace admission list ws-123
# Revoke admission
sovra workspace admission revoke ws-123 user-456
Admission Caching
Admission decisions are cached for 30 seconds (configurable) to reduce latency on the encrypt/decrypt hot path. The cache is automatically invalidated when:
- An admission is revoked
- A group member is added or removed via IdP sync
OPA Policy Integration
When a workspace has an OPA policy, the admission checker passes enriched input to the policy engine after the tier check passes:
{
"actor": "user-456",
"operation": "admit",
"workspace": "ws-123",
"metadata": {
"classification": "SECRET",
"crk_protected": false,
"admission_tier": "secret_explicit",
"org_id": "org-a",
"group_membership": true
}
}
This uses the sovra/workspace/{id}/allow decision path. Example Rego policy that adds time-based restriction on top of tier enforcement:
package sovra.workspace.ws_123
import rego.v1
# Only allow admission during business hours
allow if {
current_hour := time.clock(time.now_ns())[0]
current_hour >= 9
current_hour < 18
}
Example Policies
Example Rego policies for admission control are available in examples/policies/:
| File | Description |
|---|---|
admission-business-hours.rego |
Restrict workspace access to business hours |
admission-org-allowlist.rego |
Limit access to specific partner organizations |
admission-secret-dual-check.rego |
Combined hours + org + weekday check for SECRET workspaces |
admission-crk-audit-only.rego |
Template for CRK-protected workspace restrictions |
Upload an example policy:
sovra policy create \
--name admission-business-hours \
--workspace ws-123 \
--rego-file examples/policies/admission-business-hours.rego
Revocation Behavior
- CONFIDENTIAL: Removing a user from the SSO group blocks access on the next sync cycle
- SECRET: Removing a user from the SSO group blocks access even if the admission record exists (both checks must pass). The admission record persists for re-addition
- CRK-protected: Only explicit revocation via
workspace admission revokeremoves access
Federation Authorization
For federated access, additional checks apply:
# Allow federated access if org is a participant
allow if {
input.user.org != input.resource.org
input.user.org in data.federations[input.resource.workspace].participants
input.action in data.federations[input.resource.workspace].allowed_actions
}
Testing Policies
Use the Sovra CLI to test policies:
# Test a specific request
sovra policy test \
--user alice \
--action encrypt \
--resource key:key-123
# Run policy test suite
sovra policy test --suite tests/policies/
# Validate policy syntax
sovra policy validate /etc/sovra/policies/
Or use OPA directly:
# Evaluate policy
opa eval --input input.json --data policies/ "data.authz.allow"
# Run tests
opa test policies/ -v
Debugging
Enable authorization debugging:
authorization:
opa:
debug: true
log_decisions: true
View authorization decisions:
# Query recent decisions
sovra audit query \
--event-type authorization.decision \
--limit 10
# Filter denied requests
sovra audit query \
--event-type authorization.denied
Performance
The embedded OPA engine is optimized for low latency:
| Metric | Value |
|---|---|
| Policy compilation | Once at startup |
| Decision latency (p95) | < 1ms |
| Memory overhead | ~10MB for typical policies |
For complex policies or large datasets, consider using an external OPA server.
Integration with Policy Engine
Sovra’s Policy Engine service extends OPA with:
- Policy versioning - Track policy changes over time
- Policy bundles - Distribute policies to edge nodes
- Hot reload - Update policies without restart
- Audit logging - Record all policy decisions
Best Practices
- Principle of least privilege - Grant minimal permissions
- Test policies - Use OPA’s test framework
- Version policies - Store in Git, review changes
- Monitor denials - Alert on unexpected patterns
- Use attributes - For fine-grained control
- Cache decisions - For repeated identical requests