Sovra Sovra

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:

  1. Organization-level: The caller’s organization must be a workspace participant
  2. User-level tier check: Based on classification (see table above)
  3. 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:

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


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:

  1. Policy versioning - Track policy changes over time
  2. Policy bundles - Distribute policies to edge nodes
  3. Hot reload - Update policies without restart
  4. Audit logging - Record all policy decisions

Best Practices

  1. Principle of least privilege - Grant minimal permissions
  2. Test policies - Use OPA’s test framework
  3. Version policies - Store in Git, review changes
  4. Monitor denials - Alert on unexpected patterns
  5. Use attributes - For fine-grained control
  6. Cache decisions - For repeated identical requests