Skip to content

Writing OPA Policies

jitsudo uses Open Policy Agent (OPA) embedded as a Go library to evaluate access decisions. Policies are written in Rego and stored in the jitsudo database.

TypeWhen evaluatedControls
eligibilityAt request submission”Is this user allowed to request this role/scope?”
approvalAt request review”Who must approve? Can it be auto-approved?”
Terminal window
# Apply a policy (create or update by name)
jitsudo policy apply -f sre-eligibility.rego --type eligibility
# List all policies
jitsudo policy list
# Test a policy without making changes
jitsudo policy eval \
--type eligibility \
--input '{"user":{"email":"[email protected]","groups":["sre"]},"request":{"provider":"aws","role":"prod-admin","resource_scope":"123456789012","duration_seconds":3600}}'
# Delete a policy
jitsudo policy delete pol_01J8KZ...

All policies receive the same input document:

{
"user": {
"email": "[email protected]",
"groups": ["sre", "oncall"]
},
"request": {
"provider": "aws",
"role": "prod-infra-admin",
"resource_scope": "123456789012",
"duration_seconds": 3600,
"reason": "Investigating P1 ECS crash",
"break_glass": false,
"metadata": {}
}
}
FieldTypeDescription
user.emailstringThe requester’s email (IdP subject claim)
user.groupsstring[]Groups from the groups claim in the OIDC token
request.providerstringCloud provider: aws, azure, gcp, kubernetes
request.rolestringThe requested role name
request.resource_scopestringProvider-specific scope (account ID, project ID, etc.)
request.duration_secondsnumberRequested elevation duration in seconds
request.reasonstringJustification text from the requester
request.break_glassbooleanWhether break-glass mode was requested
request.metadataobjectProvider-specific additional parameters
package jitsudo.eligibility
# allowed: bool — whether the request is allowed
# reason: string — explanation (shown on denial)
default allow = false
default reason = "not authorized"

The policy engine evaluates allow and reason from the jitsudo.eligibility package.

package jitsudo.eligibility
default allow = false
default reason = "user must be in the sre group"
allow {
input.user.groups[_] == "sre"
}
reason = "access allowed for SRE team" {
allow
}
package jitsudo.eligibility
default allow = false
default reason = "not authorized for this provider/role combination"
# SRE can request any AWS role
allow {
input.user.groups[_] == "sre"
input.request.provider == "aws"
}
# Developers can only request read-only GCP access
allow {
input.user.groups[_] == "developer"
input.request.provider == "gcp"
input.request.role == "roles/viewer"
}
# Ops can request Kubernetes view in non-production namespaces
allow {
input.user.groups[_] == "ops"
input.request.provider == "kubernetes"
input.request.role == "view"
not startswith(input.request.resource_scope, "prod")
}
package jitsudo.eligibility
default allow = false
default reason = "not authorized"
allow {
input.user.groups[_] == "sre"
# SRE can request up to 4 hours
input.request.duration_seconds <= 14400
}
allow {
input.user.groups[_] == "sre-lead"
# SRE leads can request up to 12 hours
input.request.duration_seconds <= 43200
}
package jitsudo.eligibility
default allow = false
default reason = "not authorized"
# Only oncall users can use break-glass
allow {
not input.request.break_glass
input.user.groups[_] == "sre"
}
allow {
input.request.break_glass
input.user.groups[_] == "oncall"
}
package jitsudo.approval
# allowed: bool — whether this request can proceed to approval
# reason: string — explanation
default allow = false

Example: Require SRE Lead Approval for Production

Section titled “Example: Require SRE Lead Approval for Production”
package jitsudo.approval
default allow = false
default reason = "requires SRE lead approval"
# SRE leads can approve production AWS requests
allow {
input.user.groups[_] == "sre-lead"
input.request.provider == "aws"
}
# Anyone can approve non-production requests
allow {
not endswith(input.request.resource_scope, "prod")
}
package jitsudo.approval
import future.keywords.in
default allow = false
default reason = "requests outside business hours require manager approval"
# Standard requests: allowed during business hours (Mon-Fri, 09:00-18:00 UTC)
allow {
day := time.weekday(time.now_ns())
day in [1, 2, 3, 4, 5] # Monday through Friday
hour := time.clock(time.now_ns())[0]
hour >= 9
hour < 18
}
# Break-glass: always allowed (emergency)
allow {
input.request.break_glass
}

Use jitsudo policy eval to test without applying:

Terminal window
# Test eligibility
jitsudo policy eval \
--type eligibility \
--input '{
"user": {"email": "[email protected]", "groups": ["sre"]},
"request": {
"provider": "aws",
"role": "prod-infra-admin",
"resource_scope": "123456789012",
"duration_seconds": 7200
}
}'
# Expected output:
# allowed: true
Terminal window
# Test a denial case
jitsudo policy eval \
--type eligibility \
--input '{
"user": {"email": "[email protected]", "groups": ["developer"]},
"request": {
"provider": "aws",
"role": "prod-infra-admin",
"resource_scope": "123456789012",
"duration_seconds": 7200
}
}'
# Expected output:
# allowed: false
# reason: user must be in the sre group
  • Multiple eligibility policies: All enabled eligibility policies are evaluated. A request is allowed if any policy returns allow = true.
  • Multiple approval policies: All enabled approval policies are evaluated. The same “any” semantics apply.
  • Disabled policies: Policies with enabled: false are not loaded into the OPA engine and have no effect.

After applying or deleting policies, the OPA engine reloads automatically on a periodic schedule. To reload immediately:

Terminal window
jitsudo server reload-policies

All eligibility policies must use the package jitsudo.eligibility. All approval policies must use the package jitsudo.approval.

Any Rego that is valid within these packages is supported, including imports, helper rules, and future.keywords.