Prerequisites
- Understanding of the Rego syntax
Create Policy
The video below shows how to create a Rego policy and run it in test mode.
-
In your workspace, go to Governance > Policies from the sidebar.
-
In the Policies view, select CREATE POLICY.
-
In the Create Policy popup window
-
Select the Policy type from the dropdown menu.
-
Specify the Policy name.
-
Select REGO as the Policy language.
-
Select Create.
Result
The OPA policy editor opens.
-
-
To define your policy, enter the policy code in the OPA language into the editor. Check the below request templates for help - they are also available in the right-hand policy menu.
When ready, Save your policy. You can now assign it to a valid execution point (see the Policy Types table above).
Cloudentity Request Schema
The following schema is valid for all requests coming from Cloudentity. For that reason, it is also used in the policy test mode. Note the three top-level objects:
authn_ctx
contains the authentication context claims, including scopes.contexts
contains dynamic scopes incontexts.scopes.users.*
.request
contains data specific to the HTTP request itself.
Cloudentity would typically send input resembling the one below to the policy engine:
{
"authn_ctx": {
"scp": [
"scope_name"
],
"sub": "joe",
"groups": [
"group_name"
],
"email": "testjoe@cloudentity.com",
"email_verified": "testjoe@cloudentity.com",
"phone_number": "+1-555-6616-899",
"phone_number_verified": "+1-555-6616-899",
"address": {
"formatted": "",
"street_address": "1463 Perry Street",
"locality": "Dayton",
"region": "Kentucky",
"country": "US",
"postal_code": "41074"
},
"name": "Joe Test",
"given_name": "Joe",
"middle_name": "",
"family_name": "Test",
"nickname": "joe",
"preferred_username": "testjoe",
"profile": "",
"picture": "",
"website": "",
"gender": "male",
"birthdate": "1960-10-09",
"zoneinfo": "",
"locale": "",
"updated_at": ""
},
"contexts": {
"scopes": {
"users.*": [
{
"params": [
"joe"
],
"requested_name": "users.joe"
}
]
}
},
"request": {
"headers": {
"Content-Type": [
"application/json"
],
"X-Custom-Header": [
"BOT_DETECTED"
]
},
"method": "POST",
"path_params": {
"users": "admins"
},
"query_params": {
"limit": [
"1000"
],
"offset": [
"100"
]
},
"path": "/doawesomethings"
}
}
Your policies can verify all data passed in the above schema and validate requests based on it. Check the policy tips below and start writing!
Scope Check Policy
To write a policy checking for a scope in the request, you can use the following template:
package acp.authz
default allow = false
scope := "sample_service:write"
allow {
input.authn_ctx.scp[_] == scope
}
This policy validates the request when the required scope ("sample_service:write"
) is found in the
authentication context (input.authn_ctx.scp[_]
).
Dynamic Scope Check Policy
To write a policy checking for a dynamic scope in the request, you can use the following template:
package acp.authz
default allow = false
allow {
input.scopes["users.*"][_].params[0] == input.authn_ctx.sub
}
This policy validates the request when the required value is found in the input.scopes
object,
where dynamic
scopes are stored.
HTTP Request Check Policy
To write a policy checking the HTTP request parameters, you can use the following template:
package acp.authz
default allow = false
allow {
input.request.method == "POST"
input.request.headers["X-Custom-Header"][_] == "REGULAR_USER"
}
This policy validates the request only for a POST request containing a specific header.
HTTP Header Names Format
REGO policies by their definition are case-sensitive when matching HTTP header names, but Cloudentity authorizers follow the RFC-2616 specification which states that header names are case-insensitive. To allow authorizers to correctly validate REGO policies, header names are normalized to follow the canonical format.
Canonicalization converts the first letter and any letter following the hyphen to upper case and the rest of the letters are converted to lower case.
It means that if a request is to be validated and contains a header like, for example,
x-custom-header
, before the header is validated, the header is converted to follow the canonical formatX-Custom-Header
.As the policy check is case sensitive for REGO policies, your REGO policy that checks request headers must have the header in the canonical format as you can see in the HTTP request check policy example above.
MFA Enforcement Policy
To write a policy checking the MFA validation status of the user, you can use the following template:
package acp.authz
default allow = false
allow {
input.login.verified_recovery_methods[_] = "mfa"
}
recovery = ["mfa"]
This policy validates the request only if the user has completed MFA. Otherwise, the user is prompted for an OTP code in accordance with the tenant’s MFA setup.
HTTP Call Status Check Policy
To write a policy executing an HTTP call and checking the status, you can use the following template:
package acp.authz
default allow = false
allow {
response := http.send({
"method" : "GET",
"url": "https://cloudentity.com/developers/"
})
response.status_code == 200
}
This policy validates the request only if the request returns a given status (200
in the above policy).
You have the option to cache the results of this HTTP request, to improve performance,
by setting the optional parameters force_cache
and force_cache_duration_seconds
:
response := http.send({
"force_cache": true,
"force_cache_duration_seconds": 30,
"method" : "GET",
"url": "https://cloudentity.com/developers/"
})
Cloudentity authorizers provide an inter-query cache that persists across policy evaluations,
which enables calls to http.send()
to access cached responses from previous policy checks.
The size of this cache can be set in the authorizer’s configuration file:
enforcement:
rego_inter_query_cache_size: 1000000 # maximum size for the Rego inter-query builtin cache
For more information about http.send()
, refer to openpolicyagent.org/docs.
Group Membership Check Policy
To write a policy checking for user’s group membership, you can use the following template:
package acp.authz
default allow = false
group := "admins"
allow {
input.authn_ctx.groups[_] == group
}
This policy validates the request only if the admins
value is found in the authn_ctx.group
object inside the
authentication context (i.e. the user is an admin).
Secret Check Policy
You can retrieve a secret value for comparison via Rego policy. The below policy compares the
secret value from SECRET_NAME
against
the name
parameter passed in the authentication context:
package acp.authz
default allow = false
allow {
input.secrets.SECRET_NAME == input.authn_ctx.name
}
This policy validates the request only if the value of a secret called SECRET_NAME
matches the
value of the name
attribute from the authentication context.
Header Injection For Istio Policies
Note
The technique described here works for the Istio authorizer only.
When a policy for the Istio authorizer is resolved, all globally defined policy variables are injected as headers. Such a policy can only be assigned to APIs behind the Istio gateway bound to Cloudentity, therefore it must always have the API request type. Considering we have the following policy:
package acp.authz
default allow = false
subject := input.authn_ctx.sub
expiration := input.authn_ctx.exp
issuer := input.authn_ctx.iss
scopes := input.authn_ctx.scp
tenantid := input.authn_ctx.tid
allow {
true
}
Upon policy validation, the authentication context values defined as global variables (outside of
the allow
document) are extracted and injected as headers in
the request received by the target service (the values below are encoded):
X-Output-Issuer: Imh0dHBzOi8vYWNwLmFjcC1zeXN0ZW06ODQ0My9kZWZhdWx0L2RlZmF1bHQi
X-Output-Expiration: MTYzNTk2OTQ2OA==
X-Output-Tenantid: ImRlZmF1bHQi
X-Output-Scopes: WyJlbWFpbCIsImludHJvc3BlY3RfdG9rZW5zIiwibGlzdF9jbGllbnRzX3dpdGhfYWNjZXNzIiwibWFuYWdlX2NvbnNlbnRzIiwib2ZmbGluZV9hY2Nlc3MiLCJvcGVuaWQiLCJwcm9maWxlIiwicmV2b2tlX2NsaWVudF9hY2Nlc3MiLCJyZXZva2VfdG9rZW5zIiwidmlld19jb25zZW50cyJd
X-Output-Subject: InVzZXIi
The Istio sidecar configuration and the default Cloudentity headers (X-Output-Allow
,
X-Auth-Ctx
) are injected as well.
Embedded Policies
For REGO policies that are embedded within a Cloudentity policy, if the output contains the same keys, it is merged and the keys are overwritten. The key is set to the key of the last resolved REGO policy.
For example, in your Cloudentity policy there are two embedded REGO policies, A and B. The policy A has headers X and Y, and the B policy has headers Y and Z. The Y header is common for both policies. It’s value is set to the value of Y header of the B policy as B is the last REGO policy embedded in the Cloudentity policy. Both the X and the Z headers remain the same.
Policy With Inter-Query Cache
Fetching data from external systems can impact the system’s performance because every single call to a protected API includes a new HTTP request. So when your policy requires fetching additional data, you can apply response caching to avoid this impact.
REGO policies that authorize access to APIs via the MicroPerimeter Authorizers can take advantage of the Open Policy Agent (OPA) inter-query cache for HTTP responses. For example, the following REGO policy enables the caching features of the GET HTTP calls and sets the cache response freshness duration to 30 seconds:
package acp.authz
default allow = false
allow {
response := http.send({
"force_cache": true,
"force_cache_duration_seconds": 30,
"method" : "GET",
"url": "https://www.google.com/"
})
response.status_code == 200
}
For full information on the parameters used, check Open Policy Agent documentation.
If such a policy is used, the authorizer’s configuration must include the cache size limit. You can
override the default limit by configuring the authorizer’s rego_inter_query_cache_size
parameter.
enforcement:
rego_inter_query_cache_size: 1000000
Related Articles
Having defined a policy, it’s time to assign it to an execution point and test it. Check the following resources for help:
-
Configuring scopes for your new policy
-
Applying your policy to APIs (see Apply a sample policy in Protecting APIs with MicroPerimeter™ Custom Authorizer on AWS API Gateway.