Policy Creation
Last modified on June 20, 2024
This feature is currently in closed-access beta. Functionality and documentation may change. Contact StrongDM for more information.
This feature is part of the Enterprise bundle. If it is not enabled for your organization, please contact StrongDM at the StrongDM Help Center.
Overview
A policy consists of policy statements that enforce fine-grained access control for the users of an organization. Each policy statement declares what is forbidden or permitted for users to do with particular resources. The Policy Editor is where admins can build custom policy statements and save them to a policy.
Policies are managed in the Admin UI in Access > Policies > Policy Library. The Policy Library contains a listing and brief description of each policy that has been created in your organization. Clicking either the name or the Details button of any policy opens the policy for editing in the Policy Editor tab.
The Policy Editor allows you to build policy statements by either setting contextual controls to construct a policy statement in the Cedar policy language, or by typing policy statements in Cedar syntax directly into the editing area. When policy statements are saved to a policy, the policy enforces them immediately.
This article describes how to use the Policy Editor to build policy statements in Cedar syntax, modify them, and save them to a policy. By the end of this article, you will have learned how policy statements are constructed, enabling you to build and save your own policies to suit your organization.
For more information about policies in general, please see the Policies section.
Policy Editor Usage
The Policy Editor consists of an editing area and a set of builder controls. The editing area displays policy statements saved to a policy. You may type policy statements in Cedar syntax manually, or you may use the builder controls to create policy statements and inject them into the policy.
Writing policy statements manually requires familiarity with the Cedar language. If you are unfamiliar with Cedar, we recommend using the Policy Editor’s builder controls to learn how policy statements are structured.
The builder controls make it easy to turn your selected principals, resources, actions, and other contextual elements into custom policy statements in Cedar syntax. You can use the Inject policy statement button to add such statements to a policy and then view them in the editing area.
Builder controls
Builder controls help you to define all the conditions and requirements that must be satisfied in order for a policy statement to evaluate to true and either permit or forbid access.
These controls are:
- Access: Designates the policy statement as either a Forbid or Permit statement
- Who: Specifies the principal(s), which may be one or more users or roles, that are the subject of the statement
- What: Specifies resource(s) and/or actions taken on the resource that are the subject of the statement
- When: Specifies contextual elements, such as the location of the user, the user’s device trust status, or the IP address of the user or resource being accessed
- With: Adds other options that must be used (such as multi-factor authentication (MFA), justification for access, or workflow approval)
All these elements form the context of user access that a policy can enforce.
Policy Statement Structure
A policy statement may be either a Permit statement or a Forbid statement. Permit statements allow specific principals (which are users or roles) to perform specific actions on resources. In contrast, Forbid statements stop principals from performing specific actions on specific resources.
Permit statements
Permit statements allow user actions to be completed if all of the following are true:
- The user’s username or role matches the principal set in the policy statement.
- The resource the user is interacting with matches the resource set in the policy statement.
- Any contextual elements set in the policy statement match the context of the user.
- The user completes any further requirements that are configured in the policy statement.
Forbid statements
Forbid statements stop user actions if all of the following are true:
- The user’s username or role matches the principal set in the policy statement.
- The resource the user is interacting with matches the resource set in the policy statement.
- Any contextual elements set in the policy statement match the context of the user.
Statement structure
Every policy statement follows the same general structure, where the policy statement:
- Begins with the “access” type (
permit
orforbid
) - Includes
principal
for defining “who” is permitted or denied access - Includes
action
for specifying “what” user actions may or may not be performed in StrongDM or on a resource - Includes
resource
for specifying “what” resources the user may or may not access - Ends with a semicolon (
;
)
permit (
principal,
action,
resource
);
// or
forbid (
principal,
action,
resource
);
Principal
The principal
in a policy statement indicates specifically who is permitted or forbidden access. The principal may be a user (person or service account) or a role in the organization.
The principal is set as either the user ID or role ID in the format principal == StrongDM::Account::"<USER_ID>"
or principal == StrongDM::Role::"<ROLE_ID>"
.
In the example shown, user Alice Glick with user ID a-70254fee63ea6fbe
is permitted to take all actions on all resources.
permit (
principal == StrongDM::Account::"a-70254fee63ea6fbe",
action,
resource
);
Similarly, in the following example, only users assigned the role called Marketing (which has role ID r-1caa595464152e78
) are forbidden access:
permit (
principal in StrongDM::Role::"r-1caa595464152e78",
action,
resource
);
When no principal is specified, the policy statement applies to all principals. In the following example Permit statement, access is permitted for all users and roles.
permit (
principal,
action,
resource
);
Action
The action
in a policy statement indicates the specific action, including StrongDM actions (such as connecting to a resource) and database actions (such as Create, Read, Update, and Delete) that a user is permitted or forbidden to perform on the specified resource(s).
For more information about PostgreSQL actions, please see Context-Based Policy.
How to specify actions
StrongDM actions
In a policy statement, StrongDM actions are set in the format StrongDM::Action::"<ACTION>"
, where <ACTION>
is the name of the action (for example, StrongDM::Action::"connect"
).
When an action is for a particular resource, the resource
in the policy statement also must be set.
In the Policy Editor, if you are typing into the editing area directly, enter the action (and resource) in the following way.
forbid (
principal == StrongDM::Account::"a-3c931809650c7041",
action == StrongDM::Action::"connect",
resource == StrongDM::Resource::"rs-123d456789e12e34"
);
StrongDM::Action::"connect"
action, the specified resource is always a StrongDM::Resource
.Actions for PostgreSQL resources
In a policy statement, for a PostgreSQL resource, the action is set in the format SQL::Action::"<ACTION_NAME>"
, where SQL
is the resource type and <ACTION_NAME>
is the action itself (for example, SQL::Action::"alterAggregateFunction"
).
actionName
, where the first word is set in lowercase letters, the second word (if any) is initial-capped, and there is no space between words if there are multiple words (for example, ALTER ROLE
must be formatted like alterRole
). Single-word action names are always set in lowercase letters (for example, connect
).When an action is for a PostgreSQL resource, the resource
in the policy statement also must be set in one of the following ways:
resource == Postgres::Database::"<RESOURCE_ID>/database-name"
resource in StrongDM::Resource::"<RESOURCE_ID>"
This particular example shows that the user is forbidden from altering databases for the specified Postgres resource.
forbid (
principal == StrongDM::Account::"a-3c931809650c7041",
action == SQL::Action::"select",
resource == Postgres::Database::"rs-521d183862e19e48/database-name"
);
// or
forbid (
principal == StrongDM::Account::"a-3c931809650c7041",
action == SQL::Action::"update",
resource in StrongDM::Resource::"rs-521d183862e19e48"
);
When no action is specified, the policy statement applies to all actions. In the following example, the user is allowed to perform all actions on the specified Postgres resource.
permit (
principal == StrongDM::Account::"a-3c931809650c7041",
action,
resource == StrongDM::Resource::"rs-521d183862e19e48"
);
Resource
The resource
indicated in a policy statement indicates the specific resource that a user is permitted or forbidden to access.
In a policy statement, a resource is set in the format resource == StrongDM::Resource::"<RESOURCE_ID>"
, as in the following example.
permit (
principal,
action,
resource in StrongDM::Resource::"rs-28acd881623b7f19"
);
principal
, action
, or resource
, it means that the policy applies to all principals, all actions, and all resources.Context
Context is set as a when
condition in the policy statement. The when
condition specifies any additional constraints, along with any other attributes of the principals, actions, and resources, that the policy must evaluate in order to permit or forbid the user action.
In a policy statement, you can set context for location, Device Trust status, and IP address.
Location context
You can set context in a policy statement to forbid or permit access based on the geographical location (country) of a user or resource that the user is attempting to access.
In a policy statement, location context is set as a condition in the when
clause, in the format context.location in Location::Country::"<COUNTRY_NAME>"
. The country name is set as an ISO-3166-1 code.
In the example shown, the user is permitted to access the resource only when doing so in the United States.
permit (
principal == StrongDM::Account::"a-12354fee63ea6fbe",
action,
resource == StrongDM::Resource::"rs-12324fdc60b00166"
) when {
context has location && context.location in Location::Country::"US"
};
For a Permit statement, if the location is set, the policy will permit user access when (in addition to the other options) the user’s location is either in or not in a specified country.
For a Forbid statement, if the location is set, the policy will prohibit user access when (in addition to the other options) the user’s location is either in or not in a specified country.
For example, the following Forbid statement stops all user access when the user is located in Sweden.
forbid (
principal,
action,
resource
) when {
context has location && context.location in Location::Country::"SE"
};
location
, the location is not considered during policy evaluation, and the policy always permits or forbids, regardless of the user’s location.Device Trust context
You can set context in a policy statement to forbid or permit access based on the Device Trust status of a user’s machine.
This option uses StrongDM’s Device Trust feature, which allows you to integrate device trust scores from an endpoint management software provider (such as CrowdStrike or SentinelOne) into your authentication flow for StrongDM. This feature must be set up and working for this policy option to work.
In a policy statement, Device Trust context is set as a condition in the when
clause, in the format context.trust.status == "<TRUST_STATUS>"
.
Possible statuses are:
- High Trust:
good
- Low Trust:
bad
- Exempt:
exempt
- Unknown:
unknown
When Device Trust context is added to a policy statement and a status value is set, the policy statement evaluates to true if, in addition to the other options, the user’s device trust score matches the one specified.
In the example shown, the user is permitted to access the resource when their Device Trust status is “High Trust.”
permit (
principal == StrongDM::Account::"a-3c931809650c7041",
action,
resource == StrongDM::Resource::"rs-75fe719e62ec28d6"
) when {
context.trust.status == "good"
};
Additionally, you may check for Device Trust values of “good” or “exempt” by checking for a true value of context.trust.ok
and values of “bad” or “unknown” for a false value.
// When the device trust status is good or exempt
when {
context.trust.ok
}
// When the device trust status is bad or unknown
when {
!context.trust.ok
}
When Device Trust is set, the policy will permit user access when the user’s Device Trust status is or is not the specified status. If set to Always, the policy is enforced no matter what the user’s Device Trust status is.
IP range context
You can set context in a policy statement to forbid or permit access based on the IP address range of a user’s machine.
In a policy statement, IP range context is set as a condition in the when
clause, in the format context.network.clientIp.isInRange(ip("<STARTING_IP_ADDRESS>/<SUBNET_MASK>"))
.
In the example shown, the user is denied access when their IP address range doesn’t match 1.2.3.0/24
.
forbid (
principal,
action,
resource
) when {
!context.network.clientIp.isInRange(ip("1.2.3.0/24"))
};
When IP range context is set, the policy will permit user access when the user’s IP address range is or is not the specified range. If set to Always, the policy is enforced no matter what the user’s IP address is.
Annotations
Annotations are optional constraints that may be added to policy statements. An annotation takes the form of the following string: @annotationname("value")
.
The following table lists all possible annotations.
Annotation | Description | Value format | Policy statement type |
---|---|---|---|
@approve(“<WORKFLOW_ID>") | Uses Approval Workflows and requires approvers to approve user access or actions; the action is forbidden until access is approved | String | Permit |
@disconnect("true") | Disconnects the user from the resource | Truthy value | Forbid |
@error("<REASON>") | Overrides the default error message, that the client application may display, that is returned in the protocol response when an operation is forbidden | String | Forbid |
@justify("<PROMPT>") | Requires users to provide justification for their action; justification is logged with the request | String | Permit |
@logout("<REASON>") | Forcefully logs out users when their action is forbidden | String | Forbid |
@maxrows("<NUMBER>") | Restricts queries to returning no more than a defined number of rows | String | Permit |
@mfa("<PROMPT>") | Requires users to complete an MFA prompt in order to complete an action | String | Permit |
@notify("<VALUE>") | Provides a notification message to the user in StrongDM Desktop | String | Forbid or Permit |
You can place annotations only at the very top of the policy statement before permit
or forbid
. Note that some annotations only have an effect for Permit statements or Forbid statements and as such, they should be used only for those policy statement types.
A policy statement may include multiple annotations. For example, the following Permit statement allows all users to perform all actions on the specified resource only when they complete an MFA prompt and provide justification for their action.
@mfa("")
@justify("Please provide a reason to do this.")
permit (
principal,
action,
resource == StrongDM::Resource::"rs-55580513635ae326"
);
Approve
The @approve
annotation is used to require workflow approval for user access or actions.
This option uses StrongDM’s Approval Workflows feature. Approval workflows provide notification for selected approvers to approve access or actions. You must have an approval workflow set up and working for this policy option to work.
In a Permit statement, the workflow approval requirement is set as an annotation in the format @approve("<WORKFLOW_ID")
, where <WORKFLOW_ID>
is the ID of the workflow. The annotation is placed at the top of the Permit statement, immediately before permit
, as in the following example.
@approve("af-3dc23d7965fae03c")
permit (
principal == StrongDM::Account::"a-1c2a83a9623b8021",
action,
resource == StrongDM::Resource::"rs-128613d2623261e3"
);
When a Permit statement includes @approve
, the policy statement evaluates to true if, in addition to the other options, the user’s action is approved via the selected approval workflow. This is the only item that may occur asynchronously, depending on the approval workflow in question. The approval, once granted, is valid for a pre-set amount of time which defaults to four hours and can be changed in the Admin UI under Settings > Workflows > Policy Requests. During this period of time when the approval is valid, the user may retry the actions they were attempting to take on the resource.
Disconnect
The @disconnect
annotation is used to disconnect a user from a resource.
In a Forbid statement, the annotation is set in the format @disconnect("true")
with a case-insensitive truthy value (for example, true
, yes
, on
, t
, y
, or 1
). When set, the user’s connection to the resource is terminated when the policy statement is evaluated.
The annotation is placed at the top of a Forbid statement, immediately before forbid
.
Error
The @error
annotation is used to display an error message to the user.
In a Forbid statement, the annotation is set in the format @error("<REASON>")
, where <REASON>
is the text displayed to the user when the error occurs. The reason value may be any string.
The annotation is placed at the top of a Forbid statement, immediately before forbid
.
Justify
The @justify
annotation is used to prompt users to justify their actions, and then have their written explanation logged.
In a Permit statement, the annotation in the format @justify("<PROMPT>")
, where <PROMPT>
is the your custom text that will be displayed to the user when asked to provide justification for their action. The annotation is placed at the top of the Permit statement, immediately before permit
.
In this example, all users are permitted create roles in the specified database if they provide justification.
@justify("Please explain the need to create tables.")
permit (
principal,
action == SQL::Action::"SQL::Action::"createRole"",
resource == StrongDM::Resource::"rs-12c943ba60afff59"
);
When a Permit statement includes the Justification option, the policy statement evaluates to true if, in addition to the other options, the user records a text justification for their action. The prompt value (for example, Why do you want to do this?
) is shown to the user to provide context for what you wish them to record, why, or for other contextual information that you wish to provide. The reason they provide is not assessed in any way, but it is logged for later review.
@justification
only provides justification prompts to the user in the desktop app, not the CLI. If the user is using StrongDM solely through the CLI, they will not be able view or respond to justification prompts.Logout
The @logout
annotation is used to log out the user forcefully if their action is forbidden.
In a Forbid statement, the annotation is set in the format @logout("<REASON>")
, where <REASON>
is an optional message to be shown to the user when they are logged out. The annotation is placed at the top of the Forbid statement, immediately before forbid
.
In the following example, all users are logged out if they attempt to perform an unknown action on any resource.
@logout("Access denied")
forbid (
principal,
action == SQL::Action::"unknown",
resource
);
When the Forbid statement includes the logout option, the policy evaluates to false, the action is forbidden, the user is logged out, and your defined reason (if set) is provided to the user.
Max rows
The @maxrows
annotation is used to limit queries to a maximum number of rows.
In a Permit statement, the annotation is set in the format @maxrows("<NUMBER>")
, where <NUMBER>
is the maximum number of table rows for the user’s queries. The annotation is placed at the top of a Permit statement, immediately before permit
.
To provide a notification message to the user when the user reaches this limit, use @notify
with the desired message as the value, as in the following example.
@maxrows("100")
@notify("queries are limited to 100 rows")
permit (
principal in StrongDM::Role::"regularuser",
action,
resource
);
MFA
The @mfa
annotation is used to require users to complete a multi-factor authentication (MFA) prompt in order to be allowed to complete their action.
StrongDM’s MFA Authentication feature must be configured and working for this policy option to work.
In a Permit statement, the annotation is set in the format @mfa("<PROMPT>")
, where the value of <PROMPT>
is the action that causes the user to be prompted to complete MFA. The annotation is placed at the top of a policy statement, immediately before permit
.
@mfa("query secrets")
permit (
principal == StrongDM::Account::"a-70254fee63ea6fbe",
action,
resource
);
@mfa("")
). You can add a prompt value in the editing area.When the MFA option is added to a policy statement, the policy statement evaluates to true if, in addition to the other options, the user successfully completes the MFA prompt that is generated upon their action. The MFA prompt remains valid for five minutes before timing out and denying access, if the user’s client (such as their terminal or database access client) does not time out sooner. Users attempting actions that match this policy are prompted again for MFA if one minute or more has elapsed since their last action against the resource.
Notify
The @notify
annotation is used to provide a notification message to the user in StrongDM Desktop. It can be used in any policy statement.
In a policy statement, the annotation is set in the format @notify("<VALUE>")
, where <VALUE>
is the text to be displayed to the user in the notification message. The annotation is placed at the top of a policy statement, immediately before permit
or forbid
.
@notify
only provides notification messages to the user in the desktop app, not the CLI. If the user is using StrongDM solely through the CLI, they will not be able view notifications.