Policy Creation

Last modified on June 20, 2024

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.

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 or forbid)
  • 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"
);
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").

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"
);

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"
};

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.

AnnotationDescriptionValue formatPolicy statement type
@approve(“<WORKFLOW_ID>")Uses Approval Workflows and requires approvers to approve user access or actions; the action is forbidden until access is approvedStringPermit
@disconnect("true")Disconnects the user from the resourceTruthy valueForbid
@error("<REASON>")Overrides the default error message, that the client application may display, that is returned in the protocol response when an operation is forbiddenStringForbid
@justify("<PROMPT>")Requires users to provide justification for their action; justification is logged with the requestStringPermit
@logout("<REASON>")Forcefully logs out users when their action is forbiddenStringForbid
@maxrows("<NUMBER>")Restricts queries to returning no more than a defined number of rowsStringPermit
@mfa("<PROMPT>")Requires users to complete an MFA prompt in order to complete an actionStringPermit
@notify("<VALUE>")Provides a notification message to the user in StrongDM DesktopStringForbid 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.

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
);

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.

Top