Policy Creation
Last modified on October 28, 2024
This feature is part of the Enterprise plan. 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"
);
If you want all members of a role to be able to perform all database actions against all Postgres resources, you could use the following example policy statement:
permit (
principal in StrongDM::Role::"r-1caa595464152e78",
action,
resource == Postgres::Database
);
This statement allows all actions against Postgres databases for users with the specified role, and could be followed by other statements that forbid particular sensitive actions if necessary.
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.
This section discusses the following contextual elements:
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 allows 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 prohibits 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 calculation
When a connection or query is performed by a StrongDM user against a resource, their IP is used to calculate an approximate geographic location. The context property used to determine this public IP address is network.clientIp
, and if it is not present, a location will not be available.
The client IP is always a public IP address. It is the source of the client connection to the StrongDM control plane.
If a client is connecting to the StrongDM control plane via a VPN or proxy, their client IP will be the public IP address of the VPN proxy server, and so will the calculated location.
location
property of a client behind a VPN should not be relied upon as the true location of the client.Network information context
You can set context in a policy statement to forbid or permit access based on the network information involved in the request. This can allow you to ensure that users are connecting from the correct IPs, to correct ports, or even to forbid access to a group of resources unless the resource IP is at a particular IP.
- Client IP address: As detailed in the location calculation section, the client IP address is the public address from which a client connects to the StrongDM control plane. If the client is behind a VPN, it will be the address of the proxy server for the VPN. This context can be set in a policy statement in the following format:
context.network.clientIp == ip("101.111.121.131")
- Request IP address: The request IP address is the IP address of the client as seen by the ingress point into the StrongDM network (either the control plane or a gateway). If the client is behind a VPN, this address will be the address of the VPN client. Despite not being the public IP of the user’s device, this could still be useful in policy if, for example, clients are assigned static IP addresses on the VPN. Policies could then restrict access that authorized access based on the assigned VPN address being the same as the request IP.
context.network.requestIp == ip("102.112.122.132")
- Destination IP address: IP address of the resource being acted against
context.network.destinationIp == ip("103.113.123.133")
- Target hostname: Hostname of the resource being acted against
context.network.target.hostname == "db.example.com"
- Target port: Port of the resource being acted against
context.network.target.port == 1234
In the example shown, the user is permitted to access when particular network information is present in the request context.
permit (
principal,
action,
resource
) when {
context.network.clientIp == ip("101.111.121.131") &&
context.network.requestIp == ip("102.112.122.132") &&
context.network.destinationIp == ip("103.113.123.133") &&
context.target.hostname == "db.example.com" &&
context.target.port == "1234"
};
IP ranges
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"))
};
Considerations when checking for IP ranges:
- If using the builder controls to set IP range context, under When, click Always (to change it), and click IP Range. Select either is or is not, enter the Starting IP Address, and enter the Subnet Mask (if any).
- When IP range context is set, the policy permits 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.
- When the policy statement has no context for IP range, the user’s IP address is not considered during policy evaluation, and the policy always permits or forbids, regardless of the user’s IP address.
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 permits 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.
Resource tags context
You can require particular tags to be set on a resource in order for the policy statement to apply to it. For example, you can have users with a particular development role be permitted to perform actions against resources if the resources are tagged with env=dev
. The following is an example of this policy statement:
permit (
principal in StrongDM::Role::"r-1caa595464152e78",
action,
resource
) when {
resource.tags has env && resource.tags.env == "dev"
};
In the when
clause in this example, you can check that the resource has the tag at all (resource.tags has env
), and then verify the value of the particular tag in question (resource.tags.env == "dev"
).
Time context
You can add time context to a policy statement in order to permit or forbid access based on the time and date when the policy is evaluated. You can specify a timestamp, day of the week, day of the month, month, and year, as well as set duration and time ranges. Setting time context provides even more granular control over when users can access resources.
In a policy statement, time context is set as a condition in the when
clause, in the format context.utcNow.<FIELD> == <VALUE>
, where <FIELD>
is one of the following.
Field | Description | Format | Example usage |
---|---|---|---|
day | Day of month, starting with 1 | Long | context.utcNow.day == 30 |
dayOfWeek | Day of week, where Sunday is 1 and Saturday is 7 | Long | context.utcNow.dayOfWeek == 3 |
month | Month of year, where January is 1 and December is 12 | Long | context.utcNow.month == 1 |
timestamp | Cedar datetime (UTC) | String | context.utcNow.timestamp == YYYY-MM-DDThh:mm:ss.SSS(+/-)hhmm |
year | Four-digit year | Long | context.utcNow.year == 2024 |
Datetime
Using the datetime constructor (as in datetime("YYYY-MM-DDTHH:mm:ss.SSS(Z|(+|-)hhmm)")
) allows you to create a concrete datetime that can be used in comparisons. The time is optional, but the timestamp is always converted to UTC by applying the offset provided, if any.
Datetime objects have the following methods and operators:
.offset(duration)
: Shifts the date by the duration.durationSince(datetime)
: Subtracts the dates, returning a (possibly negative) duration.toDate()
: Drops the time portion of the datetime.toTime()
: Drops the date portion of the datetime, and returns a duration of time since midnight of the previous datetime
Datetime supports the following comparison operators:
==
!=
<
<=
>
>=
Duration
You can also set time context with a duration (as in duration("1d2h3m4s5ms")
) in order to construct a concrete duration that can be used in comparisons. The duration value may also serve as a representation of time (for example, duration("5h30m")
is effectively 5:30 a.m.).
Duration objects have the following methods and operators:
.toDays()
: Returns the number of days in the duration as an integer.toHours()
: Returns the number of hours in the duration as an integer.toMinutes()
: Returns the number of minutes in the duration as an integer.toSeconds()
: Returns the number of seconds in the duration as an integer.toMilliseconds()
: Returns the number of milliseconds in the duration as an integer
Duration supports the following comparison operators:
==
!=
<
<=
>
>=
Time context examples
In the following example, users are denied access to the resource when they try to connect outside of 9 a.m. to 5 p.m.
forbid (
principal,
action == StrongDM::Action::"connect",
resource == StrongDM::Resource::"rs-49ef6ada653aaafb"
) when {
context.utcNow.timestamp.toTime().toHours() <= 9 &&
context.utcNow.timestamp.toTime().toHours() >= 17
};
In the next example, access is allowed only during approved maintenance windows (in this case, on Thursdays from 2 a.m. to 5 a.m. UTC).
permit (
principal,
action,
resource
)
when {
context.utcNow.dayOfWeek == 5 &&
context.utcNow.timestamp.toTime().toHours() >= 2 &&
context.utcNow.timestamp.toTime().toHours() <= 5
};
The following Permit statement shows an example of how dayOfWeek
may be used. In the example, the user is permitted to access the resource on Thursdays only.
permit (
principal == StrongDM::Account::"a-3c931809650c7041",
action,
resource == StrongDM::Resource::"rs-75fe719e62ec28d6"
) when {
context.utcNow.dayOfWeek == 5
};
The following Forbid statement shows an example of how dayOfWeek
can be used to forbid users from accessing resources on Saturdays and Sundays.
forbid (
principal,
action,
resource
)
when {
[1,7].contains(context.utcNow.dayOfWeek)
};
In the following example, users can access the resource only during business hours (in this example from 9 a.m. to 5 p.m. (UTC)).
permit (
principal,
action == StrongDM::Action::"connect",
resource == StrongDM::Resource::"rs-49ef6ada653aaafb"
) when {
context.utcNow.timestamp.toTime() < duration("9h") ||
context.utcNow.timestamp.toTime() > duration("17h")
};
In the following Permit statement, users may access resources only on the first Tuesday, following the first Monday, in the month of November (UTC).
permit (
principal,
action,
resource
)
when {
context.utcNow.dayOfWeek == 3 && context.utcNow.month == 11 &&
context.utcNow.day > 1 && context.utcNow.day < 9
};
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"
);
Cache timeout
Some annotations, such as @mfa and @justify, allow you to set a cache timeout value with the annotation. The cache timeout is the amount of time that the system waits before prompting the user again for MFA or justification. Setting a cache timeout allows you to control how frequently a prompt or challenge is presented to the user when the user attempts to connect to a resource.
A short cache timeout provides the strictest security requirement for authentication, but if you need to relax that for usability reasons, you have the option to set a longer cache timeout. The maximum cache timeout value is 24 hours, and the minimum value is 1 minute. If not specified, the cache timeout defaults to one minute. Setting a cache timeout higher than the default is optional, but all cache length specifications must be explicitly written in policy.
Let’s say, for example, that your policy requires the user to complete an MFA challenge in order to connect to a resource, and that the MFA challenge has a cache timeout of one hour. In such a scenario, the user completes the MFA challenge when prompted, connects to the resource if the challenge is successful, and if still using the resource an hour later, is prompted with the MFA challenge again.
If more than one annotation of the same type with a cache timeout is collected from policies, the request with the smallest cache length is used. For example, if a policy has two policy statements with MFA requirements, and those requirements have a cache timeout of 10 minutes and 30 minutes, the 10-minute cache timeout is used.
Different types of requirements (MFA or justify) do not affect each other’s cache length.
Please note that a change in policy may result in cache invalidation (for example, changing cache=5m
to cache=60m
).
Format
A cache timeout may be formatted as a query string or a simple string in the annotation’s prompt value. The format that you choose depends on whether or not you need to send other parameters with the annotation, such as a reason or prompt value, or an amount of time.
Use query string format (for example, ?reason=reason+text&cache=60m
) to set a cache timeout value and send a reason or prompt value. The query string format is valuable primarily when you need to pass multiple parameters, such as with the MFA cache. The first item in the query string is ?key=value
, the parameters that follow are &key=value
, and normal query string syntax is used for all other parts (for example, +
for spaces and character codes for punctuation).
You may use a simple string if you only want to send a reason or prompt with the annotation (for example, Please provide a reason to do this.
).
To set a cache timeout other than the default, specify the cache parameter and timeout length as a query string in the format "?cache=00h00m"
. Valid values for cache
are amounts of time in minutes “m” and/or hours “h”, such as 6m
, 03h
, 20h30m
, 2h15m
, and so forth.
Put the query string in the annotation’s prompt value, as in the examples shown.
Example cache timeout settings
Require MFA challenge every 2 hours
@mfa("?cache=2h0m")
permit (
principal,
action,
resource
);
Require MFA challenge every 1 hour and include a reason in the MFA challenge
@mfa("?reason=reason+text&cache=60m")
permit (
principal,
action,
resource
);
Require MFA challenge and use the default cache timeout, without sending a reason
@mfa("")
permit (
principal,
action,
resource
);
Require MFA challenge, use the default cache timeout, and send a reason
@mfa("MFA is required to continue.")
permit (
principal,
action,
resource
);
Require justification every 4 hours without sending a prompt message
@justify("?cache=4h")
permit (
principal,
action,
resource
);
Require justification every 24 hours and include a justification prompt message
@justify("?prompt=Please provide a reason to do this.&cache=24h")
permit (
principal,
action,
resource
);
Require justification, include a prompt message, and use the default cache timeout
@justify("Please provide a reason to do this.")
permit (
principal,
action,
resource
);
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 is set in the format @justify("<PROMPT>")
, where <PROMPT>
is the your custom text that is 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 the example shown, all users are permitted create roles in the specified database if they provide justification.
@justify("Please explain the need to create a table.")
permit (
principal,
action == SQL::Action::"SQL::Action::"createTable"",
resource == StrongDM::Resource::"rs-12c943ba60afff59"
);
The following example shows how @justify
and time context can be used in a policy statement. In this example, users must provide justification for accessing a resource outside of business hours, 9 a.m. to 5 p.m.
@justify("Why are you working outside of 9am - 5pm?")
permit (
principal,
action == StrongDM::Action::"connect",
resource == StrongDM::Resource::"rs-49ef6ada653aaafb"
) when {
context.utcNow.timestamp.toTime() < duration("9h") ||
context.utcNow.timestamp.toTime() > duration("17h")
};
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 are not 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.
@mfa
only provides MFA challenge prompts to the user in the desktop app, not the CLI. If the user is using StrongDM solely through the CLI, they are not able view or respond to MFA challenge prompts. Other MFA providers provide prompts via a third-party application.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 are not able to view notifications.Policy Management via CLI
Policies also can be created, updated, or deleted via the CLI. The commands are:
Policy creation via CLI
When creating a policy, first write the policy in the Cedar policy language and save it to a text file, similar to this example:
// cedarfile.txt
permit (
principal == StrongDM::Account::"a-70254fee63ea6fbe",
action,
resource
);
Then use the sdm admin policies create
command to create the policy in StrongDM in the format sdm admin policies create --policy-file <FILEPATH/FILENAME> --description <DESCRIPTION> <POLICY_NAME>
. For example:
sdm admin policies create --policy-file Desktop/cedarfile.txt --description "An example policy that allows Bob all access" "Example Policy"
- The value of the
--policy-file
option is the file name and path of the text file containing the Cedar policy statements you wish to include in the policy (for example,Desktop/cedarfile.txt
). - The value of the
--description
option is optional and would typically describe the policy statements contained within the policy. - The last parameter is the name of the policy (for example, “Example Policy”).
Policy update via CLI
sdm admin policies list -e
extended option to show the content of the policies that are listed.When updating a policy, first write the updated policy in a text file saved on your local device, similar to this example:
// cedarfile.txt
permit (
principal == StrongDM::Account::"a-70254fee63ea6fbe",
action,
resource == StrongDM::Resource::"rs-12c943ba60afff59"
);
Then use the sdm admin policies update
command to update the policy in StrongDM in the format sdm admin policies update --name <NEW_POLICY_NAME> --policy-file <FILEPATH/FILENAME> --description <DESCRIPTION> <POLICY_NAME>
.
sdm admin policies update --name "Example Policy v2" --policy-file Desktop/cedarfile.txt --description "An example policy that allows Bob all access to the dev Postgres" "Example Policy"
- The value of the
--name
option is optional and is the new name you wish to give to the policy. - The value of the
--policy-file
option is the file name and path of the text file containing the Cedar policy statements you wish to include in the policy (for example,Desktop/cedarfile.txt
). - The value of the
--description
option is optional and would typically describe the policy statements contained within the policy. - The last parameter is the name of the policy (for example, “Example Policy”).
--name
option as shown in the example to indicate the desired new name, while still entering the current name as the final parameter of the command.