Authoring Guardrails

Authoring Your Own Guardrails

Really

Really is a policy language used to constrain configuration inputs in Resourcely. Instead of needing to write code, we have created Really to match as closely to plain English as possible, making it easier to both read and write policies that comply with your company's best practices.

With the structured and logical syntax of Really, anyone responsible for creating and maintaining security policies can:

  • Write policies faster than with alternatives such as Rego

  • Read, understand, and update policies quickly and easily

Below is an example of a Really policy that requires a specific prefix on an S3 bucket:

GUARDRAIL "Require prefix on all S3 buckets"
    WHEN aws_s3_bucket
        REQUIRE bucket STARTS WITH "resourcely-"

Overview

There are three components in a guardrail, which are grouped together by indentation.

Declaration

The first component is the declaration of the guardrail. In this line you use the keyword GUARDRAIL followed by a string that represents the name of the guardrail. Typically these names are a basic description of what the guardrail does.

GUARDRAIL "Do not allow public S3 buckets"

Matcher

The second component is the matcher. The matcher lets you specify when a guardrail should be applied. It is specified using the keyword WHEN, and it is indented under the guardrail declaration. The statement can either be a direct resource name, or a logical operation to constrain the matcher even further.

For example the following will run the guardrail on all aws_s3_bucket resources that have a tag team with the value eng:

WHEN aws_s3_bucket.tags.team = "eng"

You can also use wildcards in matchers to match multiple resources.

WHEN aws_s3*

Requirements

The third component of a guardrail is the requirements. This is where you actually specify what you expect to be true about the configuration. You specify this using the keyword REQUIRE and indent it under the matcher. A requirement can constrain the inputs to your configuration.

For example if you have a list of instance types you want developers to use, you can write a requirement that requires instance_type to match specific wildcard expressions:

REQUIRE instance_type MATCHES ANY ["m5a*", "m7a*"]

A requirement with no operator constrains the property to be non-empty.

Example

GUARDRAIL "Ensure EC2 Instances have an owner tag"
    WHEN aws_instance
        REQUIRE tags.owner

Operators are used to further constrain the value.

Example

GUARDRAIL "Ensure EC2 Instances have an team tag"
    WHEN aws_instance
        REQUIRE tags.team IN ["engineering", "it", "marketing"]

Reference

Operators

The following operator can be used in both the Matcher and Requirement components of a guardrail.

=

Equality

Example

GUARDRAIL "Engineering team buckets must have bucket prefix resourcely-engineering"
    WHEN aws_s3_bucket.tags.team = "eng"
        REQUIRE bucket_prefix = "resourcely-engineering"

!=

Inquality

Example

GUARDRAIL "Non-engineering team buckets must not have bucket prefix resourcely-engineering"
    WHEN aws_s3_bucket.tags.visibility != "eng"
        REQUIRE bucket_prefix != "resourcely-engineering"

> / >=

Greater Than / Greater Than or Equal

Example

GUARDRAIL "IAM Account Password Policy must specify max password age"
    WHEN aws_iam_account_password_policy
        REQUIRE max_password_age > 0

< / <=

Less Than / Less Than or Equal

Example

GUARDRAIL "IAM Account Password Policy max password age must be less than or equal to 90"
    WHEN aws_iam_account_password_policy
        REQUIRE max_password_age <= 90

STARTS WITH

Check if a string property starts with a specified string.

Example

GUARDRAIL "Engineering team buckets must start with resourcely-engineering"
    WHEN aws_s3_bucket.tags.team = "eng"
        REQUIRE bucket STARTS WITH "resourcely-engineering"

ENDS WITH

Check if a string property ends with a specified string.

Example

GUARDRAIL "Production buckets must have suffix -prod"
    WHEN aws_s3_bucket.tags.environment = "prod"
        REQUIRE bucket ENDS WITH "-prod"

MATCHES

Check if a string property matches a wildcard pattern.

Example

GUARDRAIL "Production buckets must have prod in the bucket name"
    WHEN aws_s3_bucket.tags.environment = "prod"
        REQUIRE bucket MATCHES "*prod*"

MATCHES REGEX

Check if a string property matches a regex pattern.

Example

GUARDRAIL "EC2 Instances must have instance type m5a or m7a and be medium or large"
    WHEN aws_s3_instance
        REQUIRE instance_type MATCHES REGEX "m[5,7]a.(medium|large)$"

MATCHES ANY

Check if a string property matches at least one wildcard pattern in a list of wildcard patterns.

Example

GUARDRAIL "EC2 Instances must have instance type m5a or m7a"
    WHEN aws_instance
        REQUIRE instance_type MATCHES ANY ["m5a*", "m7a*"]

MATCHES ANY REGEX

Check if a string property matches at least one regex pattern in a list of regex patterns.

Example

GUARDRAIL "EC2 Instances must have instance type m5a or m7a and be medium or large"
    WHEN aws_instance
        REQUIRE instance_type MATCHES ANY REGEX ["m5a.(medium|large)$", "m7a.(medium|large)$"]

IN

Check if a string property matches at least one literal string value in a list of strings.

Example

GUARDRAIL "EC2 Instances must have instance type m7a medium or large"
    WHEN aws_instance
        REQUIRE instance_type IN ["m7a.medium", "m7a.large"]

EXISTS

Checks if the property is specified on a resource

Allows empty lists [], the empty string "" , etc. To require that the property is not empty, omit the operator entirely.

Example

GUARDRAIL "Ensure team tag exists on EC2 Instances"
    WHEN aws_instance
        REQUIRE tags.team EXISTS

NOT

Negates an operator (can be used on any operator) in the form NOT <operator>

Example

GUARDRAIL "EC2 Instances must not have instance type m7g"
    WHEN aws_instance
        REQUIRE instance_type NOT MATCHES "m7g.*"

Connectors

You can string together operators with the connectors AND and OR. You can also use parentheses to separate multiple connectors.

AND

Requires that both operators on the left and right hand sides are satisfied.

Example

Note in this example that we could have used multiple REQUIRE statements in place of AND for better readability.

GUARDRAIL "S3 Bucket Public Access Block should set true on all attributes"
    WHEN aws_s3_bucket_public_access_block
        REQUIRE (block_public_acls = true AND block_public_policy = true) AND ignore_public_acls = true AND restrict_public_buckets = true

OR

Requires that at least one operator on the left and right hand sides are satisfied

Example

GUARDRAIL "EC2 Instances must have instance type m7a medium or large"
    WHEN aws_s3_instance
        REQUIRE instance_type MATCHES "m7a.medium" OR instance_type MATCHES "m7a.large"

List Operators

In addition to the operators that evaluate against primitive properties, Really supports operators that can traverse over a list using EVERY, SOME, and NO.

EVERY

Iterates over a list and ensures that all values of that list are satisfied. If the list is empty, EVERY will return true. However, if a guardrail specifies a list property without any list operator, we infer the EVERY operator, except that we require the list to be non-empty (so an empty list will return false)

Example: Explicit declaration of EVERY

In this example ingress.cidr_blocks can be empty

GUARDRAIL "Do not allow 0.0.0.0/0 in ingress"
    WHEN aws_security_group
        REQUIRE EVERY ingress.cidr_blocks != "0.0.0.0/0"

Example: Implicit declaration of EVERY

In this example ingress.cidr_blocks can not be empty

GUARDRAIL "Do not allow 0.0.0.0/0 in ingress"
    WHEN aws_security_group
        REQUIRE ingress.cidr_blocks != "0.0.0.0/0"

SOME

Iterates over a list and ensures that at least one value of that list is satisfied. If the list is empty, SOME will return false.

Example

GUARDRAIL "Require port 22 to be open"
    WHEN aws_security_group
        REQUIRE SOME ingress.from_port = 22

NO

Iterates over a list and ensures that none of the values of the list are satisfied. If the list is empty, NO will return true.

Example

GUARDRAIL "Do not allow port 22 to be open"
    WHEN aws_security_group
        REQUIRE NO ingress.from_port = 22

Special Syntax

HAS

Additional syntax used to change the scope inside a guardrail, usually used to enhance the human readability of a guardrail.

Example

Without HAS, a guardrail to check that a bunch of keys exist on the tags of an aws_instance would look like:

GUARDRAIL "Ensure standard tags exist on EC2 Instances"
    WHEN aws_instance
        REQUIRE tags.team
        REQUIRE tags.environment
        REQUIRE tags.owner
        REQUIRE tags.pii

With HAS you can simplify it to be:

GUARDRAIL "Ensure standard tags exist on EC2 Instances"
    WHEN aws_instance
        REQUIRE tags HAS
            team
            environment
            owner
            pii

Last updated