Calculated Policies FAQ



What is a calculated policy?

Customers often need to take remediation action based on specific business rules. For example, a static S3 bucket must be attached to a CloudFront distribution. Calculated policies encode that business logic in an executable form.

How do calculated policies work?

Calculated policies are composed of a GraphQL query to get information about a resource and a Nunjucks template to specify the business logic. The resource data feeds into the template, which outputs strings that match policy values. Calculated policies are always run from the context of the resource itself. The Guardrails Samples Repo has examples of what can be done.

On resource discovery or an event, Guardrails will evaluate a calculated policy in the same way as a normal policy.

Which policy can I turn into a calculated policy?

All policies can be made into calculated policies.

The most common policies used for implementing custom business logic are the policy pairs of Active, and Active > Age then Approved and Approved > Usage. The Active and Approved policies specify what action to take, whether raise and alarm (Check) or delete the offending resource (Enforce). The business logic for deciding resource age or approved status is encoded in the Active > Age and Approved > Usage policies.

Active and Approved controls are standard for all resources.

Are there any limitations to calculated policies?

Calculated policies can only take the actions encoded in existing controls. Active and Approved controls are great for removing unapproved resources or to raise alarms for an administrator. Guardrails has implemented many common remediations, such as encryption at rest, tagging, and resource access policies. If the desired remediation action isn't currently available, reach out to Guardrails support with your use case!

How do I set a calculated policy?

In the example below, we will investigate how to set a VPC Endpoint approved policy to Not Approved if the VPC Endpoint AWS policy has a principal value set to * (You will need an existing VPC endpoint under Guardrails management to test this calculated policy).

  1. In your AWS account locate an existing VPC endpoint and make note of its ID (e.g. vpce-05b1912865c21251f).

  2. Navigate to the account where the policy will be set, then click on the Policies tab, then select the New Setting button:

  3. Once the Create Policy screen opens up, browse to AWS > VPC > Endpoint > Approved > Usage, click Go and then Next.

  4. Expand the Scope drop down menu by clicking on it.

  5. Search or browse to select the level at which the policy will be set. (e.g Navigate to a region in your AWS Account). Once the proper level is showing, click Go.

  6. The policy wizard is currently in Standard mode. (e.g. showing the basic options of Approved, Not approved, or Approved if AWS > VPC > Enabled).

  7. To switch to calculated policy mode: Click on the Switch to calculated mode link. Once switched the layout of the Edit Policy Setting dialog box changes with some additional fields.

  8. Note that Policy Type and Resource Scope were already set in steps 3 & 4.

  9. In the Setting field paste the VPC Endpoint ID that you made note of at the start. If the endpoint has been discovered by Guardrails searching on the vpce-xxx... id will find it quickly and allow you to select it.

  10. Our calculated policy is evaluating the a policy attached to a VPC Endpoint, so we query to return only the policy data. Use the following GraphQL example for your query:

    {
    resource{
    stmts: get(path: "PolicyDocument.Statement")
    }
    }
  11. Validate that the query result window show a valid result for your VPC endpoint. A default endpoint policy will have a result set that looks like this:

    {
    "resource": {
    "stmts": [
    {
    "Action": "*",
    "Effect": "Allow",
    "Resource": "*",
    "Principal": "*"
    }
    ]
    }
    }

    Note the square bracket in the response - this indicates that the returned data is an array, and thus will affect the structure of the policy template.

  12. Calculated policies use Nunjucks template format to evaluate custom logic and return one of the expected values for this policy. In this case the allowed values are: Not approved, Approved, Approved if AWS > VPC > Enabled. The following template checks to see if any overly broad settings exist in the attached policy:

    {% set starValue = "False" %} {# Initialize a value to False #}
    {% for statement in $.resource.stmts %} {# Iterate through all statements in the policy #}
    {% if statement.Principal == "*" %}
    {% set starValue = "True" %}
    {% endif %}
    {% endfor %}
    {% if starValue == "True" %} {# If true, the policy is in violation #}
    "Not approved"
    {% else %}
    "Approved"
    {% endif %}
  13. Verify that the query and template are set and verified by Guardrails to evaluate correctly; the block text below the template field will show the evaluated value for the example VPC endpoint you selected, but each VPC endpoint will be individually evaluated by Guardrails at runtime.

  14. Click the Create button to save your calculated policy, it will immediately take effect and evaluate if the current VPC endpoints are configured correctly.

How can I reference variables that include characters in a calculated policy?

Most variables referenced in calculated policies are trivial, such as stage, data, or name. However, some cloud resources include attributes with characters. These include any attribute with a hyphen (-) or in some specific cases, where the attribute is only characters (*/*).

Let's assume we are doing the following query and template for the policy AWS > API Gateway > Stage > Approved > Usage:

{
stage {
methodSettings
}
}
{
"stage": {
"methodSettings": {
"*/*": {
"loggingLevel": "INFO",
"cachingEnabled": false,
"metricsEnabled": true,
"dataTraceEnabled": false,
"cacheTtlInSeconds": 300,
"cacheDataEncrypted": false,
"throttlingRateLimit": 10,000,
"throttlingBurstLimit": 5000,
"requireAuthorizationForCacheControl": true,
"unauthorizedCacheControlHeaderStrategy": "SUCCEED_WITH_RESPONSE_HEADER"
}
}
}
}

The goal is to make an evaluation based off the loggingLevel attribute. To call this attribute, we need to use the following syntax in the nunjucks template:

$.stage.methodSettings['*/*'].loggingLevel

We can do the same things for attributes that contain hyphens, like so:

$.bucket.turbot.tags['this-tag-has-hyphens']

How do I add Terraform to calculated policy templates?

Using a policy such as AWS > Backup > Source > Stack, it is possible to create a calculated policy template that is Terraform. For example,

resource "aws_iam_role" "{{ $.region.metadata.aws.accountId }}_ec2_backup_role" {
name = "turbot_ec2_backup_role"
assume_role_policy = ""
}

While the calculated policy will be in the ok state (Assuming the query is valid), the control AWS > Backup > Source will return invalid:

Failed to convert the stack source from HCL to JSON
Command failed: PATH=$PATH:. hcl2json
Failed to convert file: :1,104-122: Invalid single-argument block definition;
A single-line block definition must end with a closing brace immediately after its single argument definition.

To remedy this, simply use the pipe character (|) at the start of the terraform resource definition in the calculated policy template:

|
resource "aws_iam_role" "{{ $.region.metadata.aws.regionName }}_ec2_backup_role" {
name = "turbot_ec2_backup_role"
assume_role_policy = ""
}

Additional Reading