Deeper Dive: Updates to Our Terraform Essential Policy Toolkit

9 min read

Following up from our previous Kubernetes Essential Policy Toolkit Deeper Dive, we’re excited to announce upcoming enhancements to Terraform use cases in Styra DAS and take a deeper dive into the Styra DAS Terraform policy toolkit. With the enhanced Styra DAS Terraform policy toolkit, we’re making it even easier for platform engineering and cloud infrastructure teams to support their company’s application developers while delivering secure and compliant infrastructure configurations.

TLDR — Here are the key updates discussed below:

  • Expanded pre-built Terraform policy library
    • Policy-building for the Terraform Kubernetes provider
  • Flexible policy violation returns and additional enhancements
    • Styra-built rule Terraform plan input
    • Rich rule metadata and decisions
    • Styra DAS UI Terraform rule filters
  • Low/no code policy building with Terraform Custom Snippets

Expanded Pre-Built Terraform Policy Library

Our customers have found the Styra DAS Terraform policy library has enabled them to set up common policy guardrails for Terraform quickly to achieve a baseline state of infrastructure security. As the use of the Styra DAS Terraform use case has expanded since its launch in 2021, so too has the breadth of cloud provider and resource configurations being secured by customers, requiring a rapid policy library expansion to meet the needs of customers.

With our January infrastructure announcement, we detailed the largest expansion to date of the Styra DAS Terraform policy library, bringing our pre-built rule count to nearly 600 rules. This is in large part thanks to the fantastic work in the open- source KICS project, where the open- source community has built hundreds of Rego rules for Terraform. We love to see the growing use of OPA for infrastructure use cases, and we’re committed to supporting open source projects using OPA and Rego, like KICS. We’re excited to work with the KICS library and contribute back policy improvements for others to enjoy as part of our commitment to open source.

The expanded Styra DAS Terraform policy library rules include guardrails for the AWS, Azure, Google, and Kubernetes Terraform providers. The addition of policy guardrails for the Kubernetes Terraform provider is particularly important as we’ve seen the rise of platform engineering teams using Terraform to manage both the underlying infrastructure for Kubernetes clusters and the cluster configurations themselves. 

Combining Terraform and Kubernetes in the DevOps cycle

It’s now easy to organize your Terraform Kubernetes policy library rules in Styra DAS. Now, when you create a new Terraform System or Stack in Styra DAS, an example policy file is automatically created within the policy.kubernetes.cluster namespace as an example (this policy file can always be deleted if not needed). With the Kubernetes Terraform policy guardrails, teams can now combine the use of both the Styra DAS Terraform and Kubernetes use cases to enforce policy guardrails in additional policy enforcement points (PEPs) in the DevOps cycle.

As background, the policy file structure for Terraform Systems and Stacks (e.g. package policy.<provider>.<resource>) mirrors the provider >> resource structure in Terraform. For example, rules applying to AWS S3 buckets could be organized in a policy.aws.s3 package, while rules for Kubernetes volumes could be organized in a policy.kubernetes.volumes package. The Policy Package and Module Structure section of the Styra DAS Terraform Policy Authoring docs provides an in-depth explanation and further examples of this concept.

Organize Terraform Configurations Using Systems and Stacks

As a recap, Styra DAS Terraform policy library rules can be added to build infrastructure-as-code (IaC) policy guardrails at both the System level and the Stack level. Systems and Stacks in Styra DAS are based on a system type definition for a specific use case (e.g., Terraform, Kubernetes, Cloudformation, Envoy, Istio, etc.), which defines how policies are evaluated, how rules in policies receive policy inputs and how policy violations are enforced. In contrast with Kubernetes Systems in Styra DAS (which represent a single cluster), the structure of Terraform Systems (and Stacks) is more flexible, allowing users to organize policy guardrails to match their Terraform workflows. While Systems in Styra DAS serve as the Policy Decision Point (PDP) at which policy guardrails are evaluated against a given input (e.g., Terraform plan), a Terraform System can represent a multitude of concepts: a universal CICD policy check, a GitHub Action on a single repository environment branch, a workspace in Terraform Cloud or Terraform Enterprise or an environment in env0. Use a Terraform system to enforce Terraform policy guardrails for resources managed by a single team or department, resources supporting a specific application, resources in an AWS account representing a production environment or resource across multiple accounts and even cloud providers.

Most customers then use Terraform Stacks to enforce policy guardrails common to multiple Systems, with a specific team or team members provided access to manage Stacks and their policies. For example, your platform engineering team may create a Production Stack which applies to all Systems with a production label and your security team manages a Baseline Stack which applies baseline security policies to every System across all environments, applications, accounts and teams. Stacks also support the creation of policy exceptions based on feature metadata specified on a System, allowing teams managing Stack policies to provide paths for System owners to exempt their system from certain Stack-level policies. An example use of this feature may include a Baseline Stack allowing for a System representing a company’s public website to exempt itself from the “AWS: S3: Block All Public Access to Buckets” rule from the Styra DAS Terraform policy library.

Flexible Policy Violation Returns and Additional Enhancements

In this week’s upcoming Styra DAS production update, we have also incorporated feedback from customers requesting additional context in policy violations generated by rules from our Terraform policy library. Rolling out first to our enterprise customers, these enhancements impact not just the Terraform policy library but also the underlying Terraform system type and even the Styra DAS UI. The enhancements represent an entirely new Terraform system type definition (v2.0) and set the stage for more significant Terraform features and enhancements in the coming months. Users with Styra DAS Terraform v1 Systems and Stacks can continue using v1 to aid in migrating policy guardrails to v2.

The Styra DAS Terraform System Type v2.0 introduces several new features and major changes to support those features:

  • Styra-built policy library rules evaluate against resources in the Terraform plan resource_changes object
  • Richer metadata for policy library rules
  • Policy library rule decision objects return rich rule metadata and resource context
  • Filter rule violations by Terraform resource actions via the Styra DAS UI

Styra-Built Rule Terraform Plan Input

Policy library rules built by Styra will now evaluate against the resource_changes object in the Terraform plan rather than planned_values. This allows Styra-built rules to take into account the actions Terraform is taking on a resource (e.g., create, update, replace) as well as the previous state of a resource before a change. This “behind-the-scenes” change enables features like rule violation filtering in the Styra DAS UI.

The Rego rules in the KICS open-source tool use the planned_values object of Terraform plans, and the KICS rules in the Styra DAS Terraform policy library rules will continue to use the planned_values` object to remain at parity with the KICS open-source tool.

Policy Library Rule Rich Metadata and Decisions

The metadata for all rules in the Styra DAS Terraform policy library will include a number of new parameters, many of which will be included in the rule’s decision object. These new parameters, some of which may be empty at first and will be expanded upon over time, provide additional details about the rule, including compatible Terraform versions, compatible Terraform provider versions and Terraform configuration objects evaluated.

Additionally, decision objects returned by rules will also include context about the resource which resulted in the rule violation. These metadata parameters are detailed in the Rule Formats section of the Styra DAS Terraform Policy Authoring documentation.

You’ll see this richer return data in the decision object in the Styra DAS code editor view after adding a policy library rule:

With this additional metadata object in the decision return object, users can customize values in the object for their use case, such as lowering the Severity of a rule violation or setting a custom Control Category.

DAS UI Terraform Rule Filter

Similar in functionality to the Styra DAS Kubernetes rule filters, the new filter functionality available in the UI for Styra DAS Terraform v2 Systems and Stacks can be used on any policy library rule or Custom Snippet. Enable the Actions filter to select which resource actions the rule will or will not apply to. For instance, enable the Actions filter with only “create” and the rule will only generate violations for misconfigured resources being created, or configure a rule to exclude resources that aren’t changing in the current Terraform plan by using the negation option and selecting the “no-op” and “read” actions.

Building Low/No Code Policies with Terraform Custom Snippets

As with the Styra DAS Kubernetes System Type, the Terraform System Type supports the Custom Snippets feature. This allows customers to expand beyond the pre-built policy library rules to create and parameterize custom policies, making them available to users in their organization to add and configure in a low-code/no-code manner via the DAS UI. This feature is perfect for teams with members who have a range of policy-as-code experience: those who are familiar with authoring policy as code with Rego as well as those who may not even be technical.

To create a Custom Snippet, author your custom rule in a git repository and add the appropriate Custom Snippet metadata to your rule. This metadata defines the title, description, input parameters, applicable System Type and policy return object for your Custom Snippet. Then add that git repository as a global library in your tenant by following the steps outlined in the Mounting Git Repositories for Libraries doc.

Styra has created an example Terraform Custom Snippet, which can be found in Styra’s Custom Snippet Samples GitHub repository: “Allow providers only from specified registries”. This Custom Snippet can help with managing your Terraform supply chain security to ensure developers only use trusted providers, providers from trusted authors, and/or providers from trusted registries. Here’s what that looks like:

import data.global.systemtypes["terraform:2.0"].library.utils.v1 as utils
import data.library.parameters


#############################################################################
# METADATA: library-snippet/terraform
# version: v1
# title: "Allow providers only from specified registries"
# description: >-
#   Allow only providers from specified registries (e.g., registry.terraform.io, private-registry.example.com) to prevent using unapproved registries.
#   Can also be used to allow providers from specific publishers in a registry (e.g., registry.terraform.io/hashicorp).
#   Checks if the provider's full name in the Terraform plan starts with any string from the list specified.
#   To allow all registries, use the '*' wildcard entry.
# custom:
#   id: "custom.allowed_terraform_provider_registries"
#   impact: "Using a provider from an unapproved registry could result in use of unapproved cloud providers or credential exposure to malicious provider plugins"
#   remediation: "Only use Terraform providers from approved registries and publishers"
#   severity: "high"
#   resource_category: "Provider"
#   control_category: "Supply Chain Security"
#   rule_link: "https://docs.styra.com/systems/terraform/snippets"
#   platform:
#     name: "terraform"
#     versions:
#       min: "v0.12"
#       max: "v1.3"
#   rule_targets:
#     - { scope: "provider", argument: "full_name" }
# filePath:
# - systems/.*/policy/.*
# - stacks/.*/policy/.*
# schema:
#   parameters:
#     - name: allowed_registries
#       label: "Allowed provider registries"
#       type: set_of_strings
#       placeholder: "Examples: registry.terraform.io, private-registry.example.com, registry.terraform.io/hashicorp, *"
#       required: true
#   decision:
#     - type: rego
#       key: allowed
#       value: "false"
#     - type: rego
#       key: message
#       value: "violation.message"
#     - type: rego
#       key: metadata
#       value: "violation.metadata"
# policy:
#   rule:
#     type: rego
#     value: "{|{this}}[violation]"
#############################################################################
allowed_terraform_provider_registries[violation] {
   provider_registry_allowed[decision]


   violation := {
       "allowed": false,
       "message": decision.message,
       "metadata": utils.build_metadata_return(rego.metadata.rule(), parameters.allowed_registries, decision.resource, decision.context),
   }
}


# Check provider starts with registries specified in rule input
provider_registry_allowed[obj] {
   some provider
   config := input.configuration.provider_config[provider]


   not registry_allowed(config.full_name)


   obj := {
       "message": sprintf("Provider %s registry %s not allowed.", [provider, config.full_name]),
       "resource": config,
       "context": {"full_name": config.full_name},
   }
}


# User allows any registries
registry_allowed(reg) {
   wildcard(parameters.allowed_registries)
}


# User provided list of allowed registries
registry_allowed(reg) {
   not wildcard(parameters.allowed_registries)
   starts_with_any(reg, parameters.allowed_registries)
}


# Helper utils
starts_with_any(str, arr) {
   startswith(str, arr[_])
}


wildcard(arr) {
   arr[_] == "*"
}

In this Custom Snippet, the user specifies the registries, publishers, and providers to allow in the allowed_registries parameter. The Custom Snippet then checks the provider_config in the configuration object of the Terraform plan and compares it to the user-specified allowed registries. The metadata of this Custom Snippet includes the new metadata parameters for the Terraform v2 System Type.

We can see this Custom Snippet in action in a Styra DAS Terraform v2 System after adding the git repository containing it as a DAS Library:

After adding the Custom Snippet to a new policy in the policy.terraform.providers namespace, setting it to Enforce mode and configuring the allowed registries, use the Preview button to see the Custom Snippet in action. In the Preview mode’s Input pane, paste the following JSON representing the provider_config in the configuration object of a Terraform plan (the other plan contents are not needed for this Custom Snippet).

{
 "configuration": {
   "provider_config": {
     "aws": {
       "name": "aws",
       "full_name": "registry.terraform.io/hashicorp/aws",
       "version_constraint": "> 4.0.0",
       "expressions": {
         "region": {
           "constant_value": "us-west-2"
         }
       }
     }
   }
 }
}

Click the Preview button again to re-run policy evaluation,
and you’ll see the expected violation in the Output pane:

{
 "enforce": [
   {
     "allowed": false,
     "message": "Provider aws registry registry.terraform.io/hashicorp/aws not allowed.",
     "metadata": {
       "resource": {
         "actions": null,
         "address": null,
         "context": {
           "full_name": "registry.terraform.io/hashicorp/aws"
         },
         "module": "root_module",
         "name": "aws",
         "type": null
       },
       "rule": {
         "compliance_pack": null,
         "control_category": "Supply Chain Security",
         "description": "Allow only providers from specified registries 
(e.g., registry.terraform.io, private-registry.example.com) to prevent using unapproved registries. 
Can also be used to allow providers from specific publishers in a registry (e.g., registry.terraform.io/hashicorp). Checks if the provider's full name in the 
Terraform plan starts with any string from the list specified. 
To allow all registries, use the '*' wildcard entry.",
         "id": "custom.allowed_terraform_provider_registries",
         "impact": "Using a provider from an unapproved registry could result in use of unapproved cloud providers or credential exposure to malicious provider plugins",
         "parameters": [
           "example.com/mycompany"
         ],
         "platform": "terraform",
         "provider": null,
         "remediation": "Only use Terraform providers from approved registries and publishers",
         "resource_category": "Provider",
         "rule_link": "https://docs.styra.com/systems/terraform/snippets",
         "rule_targets": [
           {
             "argument": "full_name",
             "scope": "provider"
           }
         ],
         "severity": "high",
         "title": "Allow providers only from specified registries"
       }
     }
   }
 ]
}

Wrap Up and Next Steps

Securing Terraform configurations in an enterprise across multiple teams, environments, clouds, and CICD tools without disrupting customers or feature releases is a challenge assigned to platform engineering teams frequently orders of magnitude outnumbered by application engineers. With our expanded Styra DAS Terraform policy library, support for Terraform Custom Snippets and this week’s launch of richer policy violation returns and simple UI-based rule filtering, platform engineering teams, along with security teams and application engineers, have the tools to quickly and easily configure and manage Terraform policy guardrails in a no-code/low-code way.

If you’d like to learn more about using Styra DAS for Terraform, be sure to book some time with a Styra team member today. Moreover, you can explore many of these changes today in Styra DAS Free.

Cloud native
Authorization

Entitlement Explosion Repair

Join Styra and PACLabs on April 11 for a webinar exploring how organizations are using Policy as Code for smarter Access Control.

Speak with an Engineer

Request time with our team to talk about how you can modernize your access management.