Conftest and authoring custom checks in Policy as Code

Introduction

In the rapidly evolving world of Infrastructure as Code (IaC), ensuring compliance and security is paramount. How can we use tools that agnostically enforce guard rails uninformedly? One way to do this without incurring extravagant costs is using Conftest a wrapper of OPA. In this latest blog I’m going to cover how Conftest seamlessly integrates with Terraform, one of the most popular IaC tools, to provide robust policy compliance checks. Whether you’re a DevOps professional, a cloud engineer, or an IT security specialist, understanding how Conftest enhances Terraform’s capabilities is essential. From automated policy checks to customized rule enforcement, our comprehensive guide covers everything you need to know about leveraging Conftest with Terraform for a more secure, compliant, and efficient IaC environment.

How it works?

Conftest similar to static analysis relies on the the terraform plan command extracting a .json file that is able to then use a .rego file to parse the file for the conditions you set, this requires you also knowing what you’d like to block.

terraform init
terraform plan -out=tfplan
terraform show -json ./tfplan | jq > tfplan.json

Okay so once you are at this point you’ll have a .json file that represents the amount of resources you’re going to create with the configuration, referencing this blog to assist in finding the resources that are changed you can use the following syntax.

cat tfplan.json | jq .resource_changes # List resources that are going to be changed this can be create, delete, null
cat tfplan.json | jq '.resource_changes[].change.actions[]' # this is the action associated with the change.

Showing when the command is ran on our file the tfplan json shows the “actions” with create value.

So let’s use conftest if you have this installed locally you can run this simply by calling ‘conftest test <tfplan.json> -p ./policy/<rego check>

For the following checks I’ve created is Azure Policy enabled on our cluster?

package main

deny[msg] {
    input.resource_changes[i].type == "azurerm_kubernetes_cluster"
    not input.resource_changes[i].change.after.azure_policy_enabled
    resource_name := input.resource_changes[i].change.after.name
    msg = sprintf("Azure Policy should be enabled for AKS cluster '%s'", [resource_name])
}

So if run our test this should return a failure as shown I have other checks I’m troubleshooting.

Now remember, we crafted this failure we can also change this to a warn message and it will change the output, or we can have this noted as a exception.

If you’re familiar with Go then Rego checks being authored should come almost as natural I’ve recently been programming in Go and have noticed the simplicity to switch over for what exactly I’m trying to accomplish for conftest.

Use of Conftest

While of course this demo on this post shows the very basic iteration of this as I’ve quickly cloned my repo converted a existing terraform configuration to tfplan.json. I’ve then created a policy folder along with the rego file to run the check, notice that you’ll have to understand what enforcement you’d like to have this applied likely on a deployment check prior to release. In terms of the CI/CD aspect of the use of this it can be a guard rail for your defined parameters of checks prior to approval I could see the use of this as a litmus test on if new configurations aren’t passing checks you return a report as a threshold of what is allowed to fail, warn in a total count similar to a bash script.

Summary

Conftest is a wrapper over the Open Policy Agent that allows the extension of using for policy as code and not limited to terraform we can also enforce this on Dockerfile and other file types. This is not to serve as the only tool but a tool nonetheless that can assist your security operations to your developer environments. If you aren’t running a static analysis as well such as SAST on the code with trivy you should as well to ensure that is crafting best practices for your configuration. The landscape of cloud native does expand to infrastructure and the use of infrastructure as code should have checks to ensure prior to deployment they are in compliance with organizations security posture.