Cloud Security in CI/CD Part II: Terraform and Regula for Infrastructure as Code Validation

 

In part 1 of this walkthrough, we set up a CI/CD pipeline to define, commit, deploy, and secure infrastructure as code. To recap, here are the components:

  • Amazon Web Services (AWS): Provide cloud infrastructure (a VPC and security group)
  • Terraform: Define infrastructure as code
  • GitHub: Store infrastructure as code in version control
  • CircleCI: Deploy infrastructure via Terraform and kick off Fugue scan
  • Fugue: Scan infrastructure for any noncompliant resources and set a new baseline

Here, in part 2, we'll pick up where we left off and add a new component to the front of the pipeline:

  • Regula: Evaluate Terraform infrastructure-as-code for compliance

cicd-header

 

Our open source tool Regula uses the Open Policy Agent (OPA) engine to check Terraform plans against a library of compliance policies. The policy-as-code is written in Rego, OPA's query language.

 

With the addition of Regula, the pipeline demonstrates end-to-end security and compliance. Regula validates resource configuration pre-deployment and Fugue ensures it stays compliant post-deployment.

 

We'll also implement an open source utility called Conftest, which is a test runner for configuration files that use Rego for policy-as-code.

 

(We invited our Director of Security Wayne Crissman to add some commentary in this blog post about security best practices, so keep an eye out for the Security Sidebars!)

 

New call-to-action

 

Getting Started

Part 1 is a prerequisite, since this walkthrough assumes you've completed it. (If you haven't yet, visit the Part 1 repo!)

 

Steps

Here's what we'll do today:

  1. Download ZIP of GitHub repo
  2. Copy files into your repo from part 1
  3. Uncomment terraform apply step
  4. Commit and push changes
  5. Watch the CI/CD magic!

And, there's an optional step:

 

Bring Terraform into compliance to pass Regula check

 

If you'd like a sneak peek at the new files and what they do, jump ahead to step 3a.

 

Step 1: Download example repo ZIP

Once again, we've conveniently created a GitHub repo (example-tf-circleci-part-2) with all the code you need. Download the ZIP here.

 

Step 2: Copy files into your repo

Unzip the archive and copy the following files into the root of the repo you set up in part 1:

 

cp -R .circleci main.tf regula-check.sh staging ../path-to-your-repo

 

We'll go over what each file does shortly in step 3a.

 

Step 3: Uncomment terraform apply step

Action recommended (but not required)! We've commented out the terraform apply step in .circleci/config.yml again just to be on the safe side, because this example will deploy infrastructure into your account (a VPC, security group, and if you follow step 6 later, the resources listed here). Feel free to comment it back in when you're comfortable doing so -- it's line 104 this time.

 

If you'd rather, however, you can keep the line commented out -- it's not strictly necessary for the walkthrough, since we're focusing on the Regula part of the pipeline. Just note that you'll really get the full effect of the pipeline when CircleCI deploys your updated infrastructure and Fugue scans it in context with other resources in the environment.

 

Step 3a: Understand the pipeline

Before we go further, let's take a look at the new and updated files.

 

.circleci/config.yml

Our CircleCI workflow has a new job, regula. The job does a few different things:

  1. Checks out the repo
  2. Installs several binaries:
    1. Terraform
    2. Regula
    3. OPA
    4. Conftest
  3. Pulls the Regula lib, rules, and Conftest integration into Conftest
  4. Runs Regula
  5. Persists the files to the next job

Here's where the cool stuff happens. Regula checks all of the Terraform projects in directories specified in regula-check.sh, evaluating each one for compliance with a library of rules.

 

Then, one of the following things happens:

  • - If the Terraform configuration files pass the compliance rules in Regula's library, the build continues to the next step, ultimately allowing Terraform to deploy the infrastructure, and Fugue to scan your environment and set a baseline.
  • - If Regula finds any compliance violations, the build fails. No Terraform is deployed, and Fugue does not scan the environment or update the baseline.

Because Regula uses Conftest to run the tests, we can validate multiple files at once, and we end up with some nicely formatted output. (You can see for yourself in step 5!)

 

We've also modified the workflows section of config.yml. This time, regula is the first step, and the next step, terraform-init, only happens if regula passes.

 

regula-check.sh

We've added a script, regula-check.sh. The script carries out several tasks:

  1. Specifies the Terraform projects to run Regula against
  2. Defines a function to run terraform commands silently, for cleaner output
  3. Temporarily renames the backend.tf file (see note)
  4. Generates a JSON plan for each Terraform project
  5. Returns the backend file to its original name
  6. Runs Conftest, which runs Regula on all JSON plans

main.tf (root)

The main.tf file in the root of the repo still defines a VPC and security group, but it also defines a VPC flow log, which is currently commented out. Without a VPC flow log, the Terraform file violates the Center for Internet Security AWS Foundations Benchmark (CIS AWS). CIS AWS 2-9 recommends that you "Ensure VPC flow logging is enabled in all VPCs."

 

You can view the Rego policy for this rule in the Regula repo.

 

The file defines some other resources to support the flow log:

  1. A CloudWatch log group
  2. An IAM role for publishing flow logs to CloudWatch Logs (see AWS docs)
  3. An IAM policy for the role

SECURITY SIDEBAR: Why is this a good idea? Because flow logs capture IP traffic information and can be crucial for investigating security incidents after the fact. They can be used to monitor for suspicious activity and trigger alerts or to drive automation that proactively blocks IPs at the start of an attack.

 

main.tf (staging)

The staging folder contains another main.tf, which defines two IAM policies. One of them violates CIS AWS 1-22, "Ensure IAM policies that allow full "*:*" administrative privileges are not created."

 

View the Rego policy for CIS AWS 1-22 in the Regula repo.

 

SECURITY SIDEBAR: Allowing full "*:*" access is a least privilege anti-pattern. There may be legitimate reasons why "*:*" is appropriate but they should be considered the exception, not the rule.

 

What's the deal with the backend?

Due to a Terraform bug, it's impossible to output a Terraform plan in JSON without first running terraform init. And when terraform init is executed, Terraform detects backend.tf and determines that it requires backend reinitialization. This is not ideal here because it involves setting up the backend configuration again, copying existing state, and so on. We only need to generate a plan for Regula, so unsetting and resetting the S3 backend is unnecessary and undesirable.

 

For this reason, we rename backend.tf to backend.tf.backup in the directories we run Regula in, and when we're done, we return it to the original name. This allows us to simply generate a plan and validate it without affecting the backend.

 

SECURITY SIDEBAR: We've applied some security best practices in the creation of the backend. For example, the KMS key used to encrypt the Terraform state bucket has key rotation enabled, which complies with CIS AWS 2-8, "Ensure rotation for customer created CMKs is enabled." Regula checks this for us, as a matter of fact! (We're continually expanding Regula's library of rules to check for security best practices and compliance controls. We also welcome pull requests from the community, so if you'd like to contribute a rule, send us a PR!)

 

Step 4: Commit and push changes

Now, it's time to stage, commit, and push the changes:

git add .circleci main.tf regula-check.sh staging
git commit -m "Add files for example part 2"
git push

 

This push triggers the CI/CD pipeline, and it doesn't matter what branch you make the commit to -- the Regula step happens whenever a commit is pushed. This is in contrast to the rest of the pipeline, which only occurs on commits to master.

 

The reason we've configured CircleCI to run Regula on every commit is in order to provide earlier notice in case someone has pushed noncompliant Terraform. This way, you'll get the benefit of Regula's pre-deployment checks and be alerted to compliance violations long before merging PRs into master and triggering deployment.

 

Step 5: Watch the CI/CD magic!

Assuming you've kept the same repo and CircleCI configuration as in part 1, navigate to your CircleCI dashboard to see what's happening.

 

CircleCI should be running the regula job:

 

 

Whoops! Looks like our Terraform failed the Regula check:

 

 

If you view the job results, you'll see the following error message:

 

 

FAIL - regula-plan-project.json - regula: rule vpc_flow_log failed for resource aws_vpc.my_fugue_cicd_vpc
FAIL - regula-plan-staging.json - regula: rule iam_admin_policy failed for resource aws_iam_policy.basically_allow_all
--------------------------------------------------------------------------------
PASS: 1/3
WARN: 0/3
FAIL: 2/3

Exited with code exit status 1

 

Because main.tf and staging/main.tf defined noncompliant resources, they failed the Regula check, and therefore the workflow as a whole failed.

 

Let's reflect on this for a second, because it's worth emphasizing: Regula's pre-deployment check just saved us from provisioning noncompliant infrastructure!

 

Step 6 (optional): Bring Terraform into compliance

Now that you've seen what happens when Regula flags a noncompliant resource, you have the option to see what happens when all resources are compliant.

 

Action required! You'll need to make a couple of edits:

  1. In main.tf (root), uncomment lines 24-77 to add the VPC flow log and associated resources.
  2. In staging/main.tf, comment out lines 6-23 to remove the "allow all" IAM policy.

Once that's done, go ahead and stage, commit, and push the changes:

 

git add main.tf staging/main.tf
git commit -m "Bring resources into compliance"
git push

 

Head to the CircleCI dashboard, and you'll see that the Regula check passes this time:

 

 

When you view the job results, you'll see the following message:

 

 

PASS: 3/3
WARN: 0/3
FAIL: 0/3

CircleCI received exit code 0

 

Congratulations! Once again, Regula has checked your infrastructure-as-code, and this time the resources have passed the library of rules.

 

The build goes on to the next job, terraform-init, and will continue to the hold-for-approval step. At this point, you can manually approve the deployment and allow Terraform to apply it. Then, Fugue kicks off a scan to validate the new and existing infrastructure, and sets a baseline to facilitate drift detection.

 

Even though Regula is able to catch compliance violations before they happen, Fugue provides an extra layer of protection by checking for compliance violations in context of existing infrastructure.

 

What's Next?

Be sure to visit the example-tf-circleci-part-2 repo, which contains all of the code for this part of the walkthrough.

 

If you missed it the first time, check out example-tf-circleci, which has the code for part 1.

 

We'll continue to add more rules to Regula, so bookmark the repo and keep checking back!

 

Don't forget -- if you'd like to sign up for a free Enterprise trial, or a free-forever Developer account, register here.

 

Finally, for more about Fugue, see the Fugue docs and fugue.co.

 

Director of Security, Wayne Crissman, contributed to this blog.

 

New call-to-action

 

Categorized Under

regula CI/CD

Secure Your Cloud

Find Security and compliance violations in your cloud infrastructure and ensure they never happen again.