With CircleCI, there are many different CI/CD flows that can be automated. One such flow is the use of Infrastructure-as-Code (IaC) to build cloud environments. For example, you can use CircleCI to automate the process of building Terraform plans and applying them, in order to create massive production setups in AWS, Azure, GCP, and other cloud environments.

Being able to scan IaC before your environment is built is of enormous benefit. For example, you can check your code to see if the infrastructure will comply with your cloud security requirements. This allows you to catch security vulnerabilities earlier in the pipeline, when they are easier and more cost-effective to address. It is a concept known as “Shift Left” testing. In this post, we will take a closer look at how to use Indeni Cloudrail, an IaC security analysis tool, within CircleCI to implement Shift Left testing.

Prerequisites

  • GitHub account with Terraform code, like this example
  • A CircleCI account
  • Cloud account (for this example we will use an AWS account)
  • Cloudrail account, with the API key, and your cloud account added

In our example here, we will use a bit of Terraform code that builds an RDS database that is exposed to the public Internet. Obviously, that is a big no-no, so I will show how you can catch this kind of mistake by including Cloudrail in your pipeline. The Terraform code we will select is available here.. You can use any Terraform code sample you want to, of course.

Setting up the CircleCI configuration for Terraform

Our first step is adding a Terraform plan to your CircleCI workflow. To begin, we will use a CircleCI configuration file that creates a Terraform plan. This plan lists the resources that Terraform will create in your AWS account:

version: 2.1
workflows:
  main:
    jobs:
      - tf_plan
jobs:
  tf_plan:
    working_directory: /tmp/test_tf
    docker:
      - image: hashicorp/terraform:0.13.5
    steps:
      - checkout
      - run:
          name: terraform init & plan
          command: |
            cd test/aws/terraform/public_access_db_rds/individual-instance/vpc-controlled-public
            terraform init -input=false
            terraform plan -out plan.out

For the Terraform plan to work successfully, you need to set environment variables that provide access to AWS: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. If you try running your pipeline now, the job should complete successfully, with the output detailing what Terraform is going to build.

CircleCI Terraform init & plan

Adding Cloudrail as a job in the workflow

It is time to add the Cloudrail job into the workflow. The goal here is for Cloudrail to inspect the plan and see if anything that is being built is not in line with your organization’s policy. This is what the CircleCI configuration looks like now:

version: 2.1

# For more details about how to use this Orb, please see:
# https://circleci.com/developer/orbs/orb/indeni/cloudrail
orbs:
  cloudrail: indeni/cloudrail@2.0.2

workflows:
  main:
    jobs:
      # Cloudrail requires a Terraform plan as an input, so we must create a plan first.
      - tf_plan
      - security_check_terraform:
          requires:
            - tf_plan

jobs:
  tf_plan:
    working_directory: /tmp/test_tf
    docker:
      # The example TF code we use here only works in v0.13, however Cloudrail supports 0.14 as well.
      - image: hashicorp/terraform:0.13.5
    steps:
      - checkout
      - run:
          name: terraform init & plan
          command: |
            cd test/aws/terraform/public_access_db_rds/individual-instance/vpc-controlled-public
            terraform init -input=false
            terraform plan -out plan.out
      # This persistence is important in order to pass the plan.out to Cloudrail
      - persist_to_workspace:
          root: .
          paths:
            - .
  security_check_terraform:
    executor: cloudrail/default
    steps:
      # This loads the plan.out file:
      - attach_workspace:
          at: .
      - run: # Tests must be in a sub directory, per https://support.circleci.com/hc/en-us/articles/360021624194-Test-summary-troubleshooting
          name: Create test result directory (if not exists)
          command: |
            mkdir test_results
      # This will run Cloudrail and produce Junit test results. The idea is that if there are any rules
      # that are set to MANDATE, and they find violations, we will have "failed" tests in the Junit output.
      # This will then cause CircleCI to stop the pipeline and list the failed tests, allowing dev's to fix
      # the violations.
      # Note that Cloudrail has other output formats as well, please see the Orb documentation for more information.
      # Also note that rules that are set to ADVISE (which is the default) will _not_ be included in the output by default.
      - cloudrail/scan_terraform_junit:
          cloud-account-id: $AWS_ACCOUNT_ID
          cloudrail_api_key: $CLOUDRAIL_API_KEY
          plan_output_file: test/aws/terraform/public_access_db_rds/individual-instance/vpc-controlled-public/plan.out
          tf_directory: test/aws/terraform/public_access_db_rds/individual-instance/vpc-controlled-public
          junit-output-file: test_results/cloudrail-junit.xml
      - store_test_results:
          path: test_results
      - store_artifacts:
          path: test_results/cloudrail-junit.xml

You may notice two additional environment variables have been added here:

  • CLOUDRAIL_API_KEY is the API key you received from Cloudrail when you opened an account
  • AWS_ACCOUNT_ID is the ID of the AWS account you are targeting with this Terraform workflow

Now run the workflow again in CircleCI, and Cloudrail runs successfully, with the summary of results showing. All violations are considered warnings at this phase, because we have yet to set any rules to Mandate. The Mandate setting tells Cloudrail to stop the pipeline if the specific rule is violated, which forces the developer to fix the issue before proceeding.

Analyze Terraform with Cloudrail

As you can see, when Cloudrail is first used, it does not break the workflow. Instead, it looks at what is going through and generates assessments, without stopping anything. That is great at first, but to get developers to actually fix things, you need to set mandated rules.

Enforcing security controls with Cloudrail

This section covers viewing assessments in Cloudrail and modifying enforcement configuration. The output of Cloudrail within CircleCI contains a link to the assessment in Cloudrail. Click that link.

Cloudrail assessment

Cloudrail found a few violations. These are all considered warnings right now, because the rules are set to “advise”. Cloudrail can recommend which rules we should set to “mandate”.

Cloudrail add rule to policy

Now that we have created a policy, and set some of the rules to mandate, we can go ahead and run our workflow once again.

Interacting with Cloudrail’s output in CircleCI

Jumping back to the CircleCI console, run the workflow again. This time, the Cloudrail job failed.

Failed Cloudrail job

It failed because Cloudrail found violations of rules that were set to Mandate. That is the goal of Cloudrail: to stop resources from being provisioned if they violate your company’s security policy. When a failure like this occurs, CircleCI notifies the developer that the pipeline failed, and points them to the specific resource that failed Cloudrail’s scan.

With Cloudrail’s JUnit output and CircleCI integration, the developer can easily find out what failed, by clicking on the Test tab. Here, the developer can see what failed, why, and instructions on how to fix the issue.

Test tab view - test failure

Once they fix the issue, the developer can push their change and trigger the workflow once again. This time, the Terraform plan will pass, and the workflow will complete successfully.

Conclusion

Infrastructure-as-code brings new opportunities for development teams to run faster and ship more resilient environments. Cloud technologies help make this happen, but also expose critical user data to potential hackers and bad actors. With CircleCI and Cloudrail, development teams can make sure that their infrastructure is deployed in a predictable and consistent manner, without potential vulnerabilities. This allows developers to maintain speed while increasing the security of the applications they build.

Yoni Leitersdorf is the founder and CEO of Indeni, a leading security infrastructure automation company. Ideni has a new solution for cloud security analysis known as Cloudrail, which integrates with CircleCI. To learn more about how CircleCI customers can use Indeni to scan infrastructure-as-code files and enforce security policies by stopping the pipelines when necessary, read the latest news release.