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.
- 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_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.
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: firstname.lastname@example.org 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_KEYis the API key you received from Cloudrail when you opened an account
AWS_ACCOUNT_IDis 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 setting tells Cloudrail to stop the pipeline if the specific rule is violated, which forces the developer to fix the issue before proceeding.
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 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”.
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.
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.
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.
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. Indeni 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.