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
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!)
Part 1 is a prerequisite, since this walkthrough assumes you've completed it. (If you haven't yet, visit the Part 1 repo!)
Here's what we'll do today:
- Download ZIP of GitHub repo
- Copy files into your repo from part 1
- Uncomment terraform apply step
- Commit and push changes
- Watch the CI/CD magic!
And, there's an optional step:
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
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.
Our CircleCI workflow has a new job, regula. The job does a few different things:
- Checks out the repo
- Installs several binaries:
- Pulls the Regula lib, rules, and Conftest integration into Conftest
- Runs Regula
- Persists the files to the next job
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.
We've added a script, regula-check.sh. The script carries out several tasks:
- Specifies the Terraform projects to run Regula against
- Defines a function to run terraform commands silently, for cleaner output
- Temporarily renames the backend.tf file (see note)
- Generates a JSON plan for each Terraform project
- Returns the backend file to its original name
- Runs Conftest, which runs Regula on all JSON plans
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:
- A CloudWatch log group
- An IAM role for publishing flow logs to CloudWatch Logs (see AWS docs)
- 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.
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"
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
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:
- In main.tf (root), uncomment lines 24-77 to add the VPC flow log and associated resources.
- 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"
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:
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.
Be sure to visit the example-tf-circleci-part-2 repo, which contains all of the code for this part of the walkthrough.
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.
Director of Security, Wayne Crissman, contributed to this blog.