By: Matt Ray (@mattray)
Edited by: Ninad Pundalik (@ni_nad)
Compliance as Code
By now you’re probably familiar with the idea of “Infrastructure as Code” where we define how our servers and related infrastructure are configured in code that can be versioned, stored in source control, and tested in CI/CD environments. Tools such as Chef, Ansible, Puppet, CloudFormation, and Terraform have popularized this concept. But how do we know if our applications and infrastructure are secure and compliant? How can we manage this as code with all the same benefits (versioned, tested, easy to extend)?
InSpec is an open source “Compliance as Code” framework for defining and auditing your infrastructure’s compliance and security in a human and machine-readable language. You define your requirements in a high-level language built on top of Ruby and run them as automated tests against your systems. InSpec works with servers, containers, databases, applications, cloud APIs, and can easily be extended to support new auditing targets. InSpec can scan systems remotely or locally as an agent and may be used in conjunction with “Infrastructure as Code” tools to test the correctness of their deployments.
Terraform & InSpec-Iggy
The open source project Terraform allows us to define how our infrastructure is defined and provisioned across a wide variety of cloud platforms. InSpec-Iggy (InSpec Generate -> “IG” -> “Iggy”) is an InSpec plugin for generating compliance controls and profiles from Terraform .tfstate
files and AWS CloudFormation templates. While both CloudFormation and Terraform are supported by Iggy, this post will focus on Terraform.
From Terraform’s source files, we know the intended infrastructure and the .tfstate
file provides the last known state of what was deployed. By generating InSpec coverage from the .tfstate
file, Iggy allows us to test that what we built with Terraform, and check whether resources have been modified or drifted from the initial deployment. Anytime we deploy a new infrastructure with Terraform, we can create new tests to audit for fidelity.
Iggy & AWS
The InSpec-Iggy README covers installing the plugin with InSpec and documents the various subcommands, their options, and development and testing. The Terraform AWS Provider Basic Two-Tier AWS Architecture produces 9 AWS resources with the command:
$ terraform apply
The terraform.tfstate
file is the JSON representing the state of the infrastructure created from the above command. InSpec-Iggy parses the file with the following command:
$ inspec terraform generate --name AWS01 -t terraform.tfstate --platform aws --resourcepath ~/ws/inspec-aws/
───────────────────────── InSpec Iggy Code Generator ─────────────────────────
Creating new profile at /Users/mattray/ws/inspec-iggy/AWS01
• Creating file README.md
• Creating directory controls
• Creating file controls/generated.rb
• Creating file inspec.yml
Additional options such as --title
and --version
may be passed to populate the README.md
and inspec.yml
as necessary. Looking at the AWS01
profile that has been generated, we see the controls/generated.rb
contains the following content:
control "aws_elb::terraform-example-elb" do
title "InSpec-Iggy aws_elb::terraform-example-elb"
desc "
aws_elb::terraform-example-elb from the source file /Users/mattray/ws/inspec-iggy/terraform.tfstate
Generated by InSpec-Iggy v0.7.0
"
impact 1.0
describe aws_elb({:load_balancer_name=>"terraform-example-elb"}) do
it { should exist }
its("availability_zones") { should cmp ["us-west-2c"] }
its("dns_name") { should cmp "terraform-example-elb-2051343015.us-west-2.elb.amazonaws.com" }
its("load_balancer_name") { should cmp "terraform-example-elb" }
end
end
...
The generated profile leverages the inspec-aws resource pack and running inspec exec AWS01 -t aws://us-west-2
with the proper AWS credentials produces:
Profile: InSpec Profile (AWS01)
Version: 0.1.0
Target: aws://us-west-2
✔ aws_elb::terraform-example-elb: InSpec-Iggy aws_elb::terraform-example-elb
✔ AWS ELB terraform-example-elb should exist
✔ AWS ELB terraform-example-elb availability_zones should cmp == ["us-west-2c"]
✔ AWS ELB terraform-example-elb dns_name should cmp == "terraform-example-elb-2051343015.us-west-2.elb.amazonaws.com"
✔ AWS ELB terraform-example-elb load_balancer_name should cmp == "terraform-example-elb"
✔ aws_ec2_instance::i-05c6d20469a0a0ee9: InSpec-Iggy aws_ec2_instance::i-05c6d20469a0a0ee9
✔ EC2 Instance i-05c6d20469a0a0ee9 should exist
✔ EC2 Instance i-05c6d20469a0a0ee9 availability_zone should cmp == "us-west-2c"
✔ EC2 Instance i-05c6d20469a0a0ee9 subnet_id should cmp == "subnet-00f265d40d3d0a227"
✔ aws_security_group::sg-0eac4a147658285b7: InSpec-Iggy aws_security_group::sg-0eac4a147658285b7
✔ EC2 Security Group ID: sg-0eac4a147658285b7 Name: terraform_example VPC ID: vpc-035d7e339ce59ad62 should exist
✔ EC2 Security Group ID: sg-0eac4a147658285b7 Name: terraform_example VPC ID: vpc-035d7e339ce59ad62 description should cmp == "Used in the terraform"
✔ EC2 Security Group ID: sg-0eac4a147658285b7 Name: terraform_example VPC ID: vpc-035d7e339ce59ad62 group_name should cmp == "terraform_example"
✔ EC2 Security Group ID: sg-0eac4a147658285b7 Name: terraform_example VPC ID: vpc-035d7e339ce59ad62 vpc_id should cmp == "vpc-035d7e339ce59ad62"
✔ aws_security_group::sg-01199abc1619d7613: InSpec-Iggy aws_security_group::sg-01199abc1619d7613
✔ EC2 Security Group ID: sg-01199abc1619d7613 Name: terraform_example_elb VPC ID: vpc-035d7e339ce59ad62 should exist
✔ EC2 Security Group ID: sg-01199abc1619d7613 Name: terraform_example_elb VPC ID: vpc-035d7e339ce59ad62 description should cmp == "Used in the terraform"
✔ EC2 Security Group ID: sg-01199abc1619d7613 Name: terraform_example_elb VPC ID: vpc-035d7e339ce59ad62 group_name should cmp == "terraform_example_elb"
✔ EC2 Security Group ID: sg-01199abc1619d7613 Name: terraform_example_elb VPC ID: vpc-035d7e339ce59ad62 vpc_id should cmp == "vpc-035d7e339ce59ad62"
✔ aws_subnet::subnet-00f265d40d3d0a227: InSpec-Iggy aws_subnet::subnet-00f265d40d3d0a227
✔ VPC Subnet subnet-00f265d40d3d0a227 should exist
✔ VPC Subnet subnet-00f265d40d3d0a227 availability_zone should cmp == "us-west-2c"
✔ VPC Subnet subnet-00f265d40d3d0a227 cidr_block should cmp == "10.0.1.0/24"
✔ VPC Subnet subnet-00f265d40d3d0a227 vpc_id should cmp == "vpc-035d7e339ce59ad62"
✔ aws_vpc::vpc-035d7e339ce59ad62: InSpec-Iggy aws_vpc::vpc-035d7e339ce59ad62
✔ VPC vpc-035d7e339ce59ad62 should exist
✔ VPC vpc-035d7e339ce59ad62 cidr_block should cmp == "10.0.0.0/16"
✔ VPC vpc-035d7e339ce59ad62 dhcp_options_id should cmp == "dopt-8d3211eb"
✔ VPC vpc-035d7e339ce59ad62 instance_tenancy should cmp == "default"
Profile: Amazon Web Services Resource Pack (inspec-aws)
Version: 1.4.2
Target: aws://us-west-2
No tests executed.
Profile Summary: 6 successful controls, 0 control failures, 0 controls skipped
Test Summary: 23 successful, 0 failures, 0 skipped
This test can be run periodically for drift detection and to validate that our infrastructure is still configured as deployed by Terraform.
Negative Testing
What if we wanted to see all of the infrastructure that is not managed by Terraform? This “negative testing” can be used to find anything that may have been added to a particular VPC that was not created by Terraform, which may be a security issue or unwanted manual change. inspec terraform negative
will generate a profile that pulls the available cloud resources from the VPC and reports failures for those that were not provided by the terraform.tfstate
.
Visualized Reporting
InSpec allows you to send your audit reports directly to Automate for visualization and reporting.
Wrapping Up
Automatically creating InSpec tests for your Terraform-managed infrastructure provides auditing to ensure your infrastructure is always managed as code. InSpec-Iggy has been tested with AWS, Azure, and GCP, but other platforms could easily be supported. Iggy is still under development and as new cloud resources are added to the corresponding resource packs additional coverage will automatically be generated.
No comments:
Post a Comment