ScaleSec Blog

TDD in Your Infrastructure Pipeline | ScaleSec

Written by Eric Evans | Nov 5, 2020 8:00:00 AM

TDD in Your Infrastructure Pipeline

The Parts

In the last two parts of this series, we have described what Test Driven Development (TDD) is, and how it can be applied to infrastructure to validate that security controls are implemented. Generally, a TDD pipeline consists of a number of components:


Part 1: Getting Started with TDD in AWS Part 2: Test Driven Development for Secure Infrastructure

The purpose of this article is to put all of these parts together in a pipeline.

Choosing a Framework

Before starting to run (no pun intended) with TDD, choosing a proper framework should be top of mind. There are several drivers when it comes to choosing a TDD framework. The table below outlines these decisions relating to TDD for Infrastructure.

  Chef Inspec HashiCorp Sentinel Terraform Validator Open Policy Agent (OPA)
Price Open Source Enterprise-only Open Source Open Source
Cloud(s) AWS, Azure, GCP AWS, Azure, GCP GCP only AWS, Azure, GCP
Language(s) Ruby Sentinel YAML/Rego via Policy Library Rego
Limited to Infrastructure? Applications and OS-level support Works with Vault, Consul, and Nomad Only Infrastructure Many applications

A Comparison of Testing Frameworks for Infrastructure as Code

In this article, we will be using Chef Inspec. However, know that the steps to implement these test frameworks are similar.

Setting up the Code

One of the main value propositions for using TDD for IaC is to ensure that security requirements are clearly defined. Tests should be written first, then the infrastructure code should be built to let tests pass. The following diagram outlines the recommended steps to ensure success with setting up testable infrastructure code.

Steps for a Successful Translation of Requirements into Code

Security and compliance requirements start from business drivers. Meeting requirements highlighted by demonstrating compliance with regimes such as FedRAMP, GDPR, PCI-DSS, and HITRUST help break barriers to market entrance. What this means is that there should be good communication between compliance teams and the engineers that implement infrastructure. Having sessions for engineering teams to translate security requirements into tests (that may not yet pass since the infrastructure hasn’t been created/modified yet) using a selected framework is key to ensuring success.

Implementing Successful Version Control Patterns

Blocking merges before test pass in GitHub

Once tests are written, they should be checked into version control. This helps to answer questions regarding auditing, technical decisions, and compliance status of infrastructure components during development. There are numerous ways to achieve proper Git version control for infrastructure tests including: GitFlow, GitHub flow, GitLab flow, OneFlow, and Trunk-based development. The choice on what version control pattern varies depending on organization and project - and is outside of the scope of this article, however having a pattern that can enforce security checks (e.g. blocking merges to main/feature branches before tests pass) is important to success with Test-driven infrastructure.

Running with it!

In order to run tests and provide feedback effectively, some sort of system that can periodically or upon request, execute a test suite is essential. To achieve this, a system is typically implemented using a continuous integration (CI) system due to the vast ecosystem that many of them have as well as community support (and thus, ease of use).

GitHub Actions

GitHub is an excellent platform for projects that leverage the Git versioning system. Making this platform even better is the ability to perform actions depending on what happens within a particular version control pattern (such as merging branches, opening a pull request, and so on). To perform tests using Chef Inspec within GitHub Actions, the following code snippet can be used.

name: Testing
# Triggers the workflow on push or pull request
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
# Test Job
test:
needs: build
if: github.ref != 'refs/heads/main'
runs-on: ubuntu-latest
# Setup environment
env:
CHEF_LICENSE: accept-silent # silently accept the Chef InSpec license
steps:
# setup python
- uses: actions/checkout@v2
# pull credentials
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
with:
version: '290.0.1'
project_id: tdd-testing-environment
service_account_key: $
export_default_credentials: true
- name: "Install Chef Inspec"
run: curl https://omnitruck.chef.io/install.sh | sudo bash -s -- -P inspec
- name: "Run Infrastructure Tests"
run: inspec exec test/my-profile --input-file test/my-profile/attributes.yml --reporter=cli html:./report.html --show-progress -t gcp://
- uses: actions/upload-artifact@v2
if: always()
with:
name: test-report
path: ./report.html

Code to Run Chef Inspec Tests with GitHub Actions

The Whole Picture

Using a pipeline that is well defined with business objectives in mind can be an effective way to ensure security in any infrastructure project that is defined with infrastructure as code. Using a test-driven approach ensures that security requirements are properly communicated from security/compliance teams to engineering teams for proper implementation in cloud environments. Using TDD is a large upfront investment that requires writing tests and establishing automation to be effective, but ultimately allows teams to move faster and more securely.