Azure DevOps and Chechov

In this post we demonstrate how to use the open source security and compliance tool called Checkov with Azure DevOps to verify your Azure infrastructure is secure.

Introducing Checkov

Checkov is a great tool for engineering teams to harness as part of their Cloud environment deployments.

https://www.checkov.io/

Checkov currently supports scanning the following scanning capabilities:

  • Terraform (for AWS, GCP, Azure and OCI)
  • CloudFormation (including AWS SAM)
  • Azure Resource Manager (ARM)
  • Serverless framework
  • Helm charts
  • Kubernetes
  • Docker

Setup

In this article we demonstrate using Checkov with Terraform code on Azure.

We will be using brew on mac to install it locally:

brew install checkov

Terraform runs against your terraform code located in a path.

checkov --directory /user/path/to/iac/code

Or against a tfplan file.

terraform init
terraform plan -out tf.plan
terraform show -json tf.plan  > tf.json 
checkov -f tf.json

Terraform code

Lets deploy a web app with VNET integration. Sample code block snippet below (full code not shown)


resource "azurerm_resource_group" "rg" {
  name     = "rg-demoapp-dev-001"
  location = var.location
}

resource "azurerm_virtual_network" "vnet" {
  name                = "vnet"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  address_space       = ["10.0.0.0/16"]
}

resource "azurerm_subnet" "integrationsubnet" {
  name                 = "integrationsubnet"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.1.0/24"]
  delegation {
    name = "delegation"
    service_delegation {
      name = "Microsoft.Web/serverFarms"
    }
  }
}
resource "azurerm_app_service" "backwebapp" {
  name                = "backwebapprom"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  app_service_plan_id = azurerm_app_service_plan.appserviceplan.id
}

Building a Azure pipeline

Now we have a sample Azure Terraform code to deploy. The next step is to use Checkov in a CI/CD pipeline.

What we want to do is use the output Checkov to report the failures in a unit test output format.

In terms of stages we want to visualize something like:

Terraform Validate -> Checkov compliance scan -> Terraform plan

Defining the pipeline

We want to use Azure DevOps unit test feature which can display unit test results. To do this we will use Junit as the output format and the following yaml task:

- task: PublishTestResults@2
        inputs:
          testRunTitle: "Checkov Results"
          failTaskOnFailedTests: true
          testResultsFormat: "JUnit"
          testResultsFiles: "CheckovReport.xml"
          searchFolder: "$(System.DefaultWorkingDirectory)"
        displayName: "Publish > Checkov scan results"

Using this with Checkov will provide us with a really nice test result dashboard as you will see.

Next we define the whole pipeline. In this pipeline we make use of the Chechov docker image to run it on a Azure DevOps build agent.

Complete pipeline:

# Azure Pipeline that run basic continuous integration on a Terraform project

# This makes sure the pipeline is triggered every time code is pushed in the validation-testing example source, on all branches.
trigger:
  branches:
    include:
    - '*'
  paths:
    include:
    - 'src/terraform-azure-webapp'
variables:
  # There must be an Azure Service Connection with that name defined in your Azure DevOps settings. See https://docs.microsoft.com/en-us/azure/devops/pipelines/library/connect-to-azure?view=azure-devops
  serviceConnection: 'terraform-basic-testing-azure-connection'
  azureLocation: 'westeurope'
  # Terraform settings
  terraformWorkingDirectory: '$(System.DefaultWorkingDirectory)/src/terraform-azure-webapp'
  terraformVersion: '1.0.1'

pool:
    vmImage: ubuntu-20.04
stages:
  - stage: Validate
    displayName: Terraform Validate
    jobs:
    - job: Validate
      steps:
       # Step 1: install Terraform on the Azure Pipelines agent
      - task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-installer.TerraformInstaller@0
        displayName: 'Install Terraform'
        inputs:
          terraformVersion: $(terraformVersion)
      # Step 2: run Terraform init to initialize the workspace
      - task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-cli.TerraformCLI@0
        displayName: 'Run terraform init'
        inputs:
          command: init
          workingDirectory: $(terraformWorkingDirectory)
       # Step 3 run Terraform validate 
      - task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-cli.TerraformCLI@0
        displayName: 'Run terraform validate'
        inputs:
          command: validate
          workingDirectory: $(terraformWorkingDirectory)
  - stage: Compliance
    displayName: Checkov compliance scan
    jobs:
    - job: Compliance
      displayName: Checkov compliance scan
      steps:
      - bash: |
              docker run \
                --volume $(pwd):/tf bridgecrew/checkov \
                --directory /tf \
                --output junitxml \
                --soft-fail > $(pwd)/CheckovReport.xml
        workingDirectory: $(System.DefaultWorkingDirectory)
        displayName: "Run > checkov"           
      - task: PublishTestResults@2
        inputs:
          testRunTitle: "Checkov Results"
          failTaskOnFailedTests: true
          testResultsFormat: "JUnit"
          testResultsFiles: "CheckovReport.xml"
          searchFolder: "$(System.DefaultWorkingDirectory)"
        displayName: "Publish > Checkov scan results"
  
      # Step 4: run Terraform validate to validate HCL syntax
  - stage: Plan
    displayName: Terraform Plan
    jobs:
    - job: Plan
      steps:
      - task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-cli.TerraformCLI@0
        displayName: 'init'  
        inputs:
          command: init
          workingDirectory: $(terraformWorkingDirectory)
          environmentServiceName: $(serviceConnection)
      # Step 5: run Terraform plan to validate HCL syntax
      - task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-cli.TerraformCLI@0
        displayName: 'Run terraform plan'
        inputs:
          command: plan
          workingDirectory: $(terraformWorkingDirectory)
          environmentServiceName: $(serviceConnection)
          commandOptions: -var location=$(azureLocation)
          
  - stage: Apply
    displayName: Terraform Apply
    jobs:
    - job: Apply
      steps:
      - task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-cli.TerraformCLI@0
        displayName: 'init'  
        inputs:
          command: init
          workingDirectory: $(terraformWorkingDirectory)
          environmentServiceName: $(serviceConnection)
      # Step 5: run Terraform apply to validate HCL syntax
      - task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-cli.TerraformCLI@0
        displayName: 'Run terraform plan'
        inputs:
          command: apply
          workingDirectory: $(terraformWorkingDirectory)
          environmentServiceName: $(serviceConnection)
          commandOptions: -var location=$(azureLocation)

Seeing the results

Let’s dissect some parts of the pipeline:

Initialisation terraform

We initialize terraform using the following Azure pipeline task:

task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-installer.TerraformInstaller@0
        displayName: 'Install Terraform'
        inputs:
          terraformVersion: $(terraformVersion)

Executing Checkov task

We make use of the Checkov docker container to actually run Checkov. This avoids us installing Checkov directly on each build agent run.

 - stage: Compliance
    displayName: Checkov compliance scan
    jobs:
    - job: Compliance
      displayName: Checkov compliance scan
      steps:
      - bash: |
              docker run \
                --volume $(pwd):/tf bridgecrew/checkov \
                --directory /tf \
                --output junitxml \
                --soft-fail > $(pwd)/CheckovReport.xml
        workingDirectory: $(System.DefaultWorkingDirectory)
        displayName: "Run > checkov"           
      - task: PublishTestResults@2
        inputs:
          testRunTitle: "Checkov Results"
          failTaskOnFailedTests: true
          testResultsFormat: "JUnit"
          testResultsFiles: "CheckovReport.xml"
          searchFolder: "$(System.DefaultWorkingDirecto

The key points from this stage and steps:

  • output the result in JUnit format
  • failTaskOnFailedTests:true – So that the compliance check fails the build

Pipeline run

Once you trigger the pipeline, Checkov should find issues and fail the pipeline:

Test results

Summary

Checkov is a great tool for for shifting security left. Additionally integrating it into Azure pipelines is seamless. You also get the added bonus of a great dashboard of results immediately.

The key takeways:

  • Shift left your security scan of your Cloud infrastructure
  • Use Checkov docker container. No need to install Checkov!!
  • Use the JUnit to output results

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: