Scanning Terraform Code with Checkov: Comprehensive Guide

scan terraform code using checkov

Learn how to enhance your Terraform code’s security by using Checkov for scanning. Our step-by-step guide walks you through the process, ensuring your infrastructure is secure and compliant.

As we discuss the growth of Infrastructure as Code (IaC), we encounter challenges related to security, quality, and testing, which are also critically important. To address these concerns, we can utilize static code analysis tools like Checkov.

Infrastructure as code

Infrastructure is a part of application development. IaC tools like Terraform and CloudFormation help to create infrastructure through code. Managing infrastructure in the cloud with IaC is more efficient and easy to modify as per the requirement changes.

Terraform is one of the popular IaC tools in the market because of its simple syntax and supports a wide range of cloud platforms, such as AWS, Microsoft Azure, and more. As with any other code development, IaC codes also have vulnerabilities, so they must be rectified before deployment.

In this process, code analysis tools play a role by helping us to identify the vulnerabilities in the early stages of the development. Now in the market, only a few IaC code analysis tools are available. However, Checkov is one of the most efficient and simple tools for static code analysis, specifically designed for IaC.

The need for IAC code analysis

There are many criteria that should be ensured when an IaC code is developed. For example.

  1. One of the common issues in IaC is misconfigurations, such as allowing traffic from all ports and IPs, excessive permissions given for resources, hard coding the secrets, and more
  2. Most organizations have their own coding standards and regulations. When a developer writes a code, it should meet the organization’s criteria. For example, tags, proper descriptions, directory structure, etc.
  3. Compliance is also very important for organizations. It helps to avoid unnecessary resource provisioning in cloud environments. For example, in AWS different types of EC2 instances are available, and each instance’s costs are different. If someone provisions R5d.xLarge instance without the necessity, it will lead to excessive expense.

Checkov Integrate with Terraform

In multiple ways, Checkov can integrate with Terraform.

  1. Checkov IDE plugins are available to help identify errors while writing code.
  2. Checkov can scan Terraform files to identify issues. We can also use the Terraform plan file to execute the scan with all the information.
  3. Automate the Checkov scan with CI/CD tools to validate the Terraform code and maintain consistency. This helps developers get immediate feedback.

Checkov setup

Prerequisites

  1. Python 3
  2. jq
  3. Checkov CLI
  4. IaC language interpreter (Terraform, CloudFormation, etc)

To install the Checkov CLI, use the following command:

pip3 install checkov

Configuring Checkov

To scan a directory, use the following command: -d : IaC root directory

checkov -d terraform-aws/environments/dev/ec2/

To scan a specific file, use this command: -f: file

checkov -f terraform-aws/environments/dev/ec2/main.tf

Scan Terraform Plan

To scan a Terraform plan file and include the related line numbers, you need to install the JSON query:

sudo apt install -y jq

Initalize the terraform code:

terraform init

Redirect the Terraform plan output to a tf.plan file:

terraform plan -var-file=../../../vars/dev/ec2.tfvars -out tfplan.binary

convert the tf.plan file to JSON format:

terraform show -json tfplan.binary | jq > tfplan.json

Now, scan the terraform tf.json file:

checkov -f tfplan.json

Checkov Reports

Check: CKV_AWS_46: "Ensure no hard-coded secrets exist in EC2 user data"
        PASSED for resource: module.ec2.aws_instance.ec2_instance[0]
        File: /tfplan.json:164-184
        Guide: <https://docs.paloaltonetworks.com/content/techdocs/en_US/prisma/prisma-cloud/prisma-cloud-code-security-policy-reference/aws-policies/secrets-policies/bc-aws-secrets-1.html>
Check: CKV_AWS_8: "Ensure all data stored in the Launch configuration or instance Elastic Blocks Store is securely encrypted"       
        FAILED for resource: module.ec2.aws_instance.ec2_instance[0]
        File: /tfplan.json:164-184
        Guide: <https://docs.paloaltonetworks.com/content/techdocs/en_US/prisma/prisma-cloud/prisma-cloud-code-security-policy-reference/aws-policies/aws-general-policies/general-13.html>

Custom Policy

Checkov enables the creation of custom policies to meet specific requirements. Custom policies can be developed using supported languages such as Python and YAML.

Python custom policy: Security groups inbound CIDR should not be public.

from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
from checkov.common.models.enums import CheckResult, CheckCategories

class NonPublicCidrBlockCheck(BaseResourceCheck):
    def __init__(self) -> None:
        name = "Ensure AWS security groups have non-public CIDR blocks"
        id = "CUSTOM_AWS_002"
        supported_resources = ("aws_security_group",)
        categories = (CheckCategories.NETWORKING,)
        guideline = "CIDR blocks in security group rules should not be set to 0.0.0.0/0."
        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources, guideline=guideline)

    def scan_resource_conf(self, conf) -> CheckResult:
       
        ingress_rules = conf.get("ingress")
        if ingress_rules:
            for rule in ingress_rules:
                cidr_blocks = rule.get("cidr_blocks")
                if cidr_blocks and any("0.0.0.0/0" in block for block in cidr_blocks):
                    return CheckResult.FAILED
        return CheckResult.PASSED

non_public_cidr_check = NonPublicCidrBlockCheck()

How to use custom policies?

Here’s an example directory structure:

jenkins-terraform/
├── Jenkinsfile
├── README.md
├── custom_checks
│   ├── SecurityGroupDescription.py
│   ├── SecurityGroupInboundCIDR.py
│   └── __init__.py
├── main.tf
└── variables.tf

The main.tf file:

provider "aws" {
  region = var.aws_region
}

resource "aws_security_group" "instance_sg" {
  name        = "instance-sg"
  #description = "checkov test"

  ingress {
    from_port        = var.from_port
    to_port          = var.to_port
    protocol         = var.protocol
    cidr_blocks      = var.cidr_block
  }
}

The variables.tf file

variable "aws_region" {
  description = "The AWS region to create things in."
  default     = "us-west-2"
}

variable "from_port" {
  type        = number
  default     = 22
  description = "List of starting ports for cidr ingress rules of the EC2 security group."
}

variable "to_port" {
  type        = number
  default     = 22
  description = "List of ending ports for cidr ingress rules of the EC2 security group."
}

variable "protocol" {
  type        = string
  default     = "tcp"
  description = "List of protocols for cidr ingress rules of the EC2 security group."
}

variable "cidr_block" {
  type        = list(string)
  **default     = ["0.0.0.0/0"]**
  description = "List of CIDR blocks for cidr ingress rules of the EC2 security group."
}

To scan the code with a custom policy, use the following command:

checkov -d . --external-checks-dir custom_checks --check CUSTOM_AWS_*

Note: You can also scan with Terraform plan.

Checkov Report

terraform scan results:

Passed checks: 0, Failed checks: 2, Skipped checks: 0

Check: CUSTOM_AWS_001: "Ensure AWS security groups have descriptions"
	FAILED for resource: aws_security_group.instance_sg
	File: /main.tf:5-15
	Guide: Security groups should have meaningful descriptions for better understanding.

		5  | resource "aws_security_group" "instance_sg" {
		6  |   name        = "instance-sg"
		7  |   #description = "checkov test"
		8  | 
		9  |   ingress {
		10 |     from_port        = var.from_port
		11 |     to_port          = var.to_port
		12 |     protocol         = var.protocol
		13 |     cidr_blocks      = var.cidr_block
		14 |   }
		15 | }

Check: CUSTOM_AWS_002: "Ensure AWS security groups have non-public CIDR blocks"
	FAILED for resource: aws_security_group.instance_sg
	File: /main.tf:5-15
	Guide: CIDR blocks in security group rules should not be set to 0.0.0.0/0.

		5  | resource "aws_security_group" "instance_sg" {
		6  |   name        = "instance-sg"
		7  |   #description = "checkov test"
		8  | 
		9  |   ingress {
		10 |     from_port        = var.from_port
		11 |     to_port          = var.to_port
		12 |     protocol         = var.protocol
		13 |     cidr_blocks      = var.cidr_block
		14 |   }
		15 | }

Suppressing and skipping policies

Skip the custom policy check (Include the comment inside the code)

provider "aws" {
  region = var.aws_region
}

resource "aws_security_group" "instance_sg" {
  # checkov:skip=CUSTOM_AWS_001: ADD REASON
  name        = "instance-sg"
}

Output:

terraform scan results:

Passed checks: 0, Failed checks: 0, Skipped checks: 1

Check: CUSTOM_AWS_001: "Ensure AWS security groups have descriptions"
        SKIPPED for resource: aws_security_group.instance_sg
        Suppress comment:  ADD REASON
        File: /main.tf:5-8
        Guide: Security groups should have meaningful descriptions for better understanding.

To skip a check with a specific ID:

checkov -d terraform-aws/environments/dev/ec2/ --skip-check CKV2_AWS_5

Hard and Soft fail

These arguments will provide an error code of either 0 or 1 based on the scanning. This feature is very helpful when integrated with the CI/CD pipeline. Here, 0 means pass, and 1 means fail, If the scan results in an error code of 1, the pipeline will halt; if it receives 0, the provisioning will continue. These arguments can be paired up with policy IDs and severity levels.

—soft-fail

This argument will always give the error code 0 with the scan results.

checkov -d . --soft-fail 

—soft-fail-on

This argument will return an error code 0 if any failed checks match the list.

checkov -d . --soft-fail-on LOW,CKV_AWS_46

It will return an error code of 1 if any non-listed checks fail.

—hard-fail-on

This argument will return an error code of 1 if any failed checks match the list.

checkov -d . --hard-fail-on HIGH,CKV_AWS_8,CRITICAL,MEDIUM

It will return an error code 0 if any non-listed checks fail.

Important CLI commands

To check the code with a specific framework:

checkov -d terraform-aws/environment/dev/ec2 --framework terraform

To obtain the output with severity levels:

checkov -d terraform-aws/environments/dev/ec2/ --bc-api-key MY_API_KEY

To view the output with severity levels, you need to create an API key from Bridgecrew

To get the output with a specific severity level:

checkov -d terraform-aws/environments/dev/ec2/ --check HIGH --bc-api-key MY_API_KEY

To scan the code with a specific ID:

checkov -d terraform-aws/environments/dev/ec2/ --check CKV_AWS_46

To get the output in a specific format:

checkov -d terraform-aws/environments/dev/ec2/ --check CKV_AWS_46 --output json

Supports more output formats, such as JSON, CSV, JUnit XML, sarif, spdx, and more.

To learn more about the CLI arguments. please refer to the official documentation.

Checkov Config File

To avoid long Checkov CLI commands, we can use a Checkov configuration file to store all the scan arguments. We can also reuse it for multiple scans and make modifications, which can help avoid duplicating commands.

Create a configuration file. The configuration file supports the YAML format.

checkov -f tfplan.json --external-checks-dir ../../../custom_checks/ --check CUSTOM_AWS_* --create-config ./checkov.yml

This will automatically create the configuration file, checkov.yml .

block-list-secret-scan: []
branch: master
check:
- CUSTOM_AWS_*
download-external-modules: false
evaluate-variables: true
external-checks-dir:
- ../../../custom_checks/
external-modules-download-path: .external_modules
file:
- - tfplan.json
framework:
- all
mask: []
secrets-history-timeout: 12h
secrets-scan-file-type: []
summary-position: top

And the next time, you only have to mention the configuration file to scan the code.

checkov --config-file ./checkov.yml

The Checkov GUI output.

Conclusion

Combining these tools simplifies safer infrastructure provisioning in cloud environments, we can simply use this tool to scan the Terraform code or can integrate with CI/CD tools to automate the scanning process.

I recommend integrating Checkov with your IaC code creation to reduce the risk and improve compliance.

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like