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.
- 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
- 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.
- 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.
- Checkov IDE plugins are available to help identify errors while writing code.
- Checkov can scan Terraform files to identify issues. We can also use the Terraform plan file to execute the scan with all the information.
- 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
- Python 3
- jq
- Checkov CLI
- 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.