{"id":832,"date":"2023-10-15T14:03:00","date_gmt":"2023-10-15T14:03:00","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=832"},"modified":"2023-10-15T14:03:00","modified_gmt":"2023-10-15T14:03:00","slug":"terraform-checkov-scan","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=832","title":{"rendered":"Scanning Terraform Code with Checkov: Comprehensive Guide"},"content":{"rendered":"<p>Learn how to enhance your Terraform code&#8217;s security by using Checkov for scanning. Our step-by-step guide walks you through the process, ensuring your infrastructure is secure and compliant.<\/p>\n<p>As we discuss the growth of <a href=\"https:\/\/devopscube.com\/infrastructure-as-code-configuration-management\/\">Infrastructure as Code (IaC)<\/a>, 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.<\/p>\n<h2 id=\"infrastructure-as-code\">Infrastructure as code<\/h2>\n<p>Infrastructure is a part of application development. <a href=\"https:\/\/devopscube.com\/devops-tools-for-infrastructure-automation\/\">IaC tools<\/a> 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.<\/p>\n<p>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.<\/p>\n<p>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.<\/p>\n<h2 id=\"the-need-for-iac-code-analysis\">The need for IAC code analysis<\/h2>\n<p>There are many criteria that should be ensured when an IaC code is developed. For example.<\/p>\n<ol>\n<li>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<\/li>\n<li>Most organizations have their own coding standards and regulations. When a developer writes a code, it should meet the organization&#8217;s criteria. For example, tags, proper descriptions, directory structure, etc.<\/li>\n<li>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&#8217;s costs are different. If someone provisions <code>R5d.xLarge<\/code> instance without the necessity, it will lead to excessive expense.<\/li>\n<\/ol>\n<h2 id=\"checkov-integrate-with-terraform\">Checkov Integrate with Terraform<\/h2>\n<p>In multiple ways, Checkov can integrate with Terraform.<\/p>\n<ol>\n<li>Checkov IDE plugins are available to help identify errors while writing code.<\/li>\n<li>Checkov can scan Terraform files to identify issues. We can also use the Terraform plan file to execute the scan with all the information.<\/li>\n<li>Automate the Checkov scan with CI\/CD tools to validate the Terraform code and maintain consistency. This helps developers get immediate feedback.<\/li>\n<\/ol>\n<h2 id=\"checkov-setup\">Checkov setup<\/h2>\n<h3 id=\"prerequisites\"><strong>Prerequisites<\/strong><\/h3>\n<ol>\n<li><strong>Python 3<\/strong><\/li>\n<li><strong>jq<\/strong><\/li>\n<li><strong>Checkov CLI<\/strong><\/li>\n<li><strong>IaC language interpreter<\/strong> (Terraform, CloudFormation, etc)<\/li>\n<\/ol>\n<p>To install the Checkov CLI, use the following command:<\/p>\n<pre><code>pip3 install checkov<\/code><\/pre>\n<h3 id=\"configuring-checkov\">Configuring Checkov<\/h3>\n<p>To scan a directory, use the following command: <code>-d : IaC root directory<\/code><\/p>\n<pre><code>checkov -d terraform-aws\/environments\/dev\/ec2\/<\/code><\/pre>\n<p>To scan a specific file, use this command: <code>-f: file<\/code><\/p>\n<pre><code>checkov -f terraform-aws\/environments\/dev\/ec2\/main.tf<\/code><\/pre>\n<h3 id=\"scan-terraform-plan\">Scan Terraform Plan<\/h3>\n<p>To scan a Terraform plan file and include the related line numbers, you need to install the JSON query:<\/p>\n<pre><code>sudo apt install -y jq<\/code><\/pre>\n<p>Initalize the terraform code:<\/p>\n<pre><code>terraform init<\/code><\/pre>\n<p>Redirect the Terraform plan output to a <code>tf.plan<\/code> file:<\/p>\n<pre><code>terraform plan -var-file=..\/..\/..\/vars\/dev\/ec2.tfvars -out tfplan.binary<\/code><\/pre>\n<p>convert the <code>tf.plan<\/code> file to JSON format:<\/p>\n<pre><code>terraform show -json tfplan.binary | jq &gt; tfplan.json<\/code><\/pre>\n<p>Now, scan the terraform <code>tf.json<\/code> file:<\/p>\n<pre><code>checkov -f tfplan.json<\/code><\/pre>\n<h3 id=\"checkov-reports\"><strong>Checkov Reports<\/strong><\/h3>\n<pre><code>Check: CKV_AWS_46: \"Ensure no hard-coded secrets exist in EC2 user data\"\n        PASSED for resource: module.ec2.aws_instance.ec2_instance[0]\n        File: \/tfplan.json:164-184\n        Guide: &lt;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&gt;\n<\/code><\/pre>\n<pre><code>Check: CKV_AWS_8: \"Ensure all data stored in the Launch configuration or instance Elastic Blocks Store is securely encrypted\"       \n        FAILED for resource: module.ec2.aws_instance.ec2_instance[0]\n        File: \/tfplan.json:164-184\n        Guide: &lt;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&gt;\n<\/code><\/pre>\n<h2 id=\"custom-policy\">Custom Policy<\/h2>\n<p>Checkov enables the creation of custom policies to meet specific requirements. Custom policies can be developed using supported languages such as Python and YAML.<\/p>\n<h3 id=\"python-custom-policy-security-groups-inbound-cidr-should-not-be-public-\">Python custom policy: Security groups inbound CIDR should not be public.<\/h3>\n<pre><code>from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck\nfrom checkov.common.models.enums import CheckResult, CheckCategories\n\nclass NonPublicCidrBlockCheck(BaseResourceCheck):\n    def __init__(self) -&gt; None:\n        name = \"Ensure AWS security groups have non-public CIDR blocks\"\n        id = \"CUSTOM_AWS_002\"\n        supported_resources = (\"aws_security_group\",)\n        categories = (CheckCategories.NETWORKING,)\n        guideline = \"CIDR blocks in security group rules should not be set to 0.0.0.0\/0.\"\n        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources, guideline=guideline)\n\n    def scan_resource_conf(self, conf) -&gt; CheckResult:\n       \n        ingress_rules = conf.get(\"ingress\")\n        if ingress_rules:\n            for rule in ingress_rules:\n                cidr_blocks = rule.get(\"cidr_blocks\")\n                if cidr_blocks and any(\"0.0.0.0\/0\" in block for block in cidr_blocks):\n                    return CheckResult.FAILED\n        return CheckResult.PASSED\n\nnon_public_cidr_check = NonPublicCidrBlockCheck()\n<\/code><\/pre>\n<h2 id=\"how-to-use-custom-policies\">How to use custom policies?<\/h2>\n<p>Here\u2019s an example directory structure:<\/p>\n<pre><code>jenkins-terraform\/\n\u251c\u2500\u2500 Jenkinsfile\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 custom_checks\n\u2502   \u251c\u2500\u2500 SecurityGroupDescription.py\n\u2502   \u251c\u2500\u2500 SecurityGroupInboundCIDR.py\n\u2502   \u2514\u2500\u2500 __init__.py\n\u251c\u2500\u2500 main.tf\n\u2514\u2500\u2500 variables.tf\n<\/code><\/pre>\n<p>The <code>main.tf<\/code> file:<\/p>\n<pre><code>provider \"aws\" {\n  region = var.aws_region\n}\n\nresource \"aws_security_group\" \"instance_sg\" {\n  name        = \"instance-sg\"\n  #description = \"checkov test\"\n\n  ingress {\n    from_port        = var.from_port\n    to_port          = var.to_port\n    protocol         = var.protocol\n    cidr_blocks      = var.cidr_block\n  }\n}\n<\/code><\/pre>\n<p>The <code>variables.tf<\/code> file<\/p>\n<pre><code>variable \"aws_region\" {\n  description = \"The AWS region to create things in.\"\n  default     = \"us-west-2\"\n}\n\nvariable \"from_port\" {\n  type        = number\n  default     = 22\n  description = \"List of starting ports for cidr ingress rules of the EC2 security group.\"\n}\n\nvariable \"to_port\" {\n  type        = number\n  default     = 22\n  description = \"List of ending ports for cidr ingress rules of the EC2 security group.\"\n}\n\nvariable \"protocol\" {\n  type        = string\n  default     = \"tcp\"\n  description = \"List of protocols for cidr ingress rules of the EC2 security group.\"\n}\n\nvariable \"cidr_block\" {\n  type        = list(string)\n  **default     = [\"0.0.0.0\/0\"]**\n  description = \"List of CIDR blocks for cidr ingress rules of the EC2 security group.\"\n}\n<\/code><\/pre>\n<p>To scan the code with a custom policy, use the following command:<\/p>\n<pre><code>checkov -d . --external-checks-dir custom_checks --check CUSTOM_AWS_*<\/code><\/pre>\n<blockquote><p><strong>Note<\/strong>: You can also scan with Terraform plan.<\/p><\/blockquote>\n<h3 id=\"checkov-report\"><strong>Checkov Report<\/strong><\/h3>\n<pre><code>terraform scan results:\n\nPassed checks: 0, Failed checks: 2, Skipped checks: 0\n\nCheck: CUSTOM_AWS_001: \"Ensure AWS security groups have descriptions\"\n\tFAILED for resource: aws_security_group.instance_sg\n\tFile: \/main.tf:5-15\n\tGuide: Security groups should have meaningful descriptions for better understanding.\n\n\t\t5  | resource \"aws_security_group\" \"instance_sg\" {\n\t\t6  |   name        = \"instance-sg\"\n\t\t7  |   #description = \"checkov test\"\n\t\t8  | \n\t\t9  |   ingress {\n\t\t10 |     from_port        = var.from_port\n\t\t11 |     to_port          = var.to_port\n\t\t12 |     protocol         = var.protocol\n\t\t13 |     cidr_blocks      = var.cidr_block\n\t\t14 |   }\n\t\t15 | }\n\nCheck: CUSTOM_AWS_002: \"Ensure AWS security groups have non-public CIDR blocks\"\n\tFAILED for resource: aws_security_group.instance_sg\n\tFile: \/main.tf:5-15\n\tGuide: CIDR blocks in security group rules should not be set to 0.0.0.0\/0.\n\n\t\t5  | resource \"aws_security_group\" \"instance_sg\" {\n\t\t6  |   name        = \"instance-sg\"\n\t\t7  |   #description = \"checkov test\"\n\t\t8  | \n\t\t9  |   ingress {\n\t\t10 |     from_port        = var.from_port\n\t\t11 |     to_port          = var.to_port\n\t\t12 |     protocol         = var.protocol\n\t\t13 |     cidr_blocks      = var.cidr_block\n\t\t14 |   }\n\t\t15 | }\n<\/code><\/pre>\n<h3 id=\"suppressing-and-skipping-policies\">Suppressing and skipping policies<\/h3>\n<p>Skip the custom policy check (Include the comment inside the code)<\/p>\n<pre><code>provider \"aws\" {\n  region = var.aws_region\n}\n\nresource \"aws_security_group\" \"instance_sg\" {\n  # checkov:skip=CUSTOM_AWS_001: ADD REASON\n  name        = \"instance-sg\"\n}\n<\/code><\/pre>\n<p>Output:<\/p>\n<pre><code>terraform scan results:\n\nPassed checks: 0, Failed checks: 0, Skipped checks: 1\n\nCheck: CUSTOM_AWS_001: \"Ensure AWS security groups have descriptions\"\n        SKIPPED for resource: aws_security_group.instance_sg\n        Suppress comment:  ADD REASON\n        File: \/main.tf:5-8\n        Guide: Security groups should have meaningful descriptions for better understanding.\n<\/code><\/pre>\n<p>To skip a check with a specific ID:<\/p>\n<pre><code>checkov -d terraform-aws\/environments\/dev\/ec2\/ --skip-check CKV2_AWS_5\n<\/code><\/pre>\n<h2 id=\"hard-and-soft-fail\">Hard and Soft fail<\/h2>\n<p>These arguments will provide an error code of either <code>0<\/code> or <code>1<\/code> based on the scanning. This feature is very helpful when integrated with the CI\/CD pipeline. Here, <code>0<\/code> means pass, and <code>1<\/code> means fail, If the scan results in an error code of <code>1<\/code>, the pipeline will halt; if it receives <code>0<\/code>, the provisioning will continue. These arguments can be paired up with policy IDs and severity levels.<\/p>\n<p><strong>\u2014soft-fail<\/strong><\/p>\n<p>This argument will always give the error code <code>0<\/code> with the scan results.<\/p>\n<pre><code>checkov -d . --soft-fail \n<\/code><\/pre>\n<p><strong>\u2014soft-fail-on<\/strong><\/p>\n<p>This argument will return an error code \u00a0<code>0<\/code> if any failed checks match the list.<\/p>\n<pre><code>checkov -d . --soft-fail-on LOW,CKV_AWS_46\n<\/code><\/pre>\n<p>It will return an error code of <code>1<\/code> if any non-listed checks fail.<\/p>\n<p><strong>\u2014hard-fail-on<\/strong><\/p>\n<p>This argument will return an error code of <code>1<\/code> if any failed checks match the list.<\/p>\n<pre><code>checkov -d . --hard-fail-on HIGH,CKV_AWS_8,CRITICAL,MEDIUM\n<\/code><\/pre>\n<p>It will return an error code <code>0<\/code> if any non-listed checks fail.<\/p>\n<h3 id=\"important-cli-commands\">Important CLI commands<\/h3>\n<p>To check the code with a specific framework:<\/p>\n<pre><code>checkov -d terraform-aws\/environment\/dev\/ec2 --framework terraform\n<\/code><\/pre>\n<p>To obtain the output with severity levels:<\/p>\n<pre><code>checkov -d terraform-aws\/environments\/dev\/ec2\/ --bc-api-key MY_API_KEY\n<\/code><\/pre>\n<p>To view the output with severity levels, you need to create an API key from <a href=\"https:\/\/docs.bridgecrew.io\/docs\/get-api-token?ref=devopscube.com\">Bridgecrew<\/a><\/p>\n<p>To get the output with a specific severity level:<\/p>\n<pre><code>checkov -d terraform-aws\/environments\/dev\/ec2\/ --check HIGH --bc-api-key MY_API_KEY\n<\/code><\/pre>\n<p>To scan the code with a specific ID:<\/p>\n<pre><code>checkov -d terraform-aws\/environments\/dev\/ec2\/ --check CKV_AWS_46\n<\/code><\/pre>\n<p>To get the output in a specific format:<\/p>\n<pre><code>checkov -d terraform-aws\/environments\/dev\/ec2\/ --check CKV_AWS_46 --output json<\/code><\/pre>\n<p>Supports more output formats, such as JSON, CSV, JUnit XML, sarif, spdx, and more.<\/p>\n<blockquote><p><strong>To learn more about the CLI arguments. please refer to the <a href=\"https:\/\/www.checkov.io\/2.Basics\/CLI%20Command%20Reference.html?ref=devopscube.com\">official documentation<\/a>.<\/strong><\/p><\/blockquote>\n<h2 id=\"checkov-config-file\">Checkov Config File<\/h2>\n<p>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.<\/p>\n<p>Create a configuration file. The configuration file supports the YAML format.<\/p>\n<pre><code>checkov -f tfplan.json --external-checks-dir ..\/..\/..\/custom_checks\/ --check CUSTOM_AWS_* --create-config .\/checkov.yml<\/code><\/pre>\n<p>This will automatically create the configuration file, <code>checkov.yml<\/code> .<\/p>\n<pre><code>block-list-secret-scan: []\nbranch: master\ncheck:\n- CUSTOM_AWS_*\ndownload-external-modules: false\nevaluate-variables: true\nexternal-checks-dir:\n- ..\/..\/..\/custom_checks\/\nexternal-modules-download-path: .external_modules\nfile:\n- - tfplan.json\nframework:\n- all\nmask: []\nsecrets-history-timeout: 12h\nsecrets-scan-file-type: []\nsummary-position: top\n<\/code><\/pre>\n<p>And the next time, you only have to mention the configuration file to scan the code.<\/p>\n<pre><code>checkov --config-file .\/checkov.yml<\/code><\/pre>\n<h3 id=\"the-checkov-gui-output-\"><strong>The Checkov GUI output.<\/strong><\/h3>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/untitled-4.png\" class=\"kg-image\" alt loading=\"lazy\"><\/figure>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>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.<\/p>\n<p>I recommend integrating Checkov with your IaC code creation to reduce the risk and improve compliance.<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/terraform-checkov-scan\/\" target=\"_blank\" rel=\"noopener noreferrer\">Scanning Terraform Code with Checkov: Comprehensive Guide \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/terraform-checkov-scan\/<\/p>\n","protected":false},"author":1,"featured_media":833,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-832","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/832","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=832"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/832\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/833"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=832"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=832"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=832"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}