DevOps Peach

Security (DevSecOps) · IaC security and policy as code · Lesson 2 of 4

Scanning IaC with Checkov

Checkov ships more than a thousand checks for Terraform, CloudFormation, Kubernetes, Helm, Dockerfiles and ARM; scan folders or plan JSON, select or skip checks, export SARIF and skip with a reason inside the code.

Practice interactively →

What Checkov is

Checkov (open source, from Prisma Cloud) is a static analysis tool for IaC, written in Python. It understands relationships between resources (a graph), so it can detect issues such as "no public access block resource is attached to this bucket".

pip install checkov
checkov -d .                                  # scan a folder
checkov -f main.tf                            # scan one file
checkov -d . --framework terraform
checkov -d . --compact --quiet                # failed checks only, compact
checkov -d . -o sarif --output-file-path results/

Reading results

Check: CKV_AWS_24: "Ensure no security groups allow ingress from 0.0.0.0:0 to port 22"
    FAILED for resource: aws_security_group.web
    File: /network.tf:3-20
    Guide: https://docs.prismacloud.io/...

Each check has an ID (CKV_AWS_*, CKV_AZURE_*, CKV_K8S_*, CKV_DOCKER_*), the offending resource, file, lines and remediation guide. The exit code is non-zero when checks fail, so CI stops.

Scanning the plan instead of the code

Code uses variables and modules, so Checkov sometimes cannot know the final value. Scanning the plan JSON is more accurate, with values resolved:

terraform plan -out tfplan
terraform show -json tfplan > tfplan.json
checkov -f tfplan.json --framework terraform_plan

Selecting checks and exceptions

checkov -d . --check CKV_AWS_20,CKV_AWS_57      # run only these checks
checkov -d . --skip-check CKV_AWS_18            # skip globally

The best exception lives inside the resource, with a reason, where reviewers see it:

resource "aws_s3_bucket" "site" {
  # checkov:skip=CKV_AWS_18:Static website bucket, access logs are kept in CloudFront
  bucket = "example-org-site"
}

A .checkov.yaml file holds shared repo settings (frameworks, skips, output), so people and CI run the same way.

flowchart LR
  C["Code .tf"] --> CK["checkov -d ."]
  P["Plan JSON"] --> CK2["checkov -f tfplan.json"]
  CK --> R{"Failed?"}
  CK2 --> R
  R -- "yes" --> X["Block the PR"]
  R -- "no" --> OK["Allow apply"]

Quick comparison

Tool Strengths
Checkov Many frameworks, graph analysis, custom policies in Python or YAML
Trivy config (formerly tfsec) One binary shared with the image scanner
tflint Provider errors and style, not security-focused
KICS Many platforms, queries in Rego

Traps: using a global --skip-check for something only one resource needs; scanning only code when modules use dynamic values; letting hundreds of failed checks pile up until nobody reads the output.

Practice interactively →