DEV Community

Securing Infrastructure as Code with Checkov: A Practical SAST Approach

Introduction

Infrastructure as Code (IaC) has revolutionized how we manage and deploy cloud resources. However, with great power comes great responsibility. Security misconfigurations in IaC templates can lead to serious vulnerabilities in production environments. This is where Static Application Security Testing (SAST) tools like Checkov come into play.

In this article, we'll explore how to implement Checkov, a powerful open-source SAST tool, to scan and secure your Infrastructure as Code across multiple platforms including Terraform, CloudFormation, Kubernetes, and more.

What is Checkov?

Checkov is a static code analysis tool developed by Bridgecrew (now part of Palo Alto Networks) that scans infrastructure code for security and compliance misconfigurations. It supports:

  • Terraform
  • CloudFormation
  • Kubernetes
  • Helm
  • ARM Templates
  • Serverless Framework
  • Docker
  • And many more...

Why Use SAST for Infrastructure as Code?

Early Detection: Identify security issues before they reach production
Compliance: Ensure adherence to industry standards (CIS, PCI-DSS, HIPAA)
Cost Reduction: Fix issues early in the development cycle
Automation: Integrate seamlessly into CI/CD pipelines
Policy as Code: Define and enforce organizational security policies

Getting Started with Checkov

Installation

Checkov can be installed using pip:

pip install checkov
Enter fullscreen mode Exit fullscreen mode

Or using Docker:

docker pull bridgecrew/checkov
Enter fullscreen mode Exit fullscreen mode

Basic Usage

Scan a Terraform directory:

checkov -d /path/to/terraform
Enter fullscreen mode Exit fullscreen mode

Scan a specific file:

checkov -f main.tf
Enter fullscreen mode Exit fullscreen mode

Practical Example: Securing Terraform Code

Let's examine a vulnerable Terraform configuration and how Checkov identifies the issues.

Vulnerable Configuration

# main.tf - INSECURE EXAMPLE
resource "aws_s3_bucket" "data_bucket" {
  bucket = "my-data-bucket"

  # Missing encryption
  # Missing versioning
  # Missing logging
}

resource "aws_security_group" "web_sg" {
  name        = "web-security-group"
  description = "Security group for web servers"

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]  # Too permissive!
  }
}

resource "aws_db_instance" "database" {
  allocated_storage    = 20
  engine              = "mysql"
  instance_class      = "db.t3.micro"
  username            = "admin"
  password            = "hardcodedpassword123"  # Hardcoded password!
  skip_final_snapshot = true

  # Missing encryption
  # Publicly accessible by default
}
Enter fullscreen mode Exit fullscreen mode

Running Checkov

checkov -f main.tf --output json --output-file results.json
Enter fullscreen mode Exit fullscreen mode

Checkov Output

Checkov will identify multiple security issues:

Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"
Check: CKV_AWS_19: "Ensure the S3 bucket has server-side encryption enabled"
Check: CKV_AWS_21: "Ensure the S3 bucket has versioning enabled"
Check: CKV_AWS_23: "Ensure Security Group does not allow ingress from 0.0.0.0/0 to port 22"
Check: CKV_AWS_16: "Ensure RDS database is encrypted at rest"
Check: CKV_AWS_41: "Ensure RDS instance has IAM authentication enabled"
Enter fullscreen mode Exit fullscreen mode

Secure Configuration

Now let's fix these issues:

# main.tf - SECURE VERSION
resource "aws_s3_bucket" "data_bucket" {
  bucket = "my-data-bucket"
}

resource "aws_s3_bucket_versioning" "data_bucket_versioning" {
  bucket = aws_s3_bucket.data_bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "data_bucket_encryption" {
  bucket = aws_s3_bucket.data_bucket.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

resource "aws_s3_bucket_logging" "data_bucket_logging" {
  bucket = aws_s3_bucket.data_bucket.id

  target_bucket = aws_s3_bucket.log_bucket.id
  target_prefix = "log/"
}

resource "aws_security_group" "web_sg" {
  name        = "web-security-group"
  description = "Security group for web servers"

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/8"]  # Restricted to internal network
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_db_instance" "database" {
  allocated_storage    = 20
  engine              = "mysql"
  instance_class      = "db.t3.micro"
  username            = "admin"
  password            = var.db_password  # Using variable instead
  skip_final_snapshot = false

  storage_encrypted          = true
  iam_database_authentication_enabled = true
  publicly_accessible        = false

  backup_retention_period = 7
  enabled_cloudwatch_logs_exports = ["error", "general", "slowquery"]
}
Enter fullscreen mode Exit fullscreen mode

Integrating Checkov into CI/CD

GitHub Actions Example

name: IaC Security Scan

on: [push, pull_request]

jobs:
  checkov:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.9'

      - name: Install Checkov
        run: pip install checkov

      - name: Run Checkov
        run: |
          checkov -d . --output cli --output json --output-file checkov-report.json

      - name: Upload results
        uses: actions/upload-artifact@v3
        if: always()
        with:
          name: checkov-report
          path: checkov-report.json
Enter fullscreen mode Exit fullscreen mode

GitLab CI Example

checkov:
  stage: test
  image: bridgecrew/checkov:latest
  script:
    - checkov -d . --output cli --output junitxml --output-file checkov-report.xml
  artifacts:
    reports:
      junit: checkov-report.xml
Enter fullscreen mode Exit fullscreen mode

Custom Policies

Checkov allows you to create custom policies using Python:

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

class S3BucketEncryption(BaseResourceCheck):
    def __init__(self):
        name = "Ensure S3 bucket is encrypted with KMS"
        id = "CKV_AWS_CUSTOM_1"
        supported_resources = ['aws_s3_bucket']
        categories = [CheckCategories.ENCRYPTION]
        super().__init__(name=name, id=id, categories=categories, 
                         supported_resources=supported_resources)

    def scan_resource_conf(self, conf):
        if 'server_side_encryption_configuration' in conf:
            encryption_config = conf['server_side_encryption_configuration'][0]
            if 'rule' in encryption_config:
                rule = encryption_config['rule'][0]
                if 'apply_server_side_encryption_by_default' in rule:
                    encryption = rule['apply_server_side_encryption_by_default'][0]
                    if encryption.get('sse_algorithm') == ['aws:kms']:
                        return CheckResult.PASSED
        return CheckResult.FAILED

check = S3BucketEncryption()
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Run Checkov Early: Integrate it into your development workflow
  2. Use Suppressions Wisely: Document why certain checks are suppressed
  3. Keep Checkov Updated: New checks are added regularly
  4. Set Baseline: Use --baseline to track improvements over time
  5. Combine with Other Tools: Use alongside dynamic testing and manual reviews
  6. Educate Your Team: Ensure developers understand the security implications

Suppressing False Positives

Sometimes you need to suppress legitimate findings:

resource "aws_security_group" "special_case" {
  name = "special-sg"

  #checkov:skip=CKV_AWS_23:This SG is for internal load balancer
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Measuring Success

Track these metrics to measure the effectiveness of Checkov:

  • Vulnerability Detection Rate: Number of issues found per scan
  • Fix Time: Time from detection to remediation
  • False Positive Rate: Percentage of findings that are not actual issues
  • Coverage: Percentage of infrastructure code being scanned
  • Compliance Score: Pass rate against security benchmarks

Conclusion

Implementing SAST tools like Checkov is essential for maintaining secure infrastructure as code. By catching security issues early in the development cycle, you can:

  • Reduce security risks in production
  • Ensure compliance with industry standards
  • Foster a security-first culture
  • Save time and money on remediation

Start small by integrating Checkov into one project, measure the results, and gradually expand coverage across your organization. Security is a journey, not a destination, and tools like Checkov are invaluable companions along the way.

References


Top comments (0)