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
Or using Docker:
docker pull bridgecrew/checkov
Basic Usage
Scan a Terraform directory:
checkov -d /path/to/terraform
Scan a specific file:
checkov -f main.tf
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
}
Running Checkov
checkov -f main.tf --output json --output-file results.json
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"
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"]
}
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
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
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()
Best Practices
- Run Checkov Early: Integrate it into your development workflow
- Use Suppressions Wisely: Document why certain checks are suppressed
- Keep Checkov Updated: New checks are added regularly
-
Set Baseline: Use
--baselineto track improvements over time - Combine with Other Tools: Use alongside dynamic testing and manual reviews
- 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"]
}
}
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
- Checkov Official Documentation
- OWASP Source Code Analysis Tools
- Terraform Security Best Practices
- CIS Benchmarks
Top comments (0)