Secrets Management & Infrastructure Security
Infrastructure as Code Security (Checkov, tfsec)
4 min read
Infrastructure as Code (IaC) lets you version-control your infrastructure. But misconfigured IaC means misconfigured infrastructure at scale. IaC security scanning catches these issues before deployment.
Why IaC Security Matters
| Misconfiguration | Impact |
|---|---|
| S3 bucket public access | Data breach |
| Security group 0.0.0.0/0 | Network exposure |
| Unencrypted database | Compliance violation |
| Missing logging | No incident visibility |
| IAM overly permissive | Privilege escalation |
Statistics:
- 65% of cloud breaches stem from misconfigurations
- 99% of IaC files have at least one misconfiguration
- Average 45 days to detect misconfiguration-based breaches
IaC Security Tools
| Tool | Focus | Best For |
|---|---|---|
| Checkov | Multi-framework (TF, K8s, ARM, CFN) | Comprehensive scanning |
| tfsec | Terraform-specific | Fast Terraform scanning |
| KICS | Multi-framework | Open-source alternative |
| Terrascan | Policy-as-code | Custom policies |
| Snyk IaC | Multi-framework | Snyk users |
Checkov: Multi-Framework Scanning
Installation
# Install via pip
pip install checkov
# Or via Homebrew
brew install checkov
# Or via Docker
docker pull bridgecrew/checkov
Basic Usage
# Scan current directory
checkov -d .
# Scan specific file
checkov -f main.tf
# Scan with specific framework
checkov -d . --framework terraform
# Output as JSON
checkov -d . -o json > results.json
# Scan Kubernetes manifests
checkov -d ./k8s --framework kubernetes
Example Findings
Passed checks: 23, Failed checks: 5, Skipped checks: 0
Check: CKV_AWS_19: "Ensure the S3 bucket has server-side-encryption enabled"
FAILED for resource: aws_s3_bucket.data
File: /main.tf:15-25
Guide: https://docs.bridgecrew.io/docs/s3_19-s3-bucket-encryption
Check: CKV_AWS_20: "Ensure S3 bucket has block public ACLs enabled"
FAILED for resource: aws_s3_bucket.data
File: /main.tf:15-25
Custom Checks
# .checkov/custom_policy.yaml
metadata:
id: "CKV2_CUSTOM_1"
name: "Ensure all S3 buckets have a Name tag"
category: "CONVENTION"
definition:
cond_type: "attribute"
resource_types:
- "aws_s3_bucket"
attribute: "tags.Name"
operator: "exists"
tfsec: Terraform-Specific Scanning
Installation
# macOS
brew install tfsec
# Go install
go install github.com/aquasecurity/tfsec/cmd/tfsec@latest
# Docker
docker pull aquasec/tfsec
Basic Usage
# Scan directory
tfsec .
# Scan with soft-fail (don't exit 1)
tfsec . --soft-fail
# Output as SARIF for GitHub
tfsec . --format sarif > tfsec.sarif
# Exclude specific checks
tfsec . --exclude aws-s3-enable-bucket-logging
CI/CD Integration
# GitHub Actions with Checkov
name: IaC Security Scan
on:
pull_request:
paths:
- '**/*.tf'
- '**/*.yaml'
- '**/*.yml'
jobs:
checkov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@v12
with:
directory: .
framework: terraform
output_format: sarif
output_file_path: checkov.sarif
soft_fail: false
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: checkov.sarif
# tfsec GitHub Action
- name: Run tfsec
uses: aquasecurity/tfsec-action@v1.0.3
with:
soft_fail: false
Common Misconfigurations by Provider
AWS
# BAD: Public S3 bucket
resource "aws_s3_bucket" "bad" {
bucket = "my-bucket"
}
resource "aws_s3_bucket_public_access_block" "bad" {
bucket = aws_s3_bucket.bad.id
block_public_acls = false # Should be true
block_public_policy = false # Should be true
}
# GOOD: Secure S3 bucket
resource "aws_s3_bucket" "good" {
bucket = "my-bucket"
}
resource "aws_s3_bucket_public_access_block" "good" {
bucket = aws_s3_bucket.good.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "good" {
bucket = aws_s3_bucket.good.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
Kubernetes
# BAD: Running as root
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
image: nginx
securityContext:
runAsUser: 0 # Root!
# GOOD: Non-root, restricted
apiVersion: v1
kind: Pod
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx
securityContext:
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
Suppressing False Positives
Checkov Inline Skip
resource "aws_s3_bucket" "logs" {
#checkov:skip=CKV_AWS_19:Encryption not required for public logs
bucket = "public-logs"
}
tfsec Inline Ignore
resource "aws_security_group" "allow_all" {
#tfsec:ignore:aws-vpc-no-public-ingress-sgr
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # Intentionally public
}
}
Pre-Commit Integration
# .pre-commit-config.yaml
repos:
- repo: https://github.com/bridgecrewio/checkov
rev: '3.0.0'
hooks:
- id: checkov
args: ['--framework', 'terraform']
- repo: https://github.com/aquasecurity/tfsec
rev: v1.28.0
hooks:
- id: tfsec
In the final module, we'll cover compliance, governance, and DevSecOps maturity. :::