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. :::

Quiz

Module 5: Secrets Management & Infrastructure Security

Take Quiz