--- name: implementing-infrastructure-as-code-security-scanning description: 'Implements automated security scanning for Infrastructure as Code using Checkov, tfsec, and KICS to detect misconfigurations in Terraform, CloudFormation, Kubernetes manifests, and Helm charts, plus policy-based governance and CI/CD integration. Use when validating cloud infrastructure before deployment or blocking insecure changes (public S3 buckets, open security groups) in pull requests. ' domain: cybersecurity subdomain: devsecops tags: - devsecops - cicd - iac-security - checkov - tfsec - terraform - secure-sdlc version: 1.0.0 author: mahipal license: Apache-2.0 nist_csf: - PR.PS-01 - GV.SC-07 - ID.IM-04 - PR.PS-04 mitre_attack: - T1195 - T1554 - T1059.004 - T1078.004 - T1530 --- # Implementing Infrastructure as Code Security Scanning ## When to Use - When provisioning cloud infrastructure with Terraform, CloudFormation, or Pulumi and needing automated security validation - When compliance frameworks require evidence of infrastructure configuration review before deployment - When preventing common cloud misconfigurations like public S3 buckets, open security groups, or unencrypted storage - When establishing guardrails that block insecure infrastructure changes in pull requests - When managing multi-cloud environments requiring consistent security policies across AWS, Azure, and GCP **Do not use** for scanning application source code (use SAST), for monitoring already-deployed infrastructure drift (use cloud security posture management tools), or for container image vulnerability scanning (use Trivy). ## Prerequisites - Checkov v3.x installed (`pip install checkov`) or tfsec installed - Terraform, CloudFormation, or Kubernetes IaC files in the repository - CI/CD pipeline with access to IaC directories - Bridgecrew API key (optional, for Checkov platform integration) ## Workflow ### Step 1: Run Checkov Against Terraform Files ```bash # Scan all Terraform files in a directory checkov -d ./terraform/ --framework terraform --output cli --output json --output-file-path ./results # Scan specific file checkov -f main.tf --output json # Scan Terraform plan (more accurate for dynamic values) terraform init && terraform plan -out=tfplan terraform show -json tfplan > tfplan.json checkov -f tfplan.json --framework terraform_plan # Scan with specific checks only checkov -d ./terraform/ --check CKV_AWS_18,CKV_AWS_19,CKV_AWS_20 # Skip specific checks checkov -d ./terraform/ --skip-check CKV_AWS_145,CKV2_AWS_6 ``` ### Step 2: Integrate IaC Scanning into GitHub Actions ```yaml # .github/workflows/iac-security.yml name: IaC Security Scan on: pull_request: paths: - 'terraform/**' - 'cloudformation/**' - 'k8s/**' jobs: checkov: name: Checkov IaC Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run Checkov uses: bridgecrewio/checkov-action@v12 with: directory: terraform/ framework: terraform output_format: cli,sarif output_file_path: console,checkov.sarif soft_fail: false skip_check: CKV_AWS_145 - name: Upload SARIF if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: checkov.sarif category: checkov-iac tfsec: name: tfsec Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run tfsec uses: aquasecurity/tfsec-action@v1.0.3 with: working_directory: terraform/ sarif_file: tfsec.sarif soft_fail: false - name: Upload SARIF if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: tfsec.sarif category: tfsec ``` ### Step 3: Create Custom Checkov Policies ```python # custom_checks/s3_versioning.py from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck from checkov.common.models.enums import CheckResult, CheckCategories class S3BucketVersioning(BaseResourceCheck): def __init__(self): name = "Ensure S3 bucket has versioning enabled" id = "CKV_CUSTOM_1" supported_resources = ["aws_s3_bucket"] categories = [CheckCategories.GENERAL_SECURITY] super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) def scan_resource_conf(self, conf): versioning = conf.get("versioning", [{}]) if isinstance(versioning, list) and len(versioning) > 0: if versioning[0].get("enabled", [False])[0]: return CheckResult.PASSED return CheckResult.FAILED check = S3BucketVersioning() ``` ### Step 4: Configure Baseline and Suppressions ```yaml # .checkov.yaml branch: main compact: true directory: - terraform/ - cloudformation/ framework: - terraform - cloudformation - kubernetes output: - cli - sarif skip-check: - CKV_AWS_145 # S3 default encryption with CMK (using SSE-S3 is acceptable) - CKV2_AWS_6 # S3 bucket request logging (handled at CloudTrail level) soft-fail: false ``` ### Step 5: Scan Kubernetes Manifests and Helm Charts ```bash # Scan Kubernetes manifests checkov -d ./k8s/ --framework kubernetes # Scan Helm charts (renders templates first) checkov -d ./charts/myapp/ --framework helm # Scan with KICS (Keeping Infrastructure as Code Secure) docker run -v $(pwd)/k8s:/path checkmarx/kics:latest scan \ --path /path \ --output-path /path/results \ --type Kubernetes \ --report-formats json,sarif ``` ## Key Concepts | Term | Definition | |------|------------| | IaC Scanning | Automated analysis of infrastructure code templates to detect security misconfigurations before deployment | | Policy as Code | Security policies defined as executable code that can be version-controlled, tested, and enforced automatically | | CKV Check ID | Checkov's unique identifier for each security check (e.g., CKV_AWS_18 for S3 public access) | | Terraform Plan Scanning | Scanning the resolved Terraform plan JSON which includes computed values and module expansions | | Graph-based Scanning | Checkov's ability to analyze relationships between resources, not just individual resource configs | | Drift Detection | Identifying differences between IaC definitions and actual deployed infrastructure state | | Custom Policy | Organization-specific security checks authored in Python or YAML to enforce internal standards | ## Tools & Systems - **Checkov**: Open-source IaC scanner by Bridgecrew with 2500+ built-in policies covering major cloud providers - **tfsec**: Terraform-focused static analysis tool by Aqua Security with deep HCL understanding - **KICS**: Open-source IaC scanner by Checkmarx supporting 15+ IaC frameworks - **Terrascan**: IaC scanner with OPA Rego policy support for custom policy authoring - **Snyk IaC**: Commercial IaC scanner integrated with the Snyk platform ## Common Scenarios ### Scenario: Preventing Public S3 Buckets in Terraform **Context**: A development team repeatedly creates S3 buckets without proper access controls. A recent incident exposed customer data through a public bucket. **Approach**: 1. Enable Checkov in the CI/CD pipeline for all Terraform changes 2. Enforce CKV_AWS_18 (no public read ACL), CKV_AWS_19 (encryption), CKV_AWS_20 (no public access block disabled) 3. Create a custom policy requiring the `aws_s3_bucket_public_access_block` resource for every S3 bucket 4. Set `soft_fail: false` to block PR merges when S3 security checks fail 5. Provide Terraform modules with security defaults that teams can reuse **Pitfalls**: Scanning only `.tf` files misses dynamically computed values. Use Terraform plan scanning for higher accuracy. Checkov's resource-relationship checks (CKV2 prefix) require graph analysis mode. ## Output Format ``` IaC Security Scan Report ========================== Framework: Terraform Directory: terraform/ Scan Date: 2026-02-23 Checkov Results: Passed: 187 Failed: 12 Skipped: 3 Unknown: 0 FAILED CHECKS: CKV_AWS_18 [HIGH] S3 Bucket has public read ACL Resource: aws_s3_bucket.data_lake File: terraform/storage.tf:15-28 CKV_AWS_24 [HIGH] CloudWatch log group not encrypted Resource: aws_cloudwatch_log_group.app File: terraform/monitoring.tf:3-8 CKV_AWS_79 [MEDIUM] Instance metadata service v1 enabled Resource: aws_instance.web File: terraform/compute.tf:12-30 QUALITY GATE: FAILED (2 HIGH severity findings) ```