Skip to content

R055LE/iac-security-lab

Repository files navigation

IaC Security Lab

CI

Policy-as-code enforcement and static analysis for Terraform infrastructure modules, aligned with the CIS AWS Foundations Benchmark. Companion project to container-hardening-lab.

No AWS credentials required. All checks are static analysis against HCL — nothing is deployed.


What This Demonstrates

  • Misconfigurations are caught before they reach a cloud account. tfsec, Trivy, and Conftest (OPA) all run against Terraform source files — no plan execution, no cloud API calls.
  • Every hardening control maps to a CIS benchmark reference. Each module variable, resource, and policy rule cites the CIS AWS Foundations Benchmark section it enforces.
  • Policy-as-code with OPA/Rego. Six custom Rego policies enforce controls that generic scanners miss: cross-resource joins (VPC → flow log), deep IAM JSON parsing (wildcard Action/Resource), multi-field checks (all four S3 public access block settings), CloudTrail log validation chains, RDS SSL/TLS parameter enforcement, and EKS Secrets encryption with Conftest HCL parsing normalization.
  • Failure demo validates the pipeline itself. The examples/insecure/ directory contains deliberate violations. CI asserts that scanners must find them — a clean scan fails the job.
  • Three test layers. OPA unit tests (synthetic fixtures), native Terraform tests (variable validation, mock providers), and static analysis scans.

Repository Structure

iac-security-lab/
├── modules/
│   ├── s3/               # CIS 2.1.x — encryption, public access block, versioning, logging
│   ├── iam/              # CIS 1.x  — no wildcards, permission boundary, non-root path
│   ├── vpc/              # CIS 4.x/5.x — flow logs, restricted SGs, default SG locked
│   ├── cloudtrail/       # CIS 3.x  — multi-region, log validation, CWL, KMS encryption
│   ├── rds/              # CIS 2.3.x — storage encryption, SSL/TLS, no public access
│   └── eks/              # CIS EKS Benchmark — Secrets encryption, private endpoint, IMDSv2
│
├── examples/
│   ├── hardened/         # Correct module consumption — make lint must pass cleanly
│   └── insecure/         # 10 deliberate violations — scanners must find them
│
├── policies/opa/         # Rego policies evaluated by Conftest against HCL
│   ├── s3.rego
│   ├── iam.rego
│   ├── vpc.rego
│   ├── cloudtrail.rego
│   ├── rds.rego
│   └── eks.rego
│
├── tests/
│   ├── opa/              # OPA unit tests — one file per policy
│   │   ├── s3_test.rego
│   │   ├── iam_test.rego
│   │   ├── vpc_test.rego
│   │   ├── cloudtrail_test.rego
│   │   ├── rds_test.rego
│   │   └── eks_test.rego
│   └── terraform/        # Native terraform test configs (TF >= 1.6)
│       ├── s3.tftest.hcl
│       ├── iam.tftest.hcl
│       ├── vpc.tftest.hcl
│       ├── cloudtrail.tftest.hcl
│       ├── rds.tftest.hcl
│       └── eks.tftest.hcl
│
├── docs/
│   ├── tool-decisions.md
│   ├── adding-a-module.md
│   └── conftest-hcl-parsing.md
│
├── .github/workflows/ci.yml
├── .tfsec.yml
├── Makefile
├── CONTRIBUTING.md
└── README.md

Hardening Controls

S3 — CIS 2.1.x

CIS Control What It Prevents Implementation
2.1.1 — SSE enabled Unencrypted data at rest aws_s3_bucket_server_side_encryption_configuration always created; AES-256 default, KMS optional
2.1.2 — Public access block Accidental internet exposure via ACL or bucket policy All four block settings hardcoded to true
2.1.3 — Versioning Ransomware overwrite, accidental permanent deletion aws_s3_bucket_versioning status = "Enabled"
2.1.5 — Access logging Undetectable exfiltration, no GetObject audit trail aws_s3_bucket_logging required; log bucket passed as variable

IAM — CIS 1.x

CIS Control What It Prevents Implementation
1.16 — No inline policies Inline policies bypass IAM policy versioning and audit tools Module only creates managed policies via aws_iam_policy
1.16 — Permission boundary required Privilege escalation via over-permissive policy attachment permission_boundary_arn is a required variable with ARN validation
1.16 — Non-root IAM path Wildcard path-prefix matches in IAM conditions role_path defaults to /app/; root / is rejected by variable validation
Principle of least privilege Credential compromise → full account access OPA iam.rego denies any wildcard Action or Resource

VPC — CIS 4.x / 5.x

CIS Control What It Prevents Implementation
4.x — VPC flow logs No network audit trail for lateral movement or exfiltration aws_flow_log always created; CloudWatch Logs destination
5.1 — No 0.0.0.0/0 on sensitive ports Unrestricted SSH/RDP/DB access from the internet OPA vpc.rego denies; module creates no such rules
5.4 — Default SG restricted Resources in default SG have implicit full access aws_default_security_group removes all ingress/egress rules

CloudTrail — CIS 3.x

CIS Control What It Prevents Implementation
3.1 — Multi-region trail API calls in non-home regions leave no trail is_multi_region_trail = true, include_global_service_events = true
3.2 — Log file validation Attacker with S3 write access deletes/modifies logs undetected enable_log_file_validation = true — SHA-256 HMAC digest chain
3.4 — CloudWatch Logs integration No real-time alerting on DeleteTrail, StopLogging, ConsoleLogin without MFA cloud_watch_logs_group_arn and delivery role always configured
3.6 — S3 access logging on trail bucket Exfiltration of audit trail itself is unlogged aws_s3_bucket_logging on the trail log bucket
3.7 — KMS encryption of log files S3-stored logs readable by anyone with bucket GetObject kms_key_id is required; no default AWS-managed key permitted

RDS — CIS 2.3.x

CIS Control What It Prevents Implementation
2.3.1 — Storage encrypted (CMK) Exfiltrated EBS snapshots are readable without a key storage_encrypted = true; kms_key_id required
2.3.2 — Automated backups ≥ 7 days No recovery path after ransomware or insider deletion backup_retention_period validated >= 7
2.3.3 — Not publicly accessible Direct internet database access from leaked connection strings publicly_accessible = false hardcoded
In-transit SSL/TLS Cleartext credentials and queries on the wire Parameter group enforces rds.force_ssl=1 (PostgreSQL) or require_secure_transport=ON (MySQL)
SG-to-SG only ingress CIDR-based internet ingress structurally impossible No CIDR ingress variable; only allowed_security_group_ids

EKS — CIS EKS Benchmark

CIS Control What It Prevents Implementation
EKS 2.1.1 — All control plane log types Audit blind spots: RBAC decisions, auth failures, scheduler activity All five types: api, audit, authenticator, controllerManager, scheduler
EKS 3.1.1 — Secrets encrypted at rest (CMK) Compromised etcd backup exposes all Secrets in base64 encryption_config with resources = ["secrets"] and CMK
EKS 5.4.1 — Private API endpoint Control plane accessible from the internet endpoint_private_access = true; endpoint_public_access = false by default
EKS 5.4.2 — Public access CIDRs restricted 0.0.0.0/0 access to API server when public endpoint enabled public_access_cidrs required when enabling public endpoint
IMDSv2 enforced SSRF in any container retrieves node IAM credentials Launch template: http_tokens = "required", hop limit = 1

Test Layers

Layer 1: OPA Unit Tests

Fast, no tooling beyond opa. Each test mocks input directly as the Conftest-parsed JSON structure.

make test-opa
# or individually:
opa test policies/opa/s3.rego tests/opa/s3_test.rego --verbose

Covers: deny triggers, pass cases, warn rules, multi-resource edge cases.

Layer 2: Native Terraform Tests

Validates variable validation logic and output structure without cloud credentials.

terraform -chdir=modules/s3 test
terraform -chdir=modules/iam test
terraform -chdir=modules/vpc test

Uses mock_provider "aws" {} — no API calls. Tests include expect_failures cases for invalid inputs.

Layer 3: Static Analysis

tfsec and Trivy run against module HCL. Conftest runs custom OPA policies.

make lint   # Conftest
make scan   # tfsec + trivy

Failure Demo

examples/insecure/main.tf contains ten deliberate violations across five services:

Violation CIS Control Scanner
S3 bucket with no SSE 2.1.1 tfsec, Trivy
S3 public access block all false 2.1.2 tfsec, Trivy, Conftest
IAM policy with Action: "*" 1.16 Conftest (iam.rego)
IAM policy with Resource: "*" 1.16 Conftest (iam.rego)
Security group 0.0.0.0/0 on port 22 5.1 tfsec, Trivy, Conftest
VPC with no flow logs 4.x Conftest (vpc.rego)
RDS no encryption + publicly accessible 2.3.1, 2.3.3 tfsec, Trivy, Conftest
RDS parameter group without SSL In-transit Conftest (rds.rego)
EKS public endpoint + no Secrets encryption EKS 5.4.1, 3.1.1 Conftest (eks.rego)
Launch template without IMDSv2 IMDSv2 Conftest (eks.rego)

CI job failure-demo asserts that both conftest and tfsec exit non-zero against this directory. A clean result fails the job — proving the pipeline itself is working.

See examples/insecure/README.md for the attack each violation enables.


CI Pipeline

graph LR
    A[push / PR] --> B[policy-tests\nOPA unit tests]
    B --> C[failure-demo\ninsecure configs must fail]
    B --> D[module-pipeline\nvalidate · lint · tfsec · trivy]
    D --> E[SARIF → GitHub Security tab]
Loading

Three jobs run on every push to main and every pull request:

  1. policy-tests — OPA unit tests for all six policies. No cloud, no Docker. Fast feedback.
  2. failure-demo — Conftest + tfsec against examples/insecure/. Must produce findings.
  3. module-pipeline — Matrix over [s3, iam, vpc, cloudtrail, rds, eks]. Validate, lint, tfsec SARIF, Trivy SARIF, artifact upload.

Quick Start

# Prerequisites: opa, conftest, tfsec, trivy, terraform

# Run OPA unit tests (no external tools except opa)
make test-opa

# Validate module HCL syntax
make validate

# Run Conftest policy check
make lint

# Run tfsec + trivy
make scan

# Full pipeline
make all

Prerequisites

  • Terraform 1.9.x
  • OPA 0.70.0
  • Conftest 0.57.0
  • tfsec 1.28.x
  • Trivy 0.69.x

No AWS credentials required. See CONTRIBUTING.md for install instructions.


Related

About

Policy-as-code static analysis for Terraform against the CIS AWS Foundations Benchmark using tfsec, Trivy, and OPA/Rego.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors