DEV Community

suhteevah
suhteevah

Posted on

Your Terraform Is Probably Insecure — Here Are 90 Patterns to Check

Cloud misconfigurations were responsible for 15% of all initial attack vectors in data breaches last year. Not zero-days. Not sophisticated exploits. Misconfigurations. Public S3 buckets, overprivileged IAM roles, security groups that allow the entire internet to SSH in.

The infrastructure-as-code revolution was supposed to fix this — codify your infrastructure, review it like application code, catch mistakes in PRs. But terraform plan tells you what will change. It does not tell you if what you're deploying is secure.

I built CloudGuard to close that gap. 90 security patterns for Terraform and CloudFormation files. Here's what it checks and why.

1. Public S3 Buckets and Storage Access

# The pattern — public-read ACL with no block
resource "aws_s3_bucket" "assets" {
  bucket = "company-assets"
  acl    = "public-read"  # Anyone on the internet can read this
}

# The fix — block public access explicitly
resource "aws_s3_bucket" "assets" {
  bucket = "company-assets"
}

resource "aws_s3_bucket_public_access_block" "assets" {
  bucket = aws_s3_bucket.assets.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
Enter fullscreen mode Exit fullscreen mode

Why it still happens: marketing wants a public assets bucket, someone copies a Terraform example that defaults to public, the bucket policy is complex and nobody reads it carefully.

CloudGuard rule S3-001 catches public-read ACL on buckets. S3-004 flags missing S3 Block Public Access configuration. S3-008 detects bucket policies allowing wildcard principals.

2. Wildcard IAM Policies

# The pattern — full admin access to everything
resource "aws_iam_policy" "admin" {
  name = "ci-pipeline-policy"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = "*"       # Every AWS action
      Resource = "*"       # On every resource
    }]
  })
}

# The fix — scope to specific actions and resources
resource "aws_iam_policy" "ci_deploy" {
  name = "ci-pipeline-policy"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = ["s3:PutObject", "s3:GetObject"]
      Resource = "arn:aws:s3:::my-deploy-bucket/*"
    }]
  })
}
Enter fullscreen mode Exit fullscreen mode

"I'll scope it down later." You won't. Or: "It's just for the CI pipeline." Until the CI credentials leak.

CloudGuard rule IM-001 catches wildcard Action in IAM policies. IM-003 flags wildcard Resource. IM-007 detects IAM roles with wildcard trust policies.

3. Open Security Groups

# The pattern — SSH open to the entire internet
resource "aws_security_group" "web" {
  name = "web-server"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]  # The entire internet can SSH in
  }
}

# The fix — restrict to specific CIDR blocks
resource "aws_security_group" "web" {
  name = "web-server"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/8"]  # VPN/internal only
  }
}
Enter fullscreen mode Exit fullscreen mode

"I need to SSH in from home." Use a bastion host or AWS SSM Session Manager. Never expose SSH directly to the internet.

CloudGuard rule SG-001 catches security groups allowing 0.0.0.0/0 ingress. SG-004 flags database ports open to the internet.

4. Unencrypted Storage and Databases

# The pattern — RDS without encryption (Terraform default)
resource "aws_db_instance" "main" {
  engine         = "postgres"
  instance_class = "db.t3.micro"
  # storage_encrypted is false by default
}

# The fix — always encrypt
resource "aws_db_instance" "main" {
  engine            = "postgres"
  instance_class    = "db.t3.micro"
  storage_encrypted = true
  kms_key_id        = aws_kms_key.rds.arn
}
Enter fullscreen mode Exit fullscreen mode

Most Terraform resources don't enable encryption by default. If you don't explicitly set it, it's off. There's almost no performance penalty for encryption at rest.

CloudGuard rule EC-001 catches RDS without encryption at rest. EC-004 flags unencrypted EBS volumes. EC-012 detects missing KMS key rotation.

5. Missing Logging and Audit Trails

# What's missing — no CloudTrail, no VPC flow logs
# If you don't have these resources, you're flying blind

resource "aws_cloudtrail" "main" {
  name                          = "main-trail"
  s3_bucket_name                = aws_s3_bucket.trail.id
  include_global_service_events = true
  is_multi_region_trail         = true
  enable_log_file_validation    = true
}

resource "aws_flow_log" "vpc" {
  vpc_id          = aws_vpc.main.id
  traffic_type    = "ALL"
  log_destination = aws_cloudwatch_log_group.flow.arn
}
Enter fullscreen mode Exit fullscreen mode

CloudTrail is non-negotiable. Without it, you have no audit trail of who did what in your AWS account. VPC Flow Logs on production VPCs. S3 access logging on buckets with sensitive data.

CloudGuard rule LG-001 catches missing CloudTrail configuration. LG-004 flags VPCs without flow logs. LG-007 detects S3 buckets without access logging.

6. Compliance Mapping

Every CloudGuard finding is tagged with relevant compliance framework sections:

Finding SOC2 HIPAA PCI-DSS
Public S3 bucket CC6.1 164.312(a)(1) Req 7
Wildcard IAM CC6.3 164.312(a)(1) Req 7.1
Open security group CC6.6 164.312(e)(1) Req 1.2
Unencrypted RDS CC6.1 164.312(a)(2)(iv) Req 3.4
No CloudTrail CC7.2 164.312(b) Req 10.1

Run a scan, get a compliance report. No manual mapping required.

Run It On Your Infrastructure

clawhub install cloudguard
cloudguard scan infra/
Enter fullscreen mode Exit fullscreen mode

Example output:

$ cloudguard scan infra/

[CRITICAL] S3-001 Bucket with public-read ACL — storage.tf:15
[CRITICAL] IM-003 IAM policy: Action * on Resource * — iam/admin-role.tf:22
[HIGH]     SG-001 Security group allows 0.0.0.0/0 on port 22 — network/security.tf:34
[HIGH]     EC-005 RDS without encryption at rest — database/main.tf:18

Score: 38/100 (Grade: F)
Enter fullscreen mode Exit fullscreen mode

Most Terraform codebases I've scanned score below 50.

How It Compares to tfsec and checkov

Both are solid tools. CloudGuard differs in:

  1. Scoring — 0-100 grade per scan, not just pass/fail
  2. Compliance mapping — SOC2/HIPAA/PCI in the report
  3. Pre-commit hooks — bad IaC never merges (Pro tier)
  4. Ecosystem — part of 26 tools, same installer, same philosophy

If tfsec or checkov is working for your team, you probably don't need to switch. CloudGuard is for teams that want scoring + compliance mapping + the broader ecosystem.

Free to scan. Pro ($19/mo) adds pre-commit hooks + compliance reports. 100% local — your infrastructure code is literally a map of your cloud. It should never leave your machine.

CloudGuard | GitHub

Top comments (0)