checkov-scan

Move fast and avoid surprises – Shift-left on cloud security with Policy-as-Code

Security issues can be fatal. Compromises can lead to sensitive information getting into the hands of malicious actors. For companies, it could lead to substantial damage to trust and reputation.

In the series “Move fast and avoid surprises” I’m elaborating on topics and concepts I’ve learned that have had major impact in real-life situations. In the modern software industry, being fast is not sufficient on it’s own, if you get caught up in problems by breaking things along the way. I rather prefer to avoid surprises.

In this article I will share how you can prevent security issues from happening in the first place and keep your AWS environment secure by adopting Policy-as-Code with checkov and Terraform.

Cloud Security Posture Management

In any type of IT infrastructure environment it is vital to have insight into the security posture of all components and resources. Many Cloud Security Posture Management (CSPM) solutions are on the market today and the largest cloud providers all have their own offerings.

Amazon Web Services provides Security Hub, which is a service that centralizes findings from other underlying services such as AWS CloudTrail for API requests, AWS Config for changes of resource properties, Amazon GuardDuty for intelligent threat detection, Amazon Inspector for automated vulnerability management etc.

AWS Security Hub also have standards based on third parties such as CIS Amazon Web Services Benchmarks in addition to AWS Foundational Security Best Practices (FSBP) which is based on Well-Architected principles. With Security Hub you can set up automatic notifications of findings, even enable automatic remediation for pre-defined rules.

The challenge is that when resources already as compromised, it might already be too late. What if a ransomware attack based on an instance with port 3389 open already has spread to other resources in the network? How can we avoid these kinds of surprises? By adopting Policy-as-Code you can detect vulnerable infrastructure configurations before they are provisioned in the first place!

Shift-left with Policy-as-Code

Perform operations as code: In the cloud, you can apply the same engineering discipline that you use for application code to your entire environment. You can define your entire workload (applications, infrastructure, etc.) as code and update it with code. You can script your operations procedures and automate their process by launching them in response to events. By performing operations as code, you limit human error and create consistent responses to events.

First design principle of the Operational Excellence pillar in the AWS Well-Architected Framework.

The first step of the journey is of course to evolve from non-reproducible ClickOps in the web console to Infrastructure-as-Code such as Terraform, Cloudformation, Pulumi etc. When you have infrastructure defined with code you can apply traditional software quality measures such as linting, testing, validation and security scanning.

Some of the most popular Policy-as-Code solutions available today includes:

Policy-as-Code has a broad scope, it can also be used to define permitted resource/instance types, deployment windows and much much more in addition to only security aspects. Open Policy Agent (OPA) can help with “anything” regarding organizational policies. For this article we are focusing on infrastructure security.

Practical example with Checkov, Terraform and AWS

I like checkov because it’s open source, simple to use and it’s CLI can be integrated into any workflow. It supports IaC resources such as Terraform, Cloudformation, Serverless Application Model (SAM), Kubernetes, Helm, ARM Templates ++ and works as a generic tool for all the three major cloud providers. This makes it easy to adopt in Terraform workflows in organizations which may provide services across multiple cloud providers.

checkov can detect usual mistakes such as:

  • Publicly available object storage resources such as Amazon S3 buckets, Google Cloud Storage etc.
  • Resources lacking encryption at rest
  • Access keys/principals committed to version control by accident.
  • Too permissive security group rules.
  • Flaws in Kubernets Helm Charts etc.
  • Dockerfiles not according to best practices:
    • Root
    • Health checks
    • SSH exposure
    • etc.

Installing Checkov from Python Pip is as simple as:

pip3 install checkov

Then kick off a checkov scan, which with the “-d” flag will automatically detect any supported frameworks or resources in the current directory:

checkov -d .

Example one – Amazon S3

To demonstrate what kind of output checkov provides I put together a minimal Terraform code example of a traditional AWS resource such as an S3 bucket.

# AWS S3 bucket example for Checkov
resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
  bucket = "hed-policy-as-code-demo"

  tags = {
    Name  = "hed-policy-as-code-demo"
    Owner = "haakon"
  }
}
checkov --framework terraform -f main.tf  #Scans the specified file main.tf with the terraform framework

Here is the checkov scan output example for the minimal S3 bucket definition. 4 checks passed, 7 checks failed. It’s interesting to see that this is not considered very secure by default (even though some secure properties are added by AWS as a default safeguard, I prefer to have my complete configuration in Infrastructure-as-Code).

checkov --framework terraform -f main.tf
[ terraform framework ]: 100%|████████████████████|[1/1], Current File Scanned=main.tf

       _               _              
   ___| |__   ___  ___| | _______   __
  / __| '_ \ / _ \/ __| |/ / _ \ \ / /
 | (__| | | |  __/ (__|   < (_) \ V / 
  \___|_| |_|\___|\___|_|\_\___/ \_/  
                                      
By Prisma Cloud | version: 3.2.65 

terraform scan results:

Passed checks: 4, Failed checks: 7, Skipped checks: 0

Check: CKV_AWS_93: "Ensure S3 bucket policy does not lockout all but root user. (Prevent lockouts needing root account fixes)"
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-24
Check: CKV_AWS_20: "S3 Bucket has an ACL defined which allows public READ access."
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-1-acl-read-permissions-everyone
Check: CKV_AWS_19: "Ensure all data stored in the S3 bucket is securely encrypted at rest"
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-14-data-encrypted-at-rest
Check: CKV_AWS_57: "S3 Bucket has an ACL defined which allows public WRITE access."
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-2-acl-write-permissions-everyone
Check: CKV_AWS_21: "Ensure all data stored in the S3 bucket have versioning enabled"
        FAILED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-16-enable-versioning

                2 | resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
                3 |   bucket = "hed-policy-as-code-demo"
                4 | 
                5 |   tags = {
                6 |     Name  = "hed-policy-as-code-demo"
                7 |     Owner = "haakon"
                8 |   }
                9 | }
Check: CKV2_AWS_6: "Ensure that S3 bucket has a Public Access block"
        FAILED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/s3-bucket-should-have-public-access-blocks-defaults-to-false-if-the-public-access-block-is-not-attached

                2 | resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
                3 |   bucket = "hed-policy-as-code-demo"
                4 | 
                5 |   tags = {
                6 |     Name  = "hed-policy-as-code-demo"
                7 |     Owner = "haakon"
                8 |   }
                9 | }
Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"
        FAILED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-13-enable-logging

                2 | resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
                3 |   bucket = "hed-policy-as-code-demo"
                4 | 
                5 |   tags = {
                6 |     Name  = "hed-policy-as-code-demo"
                7 |     Owner = "haakon"
                8 |   }
                9 | }
Check: CKV2_AWS_62: "Ensure S3 buckets should have event notifications enabled"
        FAILED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-logging-policies/bc-aws-2-62

                2 | resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
                3 |   bucket = "hed-policy-as-code-demo"
                4 | 
                5 |   tags = {
                6 |     Name  = "hed-policy-as-code-demo"
                7 |     Owner = "haakon"
                8 |   }
                9 | }
Check: CKV2_AWS_61: "Ensure that an S3 bucket has a lifecycle configuration"
        FAILED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-logging-policies/bc-aws-2-61

                2 | resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
                3 |   bucket = "hed-policy-as-code-demo"
                4 | 
                5 |   tags = {
                6 |     Name  = "hed-policy-as-code-demo"
                7 |     Owner = "haakon"
                8 |   }
                9 | }
Check: CKV_AWS_144: "Ensure that S3 bucket has cross-region replication enabled"
        FAILED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-s3-bucket-has-cross-region-replication-enabled

                2 | resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
                3 |   bucket = "hed-policy-as-code-demo"
                4 | 
                5 |   tags = {
                6 |     Name  = "hed-policy-as-code-demo"
                7 |     Owner = "haakon"
                8 |   }
                9 | }
Check: CKV_AWS_145: "Ensure that S3 buckets are encrypted with KMS by default"
        FAILED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-9
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-s3-buckets-are-encrypted-with-kms-by-default

                2 | resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
                3 |   bucket = "hed-policy-as-code-demo"
                4 | 
                5 |   tags = {
                6 |     Name  = "hed-policy-as-code-demo"
                7 |     Owner = "haakon"
                8 |   }
                9 | }

Let’s look into the suggestions and improve the code for finding CKV_AWS_21: “Ensure all data stored in the S3 bucket have versioning enabled”.

This one is reasonable as it’s provides a safety mechanism for accidental deletion. Solution: Add resource aws_s3_bucket_versioning.

Take two:

# AWS S3 bucket example 
resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
  bucket = "hed-policy-as-code-demo"

  tags = {
    Name  = "hed-policy-as-code-demo"
    Owner = "haakon"
  }
}

resource "aws_s3_bucket_versioning" "hed-checkov-s3-bucket-versioning" {
  bucket = aws_s3_bucket.hed-checkov-s3-bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}
$ checkov --framework terraform -f main.tf --quiet
terraform scan results:

Passed checks: 5, Failed checks: 6, Skipped checks: 0

In this run I added the “–quiet” flag to leave out the details since I only cared about the results, not all the details. We are now up to 5 passed checks, 6 still fail. Let’s continue with the same approach and implement improvements for the following checks:

  • CKV2_AWS_6: “Ensure that S3 bucket has a Public Access block”
  • CKV2_AWS_61: “Ensure that an S3 bucket has a lifecycle configuration”
  • CKV_AWS_145: “Ensure that S3 buckets are encrypted with KMS by default”

Excluding irrelevant checks

I consider these findings to not be relevant to the situation and scope. Perhaps there are checks in your project as well that does not align to the project’s goals, or they are too complex? Then we can use the suppress/skip check option in checkov.

  • CKV_AWS_18: “Ensure the S3 bucket has access logging enabled”
  • CKV_AWS_144: “Ensure that S3 bucket has cross-region replication enabled”

skip-check clauses are supported inline in the code base or via the CLI (mutually exclusive). In this example I exclude CKV_AWS_18 in code, which is practical because I can add a comment which justifies the decision. I prefer this decision to be taken as “low” and close to the code as possible.

It’s also possible to exclude checks via the CLI, but one downside is that this may lead to having configuration related to application infrastructure a bit further away from the actual logic, and suddenly you end up with non-standardized parameters in you CI/CD setup.

For demonstration purposes I specify CKV_AWS_144 on the CLI, but do note that in the community edition of checkov it is not possible to combine these two modes in the same execution.

checkov --framework terraform -f main.tf --skip-check CKV_AWS_144 

Refactored code base with inline suppression configuration:

# AWS S3 bucket example for Checkov testing with Terraform
resource "aws_s3_bucket" "hed-checkov-s3-bucket" {
#checkov:skip=CKV_AWS_18: Access logging is not required for this use-case. 
#checkov:skip=CKV2_AWS_62: Event notifications is not required for this use-case. 
#checkov:skip=CKV_AWS_144: Cross-region replication is not required for this use-case. 
  bucket = "hed-policy-as-code-demo"
  lifecycle_rule {
    id      = "expire"
    status  = "Enabled"
    prefix  = "applogs/"
    transition {
      days          = 30
      storage_class = "STANDARD_IA"
    }
    expiration {
      days = 90
    }
  }
  tags = {
    Name  = "hed-policy-as-code-demo"
    Owner = "haakon"
  }
}

resource "aws_s3_bucket_versioning" "hed-checkov-s3-bucket-versioning" {
  bucket = aws_s3_bucket.hed-checkov-s3-bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_public_access_block" "hed-checkov-s3-bucket-public-access-block" {
  bucket = aws_s3_bucket.hed-checkov-s3-bucket.id
  restrict_public_buckets = true
  ignore_public_acls      = true
  block_public_acls       = true
  block_public_policy     = true
}

resource "aws_s3_bucket_server_side_encryption_configuration" "hed-checkov-s3-bucket-sse" {
  bucket = aws_s3_bucket.hed-checkov-s3-bucket.bucket
  rule {
    apply_server_side_encryption_by_default {
      kms_master_key_id = aws_kms_key.hed-checkov-kms-for-s3.arn
      sse_algorithm     = "aws:kms"
    }
  }
}

resource "aws_kms_key" "hed-checkov-kms-for-s3" {
  description         = "KMS key for S3 encryption."
  enable_key_rotation = true
  policy              = data.aws_iam_policy_document.hed-checkov-kms-policy.json
}

Result:

checkov --framework terraform -f main.tf
[ terraform framework ]: 100%|████████████████████|[1/1], Current File Scanned=main.tf

       _               _              
   ___| |__   ___  ___| | _______   __
  / __| '_ \ / _ \/ __| |/ / _ \ \ / /
 | (__| | | |  __/ (__|   < (_) \ V / 
  \___|_| |_|\___|\___|_|\_\___/ \_/  
                                      
By Prisma Cloud | version: 3.2.65 

terraform scan results:

Passed checks: 16, Failed checks: 0, Skipped checks: 3

Check: CKV_AWS_93: "Ensure S3 bucket policy does not lockout all but root user. (Prevent lockouts needing root account fixes)"
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-24
Check: CKV_AWS_56: "Ensure S3 bucket has 'restrict_public_buckets' enabled"
        PASSED for resource: aws_s3_bucket_public_access_block.hed-checkov-s3-bucket-public-access-block
        File: /main.tf:32-38
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-22
Check: CKV_AWS_55: "Ensure S3 bucket has ignore public ACLs enabled"
        PASSED for resource: aws_s3_bucket_public_access_block.hed-checkov-s3-bucket-public-access-block
        File: /main.tf:32-38
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-21
Check: CKV_AWS_54: "Ensure S3 bucket has block public policy enabled"
        PASSED for resource: aws_s3_bucket_public_access_block.hed-checkov-s3-bucket-public-access-block
        File: /main.tf:32-38
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-20
Check: CKV_AWS_53: "Ensure S3 bucket has block public ACLS enabled"
        PASSED for resource: aws_s3_bucket_public_access_block.hed-checkov-s3-bucket-public-access-block
        File: /main.tf:32-38
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-19
Check: CKV_AWS_33: "Ensure KMS key policy does not contain wildcard (*) principal"
        PASSED for resource: aws_kms_key.hed-checkov-kms-for-s3
        File: /main.tf:50-54
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-iam-policies/ensure-kms-key-policy-does-not-contain-wildcard-principal
Check: CKV_AWS_227: "Ensure KMS key is enabled"
        PASSED for resource: aws_kms_key.hed-checkov-kms-for-s3
        File: /main.tf:50-54
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-aws-key-management-service-kms-key-is-enabled
Check: CKV_AWS_7: "Ensure rotation for customer created CMKs is enabled"
        PASSED for resource: aws_kms_key.hed-checkov-kms-for-s3
        File: /main.tf:50-54
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-logging-policies/logging-8
Check: CKV_AWS_20: "S3 Bucket has an ACL defined which allows public READ access."
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-1-acl-read-permissions-everyone
Check: CKV_AWS_21: "Ensure all data stored in the S3 bucket have versioning enabled"
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-16-enable-versioning
Check: CKV_AWS_19: "Ensure all data stored in the S3 bucket is securely encrypted at rest"
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-14-data-encrypted-at-rest
Check: CKV2_AWS_64: "Ensure KMS key Policy is defined"
        PASSED for resource: aws_kms_key.hed-checkov-kms-for-s3
        File: /main.tf:50-54
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-iam-policies/bc-aws-2-64
Check: CKV2_AWS_61: "Ensure that an S3 bucket has a lifecycle configuration"
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-logging-policies/bc-aws-2-61
Check: CKV_AWS_57: "S3 Bucket has an ACL defined which allows public WRITE access."
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-2-acl-write-permissions-everyone
Check: CKV2_AWS_6: "Ensure that S3 bucket has a Public Access block"
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/s3-bucket-should-have-public-access-blocks-defaults-to-false-if-the-public-access-block-is-not-attached
Check: CKV_AWS_145: "Ensure that S3 buckets are encrypted with KMS by default"
        PASSED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-s3-buckets-are-encrypted-with-kms-by-default
Check: CKV2_AWS_62: "Ensure S3 buckets should have event notifications enabled"
        SKIPPED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        Suppress comment:  Event notifications is not required for this use-case. 
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-logging-policies/bc-aws-2-62
Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"
        SKIPPED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        Suppress comment:  Access logging is not required for this use-case. 
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-13-enable-logging
Check: CKV_AWS_144: "Ensure that S3 bucket has cross-region replication enabled"
        SKIPPED for resource: aws_s3_bucket.hed-checkov-s3-bucket
        Suppress comment:  Cross-region replication is not required for this use-case. 
        File: /main.tf:2-23
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-s3-bucket-has-cross-region-replication-enabled

Three checks were skipped, the remaining ones now all passed, exit code becomes 0 and the resources are good to go to be promoted to the next stage for deployment (intentionally left out of this example).

Example two – Insecure security group rules

Let’ say you have a general CI/CD pipeline with checkov integrated, you can now also detect and stop security group violations according to the CIS Benchmark for AWS before each rule is deployed and the attached resource becoming exposed!

// AWS VPC with default settings
resource "aws_vpc" "hed-checkov-vpc" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "hed-checkov-vpc"
  }
}

// AWS EC2 security group with ssh open to anyone
resource "aws_security_group" "hed-checkov-sg" {
  name        = "hed-checkov-sg"
  description = "Allow SSH inbound traffic"
  vpc_id      = aws_vpc.hed-checkov-vpc.id

  ingress {
    description = "SSH from anywhere - I know this is bad but it's soo conventient. I'll lock it up after the workshop, I promise!"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "hed-checkov-sg"
  }
}

Execution result:

checkov --framework terraform -f vpc.tf --compact
[ terraform framework ]: 100%|████████████████████|[1/1], Current File Scanned=vpc.tf

       _               _              
   ___| |__   ___  ___| | _______   __
  / __| '_ \ / _ \/ __| |/ / _ \ \ / /
 | (__| | | |  __/ (__|   < (_) \ V / 
  \___|_| |_|\___|\___|_|\_\___/ \_/  
                                      
By Prisma Cloud | version: 3.2.65 

terraform scan results:

Passed checks: 3, Failed checks: 5, Skipped checks: 0

Check: CKV_AWS_277: "Ensure no security groups allow ingress from 0.0.0.0:0 to port -1"
        PASSED for resource: aws_security_group.hed-checkov-sg
        File: /vpc.tf:10-33
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/ensure-aws-security-group-does-not-allow-all-traffic-on-all-ports
Check: CKV_AWS_260: "Ensure no security groups allow ingress from 0.0.0.0:0 to port 80"
        PASSED for resource: aws_security_group.hed-checkov-sg
        File: /vpc.tf:10-33
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/ensure-aws-security-groups-do-not-allow-ingress-from-00000-to-port-80
Check: CKV_AWS_25: "Ensure no security groups allow ingress from 0.0.0.0:0 to port 3389"
        PASSED for resource: aws_security_group.hed-checkov-sg
        File: /vpc.tf:10-33
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/networking-2
Check: CKV_AWS_23: "Ensure every security groups rule has a description"
        FAILED for resource: aws_security_group.hed-checkov-sg
        File: /vpc.tf:10-33
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/networking-31
Check: CKV_AWS_24: "Ensure no security groups allow ingress from 0.0.0.0:0 to port 22"
        FAILED for resource: aws_security_group.hed-checkov-sg
        File: /vpc.tf:10-33
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/networking-1-port-security
Check: CKV2_AWS_11: "Ensure VPC flow logging is enabled in all VPCs"
        FAILED for resource: aws_vpc.hed-checkov-vpc
        File: /vpc.tf:2-7
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-logging-policies/logging-9-enable-vpc-flow-logging
Check: CKV2_AWS_12: "Ensure the default security group of every VPC restricts all traffic"
        FAILED for resource: aws_vpc.hed-checkov-vpc
        File: /vpc.tf:2-7
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/networking-4
Check: CKV2_AWS_5: "Ensure that Security Groups are attached to another resource"
        FAILED for resource: aws_security_group.hed-checkov-sg
        File: /vpc.tf:10-33
        Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/ensure-that-security-groups-are-attached-to-ec2-instances-or-elastic-network-interfaces-enis

The result shows us multiple areas for improvement:

  • It’s best practice to include a description for every security group rule so that the person coming after you can understand the meaning of the rule. No description? You bet no one dares to touch that rule in the future and you end up with a mess.
  • Ensure port 22 is not openly available.
  • Ensure VPC flow logging is enabled in all VPC as well, otherwise Amazon GuardDuty won’t have any data to detect potential malicious traffic.
  • When creating a new VPC a default security group is provisioned. This one should be locked down by default and not used or shared across services. It’s recommended to create service/application security groups instead.
  • And finally, to avoid having a lot of security groups lying around, it’s recommended that the security group is attached to a resource. Comes back to my last point about service/application specific security groups.

Using “publicly available port 22” as reference, this violation is related to the following AWS Security Hub controls in the CIS AWS Foundations Benchmark, PCI DSS and NIST:

The corresponding AWS Config rule is called restricted-ssh.

Practical advice with checkov for Terraform and AWS

Disable scanning of third-party modules to keep the scope to code you have control over. It also speeds up the process.

The Terraform resource scans policy index contains a complete overview of more than 2000 available checks.

Adopt checkov in your CI/CD pipelines. Here is an excerpt sample step for GitHub Actions, which skips check ID CKV_AWS_162.
For full resource documentation see https://github.com/bridgecrewio/checkov-action.
A full working example can be found in my previous blog post https://hedrange.com/2023/10/07/adopt-open-id-connect-oidc-in-terraform-for-secure-multi-account-ci-cd-to-aws/.

    steps:
      - name: Checkov GitHub Action
        uses: bridgecrewio/checkov-action@v12
        with:
          # Run scan on all checks but a specific check identifier (comma separated)
          # skip_check: CKV_AWS_162
          download_external_modules: false
          quiet: false

checkov supports multiple output formats such as JUnit XML and GitLab SAST, which can display checkov findings directly in your pull/merge request web interface dialogue!

The ultimate state of shifting left is of course to detect discrepancies before the code is actually committed. The terraform_checkov hook in pre-commit-terraform by Anton Babenko is awesome. I highly recommend to adopt this from day one in infrastructure projects.

Here is a sample .pre-commit-config.yaml file which includes formatting, validation, docs, merge conflict and trailing whitespace improvements.

fail_fast: true stops execution after the first failed check.

fail_fast: true
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.77.3
    hooks:
      - id: terraform_fmt
      - id: terraform_validate
      - id: terraform_docs
        args: ["--args=--sort-by required"]
      - id: terraform_checkov
        args:
          - --args=--quiet --skip-check CKV_AWS_111,CKV_AWS_110,CKV_AWS_109,CKV_AWS_108
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: check-merge-conflict
      - id: trailing-whitespace
        args: [--markdown-linebreak-ext=md]
      - id: mixed-line-ending
        args: ["--fix=lf"]

Conclusion

In this article we saw how we can avoid security surprises by “fixing problems before the appear” by shifting left and adopting Policy-as-Code, including practical advice about how checkov can be adopted both in CI/CD pipelines and pre-commit.

By ensuring infrastructure is provisioned securely in the first place we can save a lot of time by not having to go back and fix fundamental issues, like replacing database clusters since we forgot to enable encryption. This should help you to be able to move faster and dedicate more time to develop business value.

Hope this will be as useful for you as it as been to me and other teams. Reach out on LinkedIn if you have any questions, comments or feedback.

Resources


Posted

in

,

by

Tags: