Skip to content

Commit

Permalink
feat(terraform): New checks (#6720)
Browse files Browse the repository at this point in the history
* Add new check

* Update S3GlobalViewACL.py

* Fix ID

* Add another check

* New check

* Add another check

* Fix test

* Fix flake8 issues
  • Loading branch information
tsmithv11 authored Sep 19, 2024
1 parent 4944805 commit 0262d6d
Show file tree
Hide file tree
Showing 13 changed files with 594 additions and 1 deletion.
30 changes: 30 additions & 0 deletions checkov/terraform/checks/resource/aws/ELBwListenerNotTLSSSL.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from __future__ import annotations

from typing import Any

from checkov.common.models.enums import CheckCategories, CheckResult
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck


class ELBwListenerNotTLSSSL(BaseResourceCheck):
def __init__(self) -> None:
name = "Ensure AWS Elastic Load Balancer listener uses TLS/SSL"
id = "CKV_AWS_376"
supported_resource = ("aws_elb",)
categories = (CheckCategories.NETWORKING,)
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resource)

def scan_resource_conf(self, conf: dict[str, list[Any]]) -> CheckResult:
if 'listener' in conf:
for listener in conf.get('listener'):
if 'instance_protocol' in listener:
if listener.get('instance_protocol')[0].lower() in ('http', 'tcp'):
return CheckResult.FAILED
if listener.get('instance_protocol')[0].lower() in ('https', 'ssl') and \
('ssl_certificate_id' not in listener or listener.get('ssl_certificate_id') == ""):
return CheckResult.FAILED

return CheckResult.PASSED


check = ELBwListenerNotTLSSSL()
22 changes: 22 additions & 0 deletions checkov/terraform/checks/resource/aws/LBTargetGroup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import Any, List

from checkov.common.models.enums import CheckCategories
from checkov.terraform.checks.resource.base_resource_negative_value_check import BaseResourceNegativeValueCheck


class LBTargetGroup(BaseResourceNegativeValueCheck):
def __init__(self) -> None:
name = "Ensure AWS Load Balancer doesn't use HTTP protocol"
id = "CKV_AWS_378"
supported_resources = ('aws_lb_target_group', 'aws_alb_target_group',)
categories = (CheckCategories.NETWORKING,)
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)

def get_inspected_key(self) -> str:
return 'protocol'

def get_forbidden_values(self) -> List[Any]:
return ["HTTP"]


check = LBTargetGroup()
22 changes: 22 additions & 0 deletions checkov/terraform/checks/resource/aws/Route53TransferLock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import Any, List

from checkov.common.models.enums import CheckCategories
from checkov.terraform.checks.resource.base_resource_negative_value_check import BaseResourceNegativeValueCheck


class Route53TransferLock(BaseResourceNegativeValueCheck):
def __init__(self) -> None:
name = "Ensure Route 53 domains have transfer lock protection"
id = "CKV_AWS_377"
supported_resources = ('aws_route53domains_registered_domain',)
categories = (CheckCategories.NETWORKING,)
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)

def get_inspected_key(self) -> str:
return 'transfer_lock'

def get_forbidden_values(self) -> List[Any]:
return [False]


check = Route53TransferLock()
31 changes: 31 additions & 0 deletions checkov/terraform/checks/resource/aws/S3GlobalViewACL.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from __future__ import annotations

from typing import Any

from checkov.common.models.enums import CheckCategories, CheckResult
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck


class S3GlobalViewACL(BaseResourceCheck):
def __init__(self) -> None:
name = "Ensure AWS S3 bucket does not have global view ACL permissions enabled"
id = "CKV_AWS_375"
supported_resource = ("aws_s3_bucket_acl",)
categories = (CheckCategories.NETWORKING,)
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resource)

def scan_resource_conf(self, conf: dict[str, list[Any]]) -> CheckResult:
if 'access_control_policy' in conf:
for policy in conf.get('access_control_policy'):
if 'grant' in policy:
for grant in policy.get('grant'):
if 'permission' in grant and ('FULL_CONTROL' in grant.get('permission') or 'READ_ACP' in grant.get('permission')):
if 'grantee' in grant:
for grantee in grant.get('grantee'):
if 'uri' in grantee and 'http://acs.amazonaws.com/groups/global/AllUsers' in grantee.get('uri'):
return CheckResult.FAILED

return CheckResult.PASSED


check = S3GlobalViewACL()
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Fail: 1 bad, 1 good
resource "aws_elb" "fail" {
name = "foobar-terraform-elb"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]

listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 80
lb_protocol = "http"
}

listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 443
lb_protocol = "https"
ssl_certificate_id = "foo"
}

health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
target = "HTTP:8000/"
interval = 30
}

instances = [aws_instance.foo.id]
cross_zone_load_balancing = true
idle_timeout = 400
connection_draining = true
connection_draining_timeout = 400
}

# Fail: 1 has cert, 1 doesn't
resource "aws_elb" "fail2" {
name = "foobar-terraform-elb"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]

listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 80
lb_protocol = "https"
ssl_certificate_id = "foo"
}

listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 443
lb_protocol = "https"
ssl_certificate_id = ""
}

health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
target = "HTTP:8000/"
interval = 30
}

instances = [aws_instance.foo.id]
cross_zone_load_balancing = true
idle_timeout = 400
connection_draining = true
connection_draining_timeout = 400
}

# Fail: 1 has cert, 1 doesn't
resource "aws_elb" "fail3" {
name = "foobar-terraform-elb"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]

listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 80
lb_protocol = "https"
ssl_certificate_id = "foo"
}

listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 443
lb_protocol = "https"
}

health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
target = "HTTP:8000/"
interval = 30
}

instances = [aws_instance.foo.id]
cross_zone_load_balancing = true
idle_timeout = 400
connection_draining = true
connection_draining_timeout = 400
}

# Pass: SSL and has cert
resource "aws_elb" "pass" {
name = "foobar-terraform-elb"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]

listener {
instance_port = 8000
instance_protocol = "SSL"
lb_port = 80
lb_protocol = "https"
ssl_certificate_id = "foo"
}

health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
target = "HTTP:8000/"
interval = 30
}

instances = [aws_instance.foo.id]
cross_zone_load_balancing = true
idle_timeout = 400
connection_draining = true
connection_draining_timeout = 400
}
32 changes: 32 additions & 0 deletions tests/terraform/checks/resource/aws/example_LBTargetGroup/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
resource "aws_lb_target_group" "fail" {
name = "tf-example-lb-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
}

resource "aws_lb_target_group" "pass" {
name = "tf-example-lb-alb-tg"
target_type = "alb"
port = 80
protocol = "TCP"
vpc_id = aws_vpc.main.id
}

resource "aws_alb_target_group" "fail" {
name = "tf-example-lb-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
}

resource "aws_alb_target_group" "pass" {
name = "tf-example-lb-nlb-tg"
port = 25
protocol = "TCP"
vpc_id = aws_vpc.main.id

target_health_state {
enable_unhealthy_connection_termination = false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
resource "aws_route53domains_registered_domain" "pass_missing" {
domain_name = "example.com"

name_server {
name = "ns-195.awsdns-24.com"
}

name_server {
name = "ns-874.awsdns-45.net"
}

tags = {
Environment = "test"
}
}

resource "aws_route53domains_registered_domain" "pass_true" {
domain_name = "example.com"
transfer_lock = true

name_server {
name = "ns-195.awsdns-24.com"
}

name_server {
name = "ns-874.awsdns-45.net"
}

tags = {
Environment = "test"
}
}

resource "aws_route53domains_registered_domain" "fail" {
domain_name = "example.com"
transfer_lock = false

name_server {
name = "ns-195.awsdns-24.com"
}

name_server {
name = "ns-874.awsdns-45.net"
}

tags = {
Environment = "test"
}
}
Loading

0 comments on commit 0262d6d

Please sign in to comment.