mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 23:05:05 +00:00
feat(tags): add resource tags to E services (#2007)
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
This commit is contained in:
@@ -10,6 +10,7 @@ class ec2_ami_public(Check):
|
||||
report.region = image.region
|
||||
report.resource_id = image.id
|
||||
report.resource_arn = image.arn
|
||||
report.resource_tags = image.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EC2 AMI {image.id} is not public."
|
||||
if image.public:
|
||||
|
||||
@@ -9,6 +9,7 @@ class ec2_ebs_public_snapshot(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = snapshot.region
|
||||
report.resource_arn = snapshot.arn
|
||||
report.resource_tags = snapshot.tags
|
||||
if not snapshot.public:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EBS Snapshot {snapshot.id} is not Public."
|
||||
|
||||
@@ -9,6 +9,7 @@ class ec2_ebs_snapshots_encrypted(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = snapshot.region
|
||||
report.resource_arn = snapshot.arn
|
||||
report.resource_tags = snapshot.tags
|
||||
if snapshot.encrypted:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EBS Snapshot {snapshot.id} is encrypted."
|
||||
|
||||
@@ -10,6 +10,7 @@ class ec2_ebs_volume_encryption(Check):
|
||||
report.region = volume.region
|
||||
report.resource_id = volume.id
|
||||
report.resource_arn = volume.arn
|
||||
report.resource_tags = volume.tags
|
||||
if volume.encrypted:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EBS Snapshot {volume.id} is encrypted."
|
||||
|
||||
@@ -16,6 +16,7 @@ class ec2_elastic_ip_shodan(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = eip.region
|
||||
report.resource_arn = eip.arn
|
||||
report.resource_tags = eip.tags
|
||||
if eip.public_ip:
|
||||
try:
|
||||
shodan_info = api.host(eip.public_ip)
|
||||
|
||||
@@ -11,6 +11,7 @@ class ec2_elastic_ip_unassgined(Check):
|
||||
if eip.public_ip:
|
||||
report.resource_id = eip.public_ip
|
||||
report.resource_arn = eip.arn
|
||||
report.resource_tags = eip.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Elastic IP {eip.public_ip} is not associated with an instance or network interface."
|
||||
if eip.association_id:
|
||||
|
||||
@@ -10,6 +10,7 @@ class ec2_instance_imdsv2_enabled(Check):
|
||||
report.region = instance.region
|
||||
report.resource_id = instance.id
|
||||
report.resource_arn = instance.arn
|
||||
report.resource_tags = instance.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"EC2 Instance {instance.id} has IMDSv2 disabled or not required."
|
||||
|
||||
@@ -10,6 +10,7 @@ class ec2_instance_internet_facing_with_instance_profile(Check):
|
||||
report.region = instance.region
|
||||
report.resource_id = instance.id
|
||||
report.resource_arn = instance.arn
|
||||
report.resource_tags = instance.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EC2 Instance {instance.id} is not internet facing with an instance profile."
|
||||
if instance.public_ip and instance.instance_profile:
|
||||
|
||||
@@ -10,6 +10,7 @@ class ec2_instance_managed_by_ssm(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = instance.region
|
||||
report.resource_arn = instance.arn
|
||||
report.resource_tags = instance.tags
|
||||
if not ssm_client.managed_instances.get(instance.id):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
|
||||
@@ -14,6 +14,7 @@ class ec2_instance_older_than_specific_days(Check):
|
||||
report.region = instance.region
|
||||
report.resource_id = instance.id
|
||||
report.resource_arn = instance.arn
|
||||
report.resource_tags = instance.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EC2 Instance {instance.id} is not running."
|
||||
if instance.state == "running":
|
||||
|
||||
@@ -10,6 +10,7 @@ class ec2_instance_profile_attached(Check):
|
||||
report.region = instance.region
|
||||
report.resource_id = instance.id
|
||||
report.resource_arn = instance.arn
|
||||
report.resource_tags = instance.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"EC2 Instance {instance.id} not associated with an Instance Profile Role."
|
||||
if instance.instance_profile:
|
||||
|
||||
@@ -9,6 +9,7 @@ class ec2_instance_public_ip(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = instance.region
|
||||
report.resource_arn = instance.arn
|
||||
report.resource_tags = instance.tags
|
||||
if instance.public_ip:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"EC2 Instance {instance.id} has a Public IP: {instance.public_ip} ({instance.public_dns})."
|
||||
|
||||
@@ -18,7 +18,7 @@ class ec2_instance_secrets_user_data(Check):
|
||||
report.region = instance.region
|
||||
report.resource_id = instance.id
|
||||
report.resource_arn = instance.arn
|
||||
|
||||
report.resource_tags = instance.tags
|
||||
if instance.user_data:
|
||||
temp_user_data_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
user_data = b64decode(instance.user_data)
|
||||
|
||||
@@ -13,6 +13,7 @@ class ec2_networkacl_allow_ingress_any_port(Check):
|
||||
report.region = network_acl.region
|
||||
report.resource_id = network_acl.id
|
||||
report.resource_arn = network_acl.arn
|
||||
report.resource_tags = network_acl.tags
|
||||
# If some entry allows it, that ACL is not securely configured
|
||||
if not check_network_acl(network_acl.entries, tcp_protocol, check_port):
|
||||
report.status = "PASS"
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_networkacl_allow_ingress_tcp_port_22(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = network_acl.region
|
||||
report.resource_arn = network_acl.arn
|
||||
report.resource_tags = network_acl.tags
|
||||
# If some entry allows it, that ACL is not securely configured
|
||||
if not check_network_acl(network_acl.entries, tcp_protocol, check_port):
|
||||
report.status = "PASS"
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_networkacl_allow_ingress_tcp_port_3389(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = network_acl.region
|
||||
report.resource_arn = network_acl.arn
|
||||
report.resource_tags = network_acl.tags
|
||||
# If some entry allows it, that ACL is not securely configured
|
||||
if not check_network_acl(network_acl.entries, tcp_protocol, check_port):
|
||||
report.status = "PASS"
|
||||
|
||||
@@ -13,6 +13,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_any_port(Check):
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not all ports open to the Internet."
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(ingress_rule, "-1", any_address=True):
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018(
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not MongoDB ports 27017 and 27018 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -14,6 +14,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21(Check)
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not FTP ports 20 and 21 open to the Internet."
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(
|
||||
|
||||
@@ -14,6 +14,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22(Check):
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not SSH port 22 open to the Internet."
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(
|
||||
|
||||
@@ -14,6 +14,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389(Check):
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Microsoft RDP port 3389 open to the Internet."
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
# Loop through every security group's ingress rule and check it
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(
|
||||
|
||||
@@ -14,6 +14,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Casandra ports 7199, 8888 and 9160 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -14,6 +14,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_ki
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Elasticsearch/Kibana ports 9200, 9300 and 5601 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092(Check
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Kafka port 9092 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211(
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Memcached port 11211 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306(Check
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not MySQL port 3306 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Oracle ports 1521 and 2483 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432(Ch
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Postgres port 5432 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379(Check
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Redis port 6379 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -14,6 +14,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Microsoft SQL Server ports 1433 and 1434 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23(Check)
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Telnet port 23 open to the Internet."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -13,6 +13,7 @@ class ec2_securitygroup_allow_wide_open_public_ipv4(Check):
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has no potential wide-open non-RFC1918 address."
|
||||
# Loop through every security group's ingress rule and check it
|
||||
|
||||
@@ -11,6 +11,7 @@ class ec2_securitygroup_default_restrict_traffic(Check):
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
# Find default security group
|
||||
if security_group.name == "default":
|
||||
report.status = "PASS"
|
||||
|
||||
@@ -10,6 +10,7 @@ class ec2_securitygroup_from_launch_wizard(Check):
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) was not created using the EC2 Launch Wizard."
|
||||
if "launch-wizard" in security_group.name:
|
||||
|
||||
@@ -11,6 +11,7 @@ class ec2_securitygroup_in_use_without_ingress_filtering(Check):
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has ingress filtering."
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_not_used(Check):
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) it is being used."
|
||||
if len(security_group.network_interfaces) == 0:
|
||||
|
||||
@@ -12,6 +12,7 @@ class ec2_securitygroup_with_many_ingress_egress_rules(Check):
|
||||
report.region = security_group.region
|
||||
report.resource_id = security_group.id
|
||||
report.resource_arn = security_group.arn
|
||||
report.resource_tags = security_group.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has {len(security_group.ingress_rules)} inbound rules and {len(security_group.egress_rules)} outbound rules"
|
||||
if (
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from botocore.client import ClientError
|
||||
from pydantic import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
|
||||
@@ -86,20 +88,21 @@ class EC2:
|
||||
|
||||
self.instances.append(
|
||||
Instance(
|
||||
instance["InstanceId"],
|
||||
arn,
|
||||
instance["State"]["Name"],
|
||||
regional_client.region,
|
||||
instance["InstanceType"],
|
||||
instance["ImageId"],
|
||||
instance["LaunchTime"],
|
||||
instance["PrivateDnsName"],
|
||||
private_ip,
|
||||
public_dns,
|
||||
public_ip,
|
||||
http_tokens,
|
||||
http_endpoint,
|
||||
instance_profile,
|
||||
id=instance["InstanceId"],
|
||||
arn=arn,
|
||||
state=instance["State"]["Name"],
|
||||
region=regional_client.region,
|
||||
type=instance["InstanceType"],
|
||||
image_id=instance["ImageId"],
|
||||
launch_time=instance["LaunchTime"],
|
||||
private_dns=instance["PrivateDnsName"],
|
||||
private_ip=private_ip,
|
||||
public_dns=public_dns,
|
||||
public_ip=public_ip,
|
||||
http_tokens=http_tokens,
|
||||
http_endpoint=http_endpoint,
|
||||
instance_profile=instance_profile,
|
||||
tags=instance.get("Tags"),
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -121,12 +124,13 @@ class EC2:
|
||||
):
|
||||
self.security_groups.append(
|
||||
SecurityGroup(
|
||||
sg["GroupName"],
|
||||
arn,
|
||||
regional_client.region,
|
||||
sg["GroupId"],
|
||||
sg["IpPermissions"],
|
||||
sg["IpPermissionsEgress"],
|
||||
name=sg["GroupName"],
|
||||
arn=arn,
|
||||
region=regional_client.region,
|
||||
id=sg["GroupId"],
|
||||
ingress_rules=sg["IpPermissions"],
|
||||
egress_rules=sg["IpPermissionsEgress"],
|
||||
tags=sg.get("Tags"),
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -148,10 +152,11 @@ class EC2:
|
||||
):
|
||||
self.network_acls.append(
|
||||
NetworkACL(
|
||||
nacl["NetworkAclId"],
|
||||
arn,
|
||||
regional_client.region,
|
||||
nacl["Entries"],
|
||||
id=nacl["NetworkAclId"],
|
||||
arn=arn,
|
||||
region=regional_client.region,
|
||||
entries=nacl["Entries"],
|
||||
tags=nacl.get("Tags"),
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -176,10 +181,11 @@ class EC2:
|
||||
encrypted = True
|
||||
self.snapshots.append(
|
||||
Snapshot(
|
||||
snapshot["SnapshotId"],
|
||||
arn,
|
||||
regional_client.region,
|
||||
encrypted,
|
||||
id=snapshot["SnapshotId"],
|
||||
arn=arn,
|
||||
region=regional_client.region,
|
||||
encrypted=encrypted,
|
||||
tags=snapshot.get("Tags"),
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -264,11 +270,12 @@ class EC2:
|
||||
public = True
|
||||
self.images.append(
|
||||
Image(
|
||||
image["ImageId"],
|
||||
arn,
|
||||
image["Name"],
|
||||
public,
|
||||
regional_client.region,
|
||||
id=image["ImageId"],
|
||||
arn=arn,
|
||||
name=image["Name"],
|
||||
public=public,
|
||||
region=regional_client.region,
|
||||
tags=image.get("Tags"),
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -290,10 +297,11 @@ class EC2:
|
||||
):
|
||||
self.volumes.append(
|
||||
Volume(
|
||||
volume["VolumeId"],
|
||||
arn,
|
||||
regional_client.region,
|
||||
volume["Encrypted"],
|
||||
id=volume["VolumeId"],
|
||||
arn=arn,
|
||||
region=regional_client.region,
|
||||
encrypted=volume["Encrypted"],
|
||||
tags=volume.get("Tags"),
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -320,11 +328,12 @@ class EC2:
|
||||
):
|
||||
self.elastic_ips.append(
|
||||
ElasticIP(
|
||||
public_ip,
|
||||
association_id,
|
||||
allocation_id,
|
||||
elastic_ip_arn,
|
||||
regional_client.region,
|
||||
public_ip=public_ip,
|
||||
association_id=association_id,
|
||||
allocation_id=allocation_id,
|
||||
arn=elastic_ip_arn,
|
||||
region=regional_client.region,
|
||||
tags=address.get("Tags"),
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -337,10 +346,10 @@ class EC2:
|
||||
try:
|
||||
self.ebs_encryption_by_default.append(
|
||||
EbsEncryptionByDefault(
|
||||
regional_client.get_ebs_encryption_by_default()[
|
||||
status=regional_client.get_ebs_encryption_by_default()[
|
||||
"EbsEncryptionByDefault"
|
||||
],
|
||||
regional_client.region,
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -349,159 +358,79 @@ class EC2:
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Instance:
|
||||
class Instance(BaseModel):
|
||||
id: str
|
||||
arn: str
|
||||
state: str
|
||||
region: str
|
||||
type: str
|
||||
image_id: str
|
||||
launch_time: str
|
||||
launch_time: datetime
|
||||
private_dns: str
|
||||
private_ip: str
|
||||
public_dns: str
|
||||
public_ip: str
|
||||
user_data: str
|
||||
http_tokens: str
|
||||
http_endpoint: str
|
||||
instance_profile: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id,
|
||||
arn,
|
||||
state,
|
||||
region,
|
||||
type,
|
||||
image_id,
|
||||
launch_time,
|
||||
private_dns,
|
||||
private_ip,
|
||||
public_dns,
|
||||
public_ip,
|
||||
http_tokens,
|
||||
http_endpoint,
|
||||
instance_profile,
|
||||
):
|
||||
self.id = id
|
||||
self.arn = arn
|
||||
self.state = state
|
||||
self.region = region
|
||||
self.type = type
|
||||
self.image_id = image_id
|
||||
self.launch_time = launch_time
|
||||
self.private_dns = private_dns
|
||||
self.private_ip = private_ip
|
||||
self.public_dns = public_dns
|
||||
self.public_ip = public_ip
|
||||
self.http_tokens = http_tokens
|
||||
self.http_endpoint = http_endpoint
|
||||
self.user_data = None
|
||||
self.instance_profile = instance_profile
|
||||
private_ip: Optional[str]
|
||||
public_dns: Optional[str]
|
||||
public_ip: Optional[str]
|
||||
user_data: Optional[str]
|
||||
http_tokens: Optional[str]
|
||||
http_endpoint: Optional[str]
|
||||
instance_profile: Optional[dict]
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
@dataclass
|
||||
class Snapshot:
|
||||
class Snapshot(BaseModel):
|
||||
id: str
|
||||
arn: str
|
||||
region: str
|
||||
encrypted: bool
|
||||
public: bool
|
||||
|
||||
def __init__(self, id, arn, region, encrypted):
|
||||
self.id = id
|
||||
self.arn = arn
|
||||
self.region = region
|
||||
self.encrypted = encrypted
|
||||
self.public = False
|
||||
public: bool = False
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
@dataclass
|
||||
class Volume:
|
||||
class Volume(BaseModel):
|
||||
id: str
|
||||
arn: str
|
||||
region: str
|
||||
encrypted: bool
|
||||
|
||||
def __init__(self, id, arn, region, encrypted):
|
||||
self.id = id
|
||||
self.arn = arn
|
||||
self.region = region
|
||||
self.encrypted = encrypted
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
@dataclass
|
||||
class SecurityGroup:
|
||||
class SecurityGroup(BaseModel):
|
||||
name: str
|
||||
arn: str
|
||||
region: str
|
||||
id: str
|
||||
network_interfaces: list[str]
|
||||
network_interfaces: list[str] = []
|
||||
ingress_rules: list[dict]
|
||||
egress_rules: list[dict]
|
||||
|
||||
def __init__(self, name, arn, region, id, ingress_rules, egress_rules):
|
||||
self.name = name
|
||||
self.arn = arn
|
||||
self.region = region
|
||||
self.id = id
|
||||
self.ingress_rules = ingress_rules
|
||||
self.egress_rules = egress_rules
|
||||
self.network_interfaces = []
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
@dataclass
|
||||
class NetworkACL:
|
||||
class NetworkACL(BaseModel):
|
||||
id: str
|
||||
arn: str
|
||||
region: str
|
||||
entries: list[dict]
|
||||
|
||||
def __init__(self, id, arn, region, entries):
|
||||
self.id = id
|
||||
self.arn = arn
|
||||
self.region = region
|
||||
self.entries = entries
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
@dataclass
|
||||
class ElasticIP:
|
||||
public_ip: str
|
||||
association_id: str
|
||||
class ElasticIP(BaseModel):
|
||||
public_ip: Optional[str]
|
||||
association_id: Optional[str]
|
||||
arn: str
|
||||
allocation_id: str
|
||||
allocation_id: Optional[str]
|
||||
region: str
|
||||
|
||||
def __init__(self, public_ip, association_id, allocation_id, arn, region):
|
||||
self.public_ip = public_ip
|
||||
self.association_id = association_id
|
||||
self.allocation_id = allocation_id
|
||||
self.arn = arn
|
||||
self.region = region
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
@dataclass
|
||||
class Image:
|
||||
class Image(BaseModel):
|
||||
id: str
|
||||
arn: str
|
||||
name: str
|
||||
public: bool
|
||||
region: str
|
||||
|
||||
def __init__(self, id, arn, name, public, region):
|
||||
self.id = id
|
||||
self.arn = arn
|
||||
self.name = name
|
||||
self.public = public
|
||||
self.region = region
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
@dataclass
|
||||
class EbsEncryptionByDefault:
|
||||
class EbsEncryptionByDefault(BaseModel):
|
||||
status: bool
|
||||
region: str
|
||||
|
||||
def __init__(self, status, region):
|
||||
self.status = status
|
||||
self.region = region
|
||||
|
||||
@@ -10,6 +10,7 @@ class ecr_repositories_lifecycle_policy_enabled(Check):
|
||||
report.region = repository.region
|
||||
report.resource_id = repository.name
|
||||
report.resource_arn = repository.arn
|
||||
report.resource_tags = repository.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Repository {repository.name} has no lifecycle policy"
|
||||
|
||||
@@ -10,6 +10,7 @@ class ecr_repositories_not_publicly_accessible(Check):
|
||||
report.region = repository.region
|
||||
report.resource_id = repository.name
|
||||
report.resource_arn = repository.arn
|
||||
report.resource_tags = repository.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Repository {repository.name} is not open"
|
||||
if repository.policy:
|
||||
|
||||
@@ -10,6 +10,7 @@ class ecr_repositories_scan_images_on_push_enabled(Check):
|
||||
report.region = repository.region
|
||||
report.resource_id = repository.name
|
||||
report.resource_arn = repository.arn
|
||||
report.resource_tags = repository.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"ECR repository {repository.name} has scan on push enabled"
|
||||
|
||||
@@ -11,6 +11,7 @@ class ecr_repositories_scan_vulnerabilities_in_latest_image(Check):
|
||||
report.region = repository.region
|
||||
report.resource_id = repository.name
|
||||
report.resource_arn = repository.arn
|
||||
report.resource_tags = repository.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ECR repository {repository.name} has imageTag {image.latest_tag} scanned without findings"
|
||||
if not image.scan_findings_status:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from json import loads
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
|
||||
@@ -19,6 +21,7 @@ class ECR:
|
||||
self.__describe_repository_policies__()
|
||||
self.__get_image_details__()
|
||||
self.__get_repository_lifecycle_policy__()
|
||||
self.__list_tags_for_resource__()
|
||||
|
||||
def __get_session__(self):
|
||||
return self.session
|
||||
@@ -104,7 +107,6 @@ class ECR:
|
||||
for page in describe_images_paginator.paginate(
|
||||
repositoryName=repository.name
|
||||
):
|
||||
|
||||
for image in page["imageDetails"]:
|
||||
severity_counts = None
|
||||
last_scan_status = None
|
||||
@@ -147,61 +149,40 @@ class ECR:
|
||||
f"-- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __list_tags_for_resource__(self):
|
||||
logger.info("ECR - List Tags...")
|
||||
try:
|
||||
for repository in self.repositories:
|
||||
regional_client = self.regional_clients[repository.region]
|
||||
response = regional_client.list_tags_for_resource(
|
||||
resourceArn=repository.arn
|
||||
)["tags"]
|
||||
repository.tags = response
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
@dataclass
|
||||
class FindingSeverityCounts:
|
||||
|
||||
class FindingSeverityCounts(BaseModel):
|
||||
critical: int
|
||||
high: int
|
||||
medium: int
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
critical,
|
||||
high,
|
||||
medium,
|
||||
):
|
||||
self.critical = critical
|
||||
self.high = high
|
||||
self.medium = medium
|
||||
|
||||
|
||||
@dataclass
|
||||
class ImageDetails:
|
||||
class ImageDetails(BaseModel):
|
||||
latest_tag: str
|
||||
latest_digest: str
|
||||
scan_findings_status: str
|
||||
scan_findings_severity_count: FindingSeverityCounts
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
latest_tag,
|
||||
latest_digest,
|
||||
scan_findings_status,
|
||||
scan_findings_severity_count,
|
||||
):
|
||||
self.latest_tag = latest_tag
|
||||
self.latest_digest = latest_digest
|
||||
self.scan_findings_status = scan_findings_status
|
||||
self.scan_findings_severity_count = scan_findings_severity_count
|
||||
scan_findings_status: Optional[str]
|
||||
scan_findings_severity_count: Optional[FindingSeverityCounts]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Repository:
|
||||
class Repository(BaseModel):
|
||||
name: str
|
||||
arn: str
|
||||
region: str
|
||||
scan_on_push: bool
|
||||
policy: dict
|
||||
images_details: list[ImageDetails]
|
||||
lyfecicle_policy: str
|
||||
|
||||
def __init__(
|
||||
self, name, arn, region, scan_on_push, policy, images_details, lyfecicle_policy
|
||||
):
|
||||
self.name = name
|
||||
self.arn = arn
|
||||
self.region = region
|
||||
self.scan_on_push = scan_on_push
|
||||
self.policy = policy
|
||||
self.images_details = images_details
|
||||
self.lyfecicle_policy = lyfecicle_policy
|
||||
policy: Optional[dict]
|
||||
images_details: Optional[list[ImageDetails]]
|
||||
lyfecicle_policy: Optional[str]
|
||||
tags: Optional[list] = []
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import threading
|
||||
from re import sub
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -34,7 +35,6 @@ class ECS:
|
||||
def __list_task_definitions__(self, regional_client):
|
||||
logger.info("ECS - Listing Task Definitions...")
|
||||
try:
|
||||
|
||||
list_ecs_paginator = regional_client.get_paginator("list_task_definitions")
|
||||
for page in list_ecs_paginator.paginate():
|
||||
for task_definition in page["taskDefinitionArns"]:
|
||||
@@ -61,9 +61,15 @@ class ECS:
|
||||
try:
|
||||
for task_definition in self.task_definitions:
|
||||
client = self.regional_clients[task_definition.region]
|
||||
container_definitions = client.describe_task_definition(
|
||||
taskDefinition=task_definition.arn
|
||||
)["taskDefinition"]["containerDefinitions"]
|
||||
response = client.describe_task_definition(
|
||||
taskDefinition=task_definition.arn,
|
||||
include=[
|
||||
"TAGS",
|
||||
],
|
||||
)
|
||||
container_definitions = response["taskDefinition"][
|
||||
"containerDefinitions"
|
||||
]
|
||||
for container in container_definitions:
|
||||
if "environment" in container:
|
||||
for env_var in container["environment"]:
|
||||
@@ -72,6 +78,7 @@ class ECS:
|
||||
name=env_var["name"], value=env_var["value"]
|
||||
)
|
||||
)
|
||||
task_definition.tags = response.get("tags")
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -89,3 +96,4 @@ class TaskDefinition(BaseModel):
|
||||
revision: str
|
||||
region: str
|
||||
environment_variables: list[ContainerEnvVariable]
|
||||
tags: Optional[list] = []
|
||||
|
||||
@@ -17,6 +17,7 @@ class ecs_task_definitions_no_environment_secrets(Check):
|
||||
report.region = task_definition.region
|
||||
report.resource_id = f"{task_definition.name}:{task_definition.revision}"
|
||||
report.resource_arn = task_definition.arn
|
||||
report.resource_tags = task_definition.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"No secrets found in variables of ECS task definition {task_definition.name} with revision {task_definition.revision}"
|
||||
if task_definition.environment_variables:
|
||||
|
||||
@@ -9,6 +9,7 @@ class efs_encryption_at_rest_enabled(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = fs.region
|
||||
report.resource_id = fs.id
|
||||
report.resource_tags = fs.tags
|
||||
report.resource_arn = ""
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
|
||||
@@ -10,6 +10,7 @@ class efs_have_backup_enabled(Check):
|
||||
report.region = fs.region
|
||||
report.resource_id = fs.id
|
||||
report.resource_arn = ""
|
||||
report.resource_tags = fs.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"EFS {fs.id} has backup enabled"
|
||||
if fs.backup_policy == "DISABLED" or fs.backup_policy == "DISABLING":
|
||||
|
||||
@@ -10,6 +10,7 @@ class efs_not_publicly_accessible(Check):
|
||||
report.region = fs.region
|
||||
report.resource_id = fs.id
|
||||
report.resource_arn = ""
|
||||
report.resource_tags = fs.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"EFS {fs.id} has policy which does not allow access to everyone"
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import json
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from botocore.client import ClientError
|
||||
from pydantic import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
|
||||
@@ -50,6 +51,7 @@ class EFS:
|
||||
policy=None,
|
||||
backup_policy=None,
|
||||
encrypted=efs["Encrypted"],
|
||||
tags=efs.get("Tags"),
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -85,24 +87,10 @@ class EFS:
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class FileSystem:
|
||||
class FileSystem(BaseModel):
|
||||
id: str
|
||||
region: str
|
||||
policy: dict
|
||||
backup_policy: str
|
||||
policy: Optional[dict]
|
||||
backup_policy: Optional[str]
|
||||
encrypted: bool
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id,
|
||||
region,
|
||||
policy,
|
||||
backup_policy,
|
||||
encrypted,
|
||||
):
|
||||
self.id = id
|
||||
self.region = region
|
||||
self.policy = policy
|
||||
self.backup_policy = backup_policy
|
||||
self.encrypted = encrypted
|
||||
tags: Optional[list] = []
|
||||
|
||||
@@ -10,6 +10,7 @@ class eks_cluster_kms_cmk_encryption_in_secrets_enabled(Check):
|
||||
report.region = cluster.region
|
||||
report.resource_id = cluster.name
|
||||
report.resource_arn = cluster.arn
|
||||
report.resource_tags = cluster.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"EKS cluster {cluster.name} has not encryption for Kubernetes secrets."
|
||||
|
||||
@@ -10,6 +10,7 @@ class eks_control_plane_endpoint_access_restricted(Check):
|
||||
report.region = cluster.region
|
||||
report.resource_id = cluster.name
|
||||
report.resource_arn = cluster.arn
|
||||
report.resource_tags = cluster.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Cluster endpoint access is private for EKS cluster {cluster.name}"
|
||||
|
||||
@@ -10,6 +10,7 @@ class eks_control_plane_logging_all_types_enabled(Check):
|
||||
report.region = cluster.region
|
||||
report.resource_id = cluster.name
|
||||
report.resource_arn = cluster.arn
|
||||
report.resource_tags = cluster.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Control plane logging is not enabled for EKS cluster {cluster.name}"
|
||||
|
||||
@@ -10,6 +10,7 @@ class eks_endpoints_not_publicly_accessible(Check):
|
||||
report.region = cluster.region
|
||||
report.resource_id = cluster.name
|
||||
report.resource_arn = cluster.arn
|
||||
report.resource_tags = cluster.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Cluster endpoint access is private for EKS cluster {cluster.name}"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import threading
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -90,6 +91,7 @@ class EKS:
|
||||
]["publicAccessCidrs"]
|
||||
if "encryptionConfig" in describe_cluster["cluster"]:
|
||||
cluster.encryptionConfig = True
|
||||
cluster.tags = [describe_cluster["cluster"].get("tags")]
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
@@ -111,3 +113,4 @@ class EKSCluster(BaseModel):
|
||||
endpoint_private_access: bool = None
|
||||
public_access_cidrs: list[str] = None
|
||||
encryptionConfig: bool = None
|
||||
tags: Optional[list] = []
|
||||
|
||||
@@ -12,6 +12,7 @@ class elb_insecure_ssl_ciphers(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"ELB {lb.name} has not insecure SSL protocols or ciphers."
|
||||
|
||||
@@ -9,6 +9,7 @@ class elb_internet_facing(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ELB {lb.name} is not internet facing."
|
||||
if lb.scheme == "internet-facing":
|
||||
|
||||
@@ -9,6 +9,7 @@ class elb_logging_enabled(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"ELB {lb.name} has not configured access logs."
|
||||
if lb.access_logs:
|
||||
|
||||
@@ -20,6 +20,7 @@ class ELB:
|
||||
self.loadbalancers = []
|
||||
self.__threading_call__(self.__describe_load_balancers__)
|
||||
self.__threading_call__(self.__describe_load_balancer_attributes__)
|
||||
self.__describe_tags__()
|
||||
|
||||
def __get_session__(self):
|
||||
return self.session
|
||||
@@ -85,6 +86,20 @@ class ELB:
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __describe_tags__(self):
|
||||
logger.info("ELB - List Tags...")
|
||||
try:
|
||||
for lb in self.loadbalancers:
|
||||
regional_client = self.regional_clients[lb.region]
|
||||
response = regional_client.describe_tags(LoadBalancerNames=[lb.name])[
|
||||
"TagDescriptions"
|
||||
][0]
|
||||
lb.tags = response.get("Tags")
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
|
||||
class Listener(BaseModel):
|
||||
protocol: str
|
||||
@@ -99,3 +114,4 @@ class LoadBalancer(BaseModel):
|
||||
scheme: str
|
||||
access_logs: Optional[bool]
|
||||
listeners: list[Listener]
|
||||
tags: Optional[list] = []
|
||||
|
||||
@@ -10,6 +10,7 @@ class elb_ssl_listeners(Check):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ELB {lb.name} has HTTPS listeners only."
|
||||
for listener in lb.listeners:
|
||||
|
||||
@@ -10,6 +10,7 @@ class elbv2_deletion_protection(Check):
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_arn = lb.arn
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"ELBv2 {lb.name} has not deletion protection."
|
||||
if lb.deletion_protection == "true":
|
||||
|
||||
@@ -11,6 +11,7 @@ class elbv2_desync_mitigation_mode(Check):
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_arn = lb.arn
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ELBv2 ALB {lb.name} is configured with correct desync mitigation mode."
|
||||
if lb.desync_mitigation_mode == "monitor":
|
||||
|
||||
@@ -22,6 +22,7 @@ class elbv2_insecure_ssl_ciphers(Check):
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_arn = lb.arn
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"ELBv2 {lb.name} has not insecure SSL protocols or ciphers."
|
||||
|
||||
@@ -10,6 +10,7 @@ class elbv2_internet_facing(Check):
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_arn = lb.arn
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ELBv2 ALB {lb.name} is not internet facing."
|
||||
if lb.scheme == "internet-facing":
|
||||
|
||||
@@ -10,6 +10,7 @@ class elbv2_listeners_underneath(Check):
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_arn = lb.arn
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ELBv2 {lb.name} has listeners underneath."
|
||||
if len(lb.listeners) == 0:
|
||||
|
||||
@@ -10,6 +10,7 @@ class elbv2_logging_enabled(Check):
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_arn = lb.arn
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"ELBv2 ALB {lb.name} has not configured access logs."
|
||||
|
||||
@@ -11,6 +11,7 @@ class elbv2_request_smugling(Check):
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_arn = lb.arn
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"ELBv2 ALB {lb.name} is not dropping invalid header fields."
|
||||
|
||||
@@ -22,6 +22,7 @@ class ELBv2:
|
||||
self.__threading_call__(self.__describe_listeners__)
|
||||
self.__threading_call__(self.__describe_load_balancer_attributes__)
|
||||
self.__threading_call__(self.__describe_rules__)
|
||||
self.__describe_tags__()
|
||||
|
||||
def __get_session__(self):
|
||||
return self.session
|
||||
@@ -148,6 +149,20 @@ class ELBv2:
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __describe_tags__(self):
|
||||
logger.info("ELBv2 - List Tags...")
|
||||
try:
|
||||
for lb in self.loadbalancersv2:
|
||||
regional_client = self.regional_clients[lb.region]
|
||||
response = regional_client.describe_tags(ResourceArns=[lb.arn])[
|
||||
"TagDescriptions"
|
||||
][0]
|
||||
lb.tags = response.get("Tags")
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
|
||||
class ListenerRule(BaseModel):
|
||||
arn: str
|
||||
@@ -176,3 +191,4 @@ class LoadBalancerv2(BaseModel):
|
||||
drop_invalid_header_fields: Optional[str]
|
||||
listeners: list[Listenerv2]
|
||||
scheme: Optional[str]
|
||||
tags: Optional[list] = []
|
||||
|
||||
@@ -11,6 +11,7 @@ class elbv2_ssl_listeners(Check):
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_arn = lb.arn
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"ELBv2 ALB {lb.name} has HTTPS listeners only."
|
||||
|
||||
@@ -13,6 +13,7 @@ class elbv2_waf_acl_attached(Check):
|
||||
report.region = lb.region
|
||||
report.resource_id = lb.name
|
||||
report.resource_arn = lb.arn
|
||||
report.resource_tags = lb.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"ELBv2 ALB {lb.name} is not protected by WAF Web ACL."
|
||||
|
||||
@@ -15,7 +15,7 @@ class emr_cluster_master_nodes_no_public_ip(Check):
|
||||
report.region = cluster.region
|
||||
report.resource_id = cluster.id
|
||||
report.resource_arn = cluster.arn
|
||||
|
||||
report.resource_tags = cluster.tags
|
||||
if cluster.public:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"EMR Cluster {cluster.id} has a Public IP"
|
||||
|
||||
@@ -19,7 +19,7 @@ class emr_cluster_publicly_accesible(Check):
|
||||
report.region = cluster.region
|
||||
report.resource_id = cluster.id
|
||||
report.resource_arn = cluster.arn
|
||||
|
||||
report.resource_tags = cluster.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"EMR Cluster {cluster.id} is not publicly accessible"
|
||||
@@ -28,7 +28,6 @@ class emr_cluster_publicly_accesible(Check):
|
||||
# their Security Groups for the Master,
|
||||
# the Slaves and the additional ones
|
||||
if cluster.public:
|
||||
|
||||
# Check Public Master Security Groups
|
||||
master_node_sg_groups = deepcopy(
|
||||
cluster.master.additional_security_groups_id
|
||||
|
||||
@@ -125,6 +125,7 @@ class EMR:
|
||||
and ".amazonaws.com" in master_public_dns_name
|
||||
):
|
||||
self.clusters[cluster.id].public = True
|
||||
cluster.tags = cluster_info["Cluster"].get("Tags")
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
@@ -185,3 +186,4 @@ class Cluster(BaseModel):
|
||||
slave: Node = Node()
|
||||
master_public_dns_name: str = ""
|
||||
public: bool = False
|
||||
tags: Optional[list] = []
|
||||
|
||||
@@ -127,6 +127,14 @@ class Test_EC2_Service:
|
||||
sg_id = ec2_client.create_security_group(
|
||||
Description="test-description",
|
||||
GroupName="test-security-group",
|
||||
TagSpecifications=[
|
||||
{
|
||||
"ResourceType": "security-group",
|
||||
"Tags": [
|
||||
{"Key": "test", "Value": "test"},
|
||||
],
|
||||
},
|
||||
],
|
||||
)["GroupId"]
|
||||
# EC2 client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
@@ -153,6 +161,9 @@ class Test_EC2_Service:
|
||||
"UserIdGroupPairs": [],
|
||||
}
|
||||
]
|
||||
assert security_group.tags == [
|
||||
{"Key": "test", "Value": "test"},
|
||||
]
|
||||
|
||||
# Test EC2 Describe Nacls
|
||||
@mock_ec2
|
||||
@@ -164,6 +175,14 @@ class Test_EC2_Service:
|
||||
vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
|
||||
nacl_id = ec2_resource.create_network_acl(
|
||||
VpcId=vpc_id,
|
||||
TagSpecifications=[
|
||||
{
|
||||
"ResourceType": "network-acl",
|
||||
"Tags": [
|
||||
{"Key": "test", "Value": "test"},
|
||||
],
|
||||
},
|
||||
],
|
||||
).id
|
||||
# EC2 client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
@@ -178,6 +197,9 @@ class Test_EC2_Service:
|
||||
== f"arn:{audit_info.audited_partition}:ec2:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:network-acl/{acl.id}"
|
||||
)
|
||||
assert acl.entries == []
|
||||
assert acl.tags == [
|
||||
{"Key": "test", "Value": "test"},
|
||||
]
|
||||
|
||||
# Test EC2 Describe Snapshots
|
||||
@mock_ec2
|
||||
@@ -193,6 +215,14 @@ class Test_EC2_Service:
|
||||
).id
|
||||
snapshot_id = ec2_client.create_snapshot(
|
||||
VolumeId=volume_id,
|
||||
TagSpecifications=[
|
||||
{
|
||||
"ResourceType": "snapshot",
|
||||
"Tags": [
|
||||
{"Key": "test", "Value": "test"},
|
||||
],
|
||||
},
|
||||
],
|
||||
)["SnapshotId"]
|
||||
# EC2 client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
@@ -207,6 +237,9 @@ class Test_EC2_Service:
|
||||
== f"arn:{audit_info.audited_partition}:ec2:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:snapshot/{snapshot.id}"
|
||||
)
|
||||
assert snapshot.region == AWS_REGION
|
||||
assert snapshot.tags == [
|
||||
{"Key": "test", "Value": "test"},
|
||||
]
|
||||
assert not snapshot.encrypted
|
||||
assert not snapshot.public
|
||||
|
||||
@@ -286,7 +319,16 @@ class Test_EC2_Service:
|
||||
# Generate EC2 Client
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
allocation_id = ec2_client.allocate_address(
|
||||
Domain="vpc", Address="127.38.43.222"
|
||||
Domain="vpc",
|
||||
Address="127.38.43.222",
|
||||
TagSpecifications=[
|
||||
{
|
||||
"ResourceType": "elastic-ip",
|
||||
"Tags": [
|
||||
{"Key": "test", "Value": "test"},
|
||||
],
|
||||
},
|
||||
],
|
||||
)["AllocationId"]
|
||||
# EC2 client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
@@ -296,6 +338,9 @@ class Test_EC2_Service:
|
||||
ec2.elastic_ips[0].arn
|
||||
== f"arn:aws:ec2:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:eip-allocation/{allocation_id}"
|
||||
)
|
||||
assert ec2.elastic_ips[0].tags == [
|
||||
{"Key": "test", "Value": "test"},
|
||||
]
|
||||
|
||||
# Test EC2 Describe Network Interfaces
|
||||
@mock_ec2
|
||||
@@ -310,7 +355,6 @@ class Test_EC2_Service:
|
||||
GroupName="test-securitygroup", Description="n/a"
|
||||
)
|
||||
eni_id = subnet.create_network_interface(Groups=[sg.id]).id
|
||||
print(eni_id)
|
||||
ec2_client.modify_network_interface_attribute(
|
||||
NetworkInterfaceId=eni_id, Groups=[sg.id]
|
||||
)
|
||||
@@ -383,6 +427,13 @@ class Test_EC2_Service:
|
||||
)
|
||||
assert not ec2.images[0].public
|
||||
assert ec2.images[0].region == AWS_REGION
|
||||
assert ec2.images[0].tags == [
|
||||
{
|
||||
"Key": "Base_AMI_Name",
|
||||
"Value": "Deep Learning Base AMI (Amazon Linux 2) Version 31.0",
|
||||
},
|
||||
{"Key": "OS_Version", "Value": "AWS Linux 2"},
|
||||
]
|
||||
|
||||
# Test EC2 Describe Volumes
|
||||
@mock_ec2
|
||||
@@ -394,7 +445,14 @@ class Test_EC2_Service:
|
||||
AvailabilityZone=AWS_REGION,
|
||||
Encrypted=False,
|
||||
Size=40,
|
||||
TagSpecifications=[],
|
||||
TagSpecifications=[
|
||||
{
|
||||
"ResourceType": "volume",
|
||||
"Tags": [
|
||||
{"Key": "test", "Value": "test"},
|
||||
],
|
||||
},
|
||||
],
|
||||
)["VolumeId"]
|
||||
|
||||
# EC2 client for this test class
|
||||
@@ -410,3 +468,6 @@ class Test_EC2_Service:
|
||||
)
|
||||
assert ec2.volumes[0].region == AWS_REGION
|
||||
assert not ec2.volumes[0].encrypted
|
||||
assert ec2.volumes[0].tags == [
|
||||
{"Key": "test", "Value": "test"},
|
||||
]
|
||||
|
||||
@@ -118,6 +118,9 @@ class Test_ECR_Service:
|
||||
ecr_client.create_repository(
|
||||
repositoryName=repo_name,
|
||||
imageScanningConfiguration={"scanOnPush": True},
|
||||
tags=[
|
||||
{"Key": "test", "Value": "test"},
|
||||
],
|
||||
)
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
ecr = ECR(audit_info)
|
||||
@@ -125,6 +128,9 @@ class Test_ECR_Service:
|
||||
assert ecr.repositories[0].name == repo_name
|
||||
assert ecr.repositories[0].arn == repo_arn
|
||||
assert ecr.repositories[0].scan_on_push
|
||||
assert ecr.repositories[0].tags == [
|
||||
{"Key": "test", "Value": "test"},
|
||||
]
|
||||
|
||||
# Test describe ECR repository policies
|
||||
@mock_ecr
|
||||
|
||||
@@ -111,6 +111,9 @@ class Test_ECS_Service:
|
||||
],
|
||||
}
|
||||
],
|
||||
tags=[
|
||||
{"key": "test", "value": "test"},
|
||||
],
|
||||
)
|
||||
|
||||
task_definition = ecs_client.register_task_definition(**definition)
|
||||
@@ -121,6 +124,9 @@ class Test_ECS_Service:
|
||||
assert (
|
||||
ecs.task_definitions[0].name == task_definition["taskDefinition"]["family"]
|
||||
)
|
||||
assert ecs.task_definitions[0].tags == [
|
||||
{"key": "test", "value": "test"},
|
||||
]
|
||||
assert (
|
||||
ecs.task_definitions[0].arn
|
||||
== task_definition["taskDefinition"]["taskDefinitionArn"]
|
||||
|
||||
@@ -91,12 +91,19 @@ class Test_EFS:
|
||||
def test__describe_file_systems__(self):
|
||||
efs_client = client("efs", AWS_REGION)
|
||||
efs = efs_client.create_file_system(
|
||||
CreationToken=creation_token, Encrypted=True
|
||||
CreationToken=creation_token,
|
||||
Encrypted=True,
|
||||
Tags=[
|
||||
{"Key": "test", "Value": "test"},
|
||||
],
|
||||
)
|
||||
filesystem = EFS(self.set_mocked_audit_info())
|
||||
assert len(filesystem.filesystems) == 1
|
||||
assert filesystem.filesystems[0].id == efs["FileSystemId"]
|
||||
assert filesystem.filesystems[0].encrypted == efs["Encrypted"]
|
||||
assert filesystem.filesystems[0].tags == [
|
||||
{"Key": "test", "Value": "test"},
|
||||
]
|
||||
|
||||
@mock_efs
|
||||
# Test EFS describe file systems
|
||||
|
||||
@@ -92,12 +92,14 @@ class Test_EKS_Service:
|
||||
],
|
||||
},
|
||||
roleArn=f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/eks-service-role-AWSServiceRoleForAmazonEKS-J7ONKE3BQ4PI",
|
||||
tags={"test": "test"},
|
||||
)
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
eks = EKS(audit_info)
|
||||
assert len(eks.clusters) == 1
|
||||
assert eks.clusters[0].name == cluster_name
|
||||
assert eks.clusters[0].region == AWS_REGION
|
||||
assert eks.clusters[0].tags == [{"test": "test"}]
|
||||
|
||||
# Test EKS describe clusters
|
||||
@mock_ec2
|
||||
|
||||
@@ -108,6 +108,9 @@ class Test_EMR_Service:
|
||||
Name=cluster_name,
|
||||
ServiceRole="EMR_DefaultRole",
|
||||
VisibleToAllUsers=True,
|
||||
Tags=[
|
||||
{"Key": "test", "Value": "test"},
|
||||
],
|
||||
)
|
||||
cluster_id = emr_client.run_job_flow(**run_job_flow_args)["JobFlowId"]
|
||||
# EMR Class
|
||||
@@ -127,6 +130,9 @@ class Test_EMR_Service:
|
||||
== "ec2-184-0-0-1.us-west-1.compute.amazonaws.com"
|
||||
)
|
||||
assert emr.clusters[cluster_id].public
|
||||
assert emr.clusters[cluster_id].tags == [
|
||||
{"Key": "test", "Value": "test"},
|
||||
]
|
||||
|
||||
@mock_emr
|
||||
def test__get_block_public_access_configuration__(self):
|
||||
|
||||
Reference in New Issue
Block a user