feat(regions): add regions to resources (#1285)

This commit is contained in:
Sergio Garcia
2022-08-04 07:35:13 -04:00
committed by GitHub
parent 6e58991986
commit a796545da5
26 changed files with 568 additions and 740 deletions

View File

@@ -55,7 +55,7 @@ repos:
language: system language: system
- id: safety - id: safety
name: bandit name: safety
description: 'Safety is a tool that checks your installed dependencies for known security vulnerabilities' description: 'Safety is a tool that checks your installed dependencies for known security vulnerabilities'
entry: bash -c 'safety check' entry: bash -c 'safety check'
language: system language: system

View File

@@ -42,53 +42,59 @@ def report(check_findings, output_options, audit_info):
csv_fields, csv_fields,
) )
for finding in check_findings: if check_findings:
# Print findings by stdout for finding in check_findings:
color = set_report_color(finding.status) # Print findings by stdout
if output_options.is_quiet and "FAIL" in finding.status: color = set_report_color(finding.status)
print( if output_options.is_quiet and "FAIL" in finding.status:
f"\t{color}{finding.status}{Style.RESET_ALL} {finding.region}: {finding.status_extended}" print(
) f"\t{color}{finding.status}{Style.RESET_ALL} {finding.region}: {finding.status_extended}"
elif not output_options.is_quiet:
print(
f"\t{color}{finding.status}{Style.RESET_ALL} {finding.region}: {finding.status_extended}"
)
if file_descriptors:
# sending the finding to input options
if "csv" in file_descriptors:
finding_output = Check_Output_CSV(
audit_info.audited_account,
audit_info.profile,
finding,
audit_info.organizations_metadata,
) )
csv_writer = DictWriter( elif not output_options.is_quiet:
file_descriptors["csv"], fieldnames=csv_fields, delimiter=";" print(
f"\t{color}{finding.status}{Style.RESET_ALL} {finding.region}: {finding.status_extended}"
) )
csv_writer.writerow(finding_output.__dict__)
if file_descriptors:
if "json" in file_descriptors: # sending the finding to input options
finding_output = Check_Output_JSON(**finding.check_metadata.dict()) if "csv" in file_descriptors:
fill_json(finding_output, audit_info, finding) finding_output = Check_Output_CSV(
audit_info.audited_account,
audit_info.profile,
finding,
audit_info.organizations_metadata,
)
csv_writer = DictWriter(
file_descriptors["csv"], fieldnames=csv_fields, delimiter=";"
)
csv_writer.writerow(finding_output.__dict__)
json.dump(finding_output.dict(), file_descriptors["json"], indent=4) if "json" in file_descriptors:
file_descriptors["json"].write(",") finding_output = Check_Output_JSON(**finding.check_metadata.dict())
fill_json(finding_output, audit_info, finding)
if "json-asff" in file_descriptors: json.dump(finding_output.dict(), file_descriptors["json"], indent=4)
finding_output = Check_Output_JSON_ASFF() file_descriptors["json"].write(",")
fill_json_asff(finding_output, audit_info, finding)
json.dump( if "json-asff" in file_descriptors:
finding_output.dict(), file_descriptors["json-asff"], indent=4 finding_output = Check_Output_JSON_ASFF()
) fill_json_asff(finding_output, audit_info, finding)
file_descriptors["json-asff"].write(",")
# Check if it is needed to send findings to security hub json.dump(
if output_options.security_hub_enabled: finding_output.dict(), file_descriptors["json-asff"], indent=4
send_to_security_hub( )
finding.region, finding_output, audit_info.audit_session file_descriptors["json-asff"].write(",")
)
# Check if it is needed to send findings to security hub
if output_options.security_hub_enabled:
send_to_security_hub(
finding.region, finding_output, audit_info.audit_session
)
else: # No service resources in the whole account
color = set_report_color("PASS")
if not output_options.is_quiet:
print(f"{color}PASS{Style.RESET_ALL} There are no resources.")
if file_descriptors: if file_descriptors:
# Close all file descriptors # Close all file descriptors

View File

@@ -309,7 +309,7 @@ def get_organizations_metadata(
def generate_regional_clients(service, audit_info): def generate_regional_clients(service, audit_info):
regional_clients = [] regional_clients = {}
# Get json locally # Get json locally
f = open_file(aws_services_json_file) f = open_file(aws_services_json_file)
data = parse_json_file(f) data = parse_json_file(f)
@@ -323,8 +323,8 @@ def generate_regional_clients(service, audit_info):
for region in regions: for region in regions:
regional_client = audit_info.audit_session.client(service, region_name=region) regional_client = audit_info.audit_session.client(service, region_name=region)
regional_client.region = region regional_client.region = region
regional_clients.append(regional_client) regional_clients[region] = regional_client
# regional_clients.append(regional_client)
return regional_clients return regional_clients

View File

@@ -5,31 +5,19 @@ from providers.aws.services.ec2.ec2_service import ec2_client
class ec2_ebs_public_snapshot(Check): class ec2_ebs_public_snapshot(Check):
def execute(self): def execute(self):
findings = [] findings = []
for regional_client in ec2_client.regional_clients: for snapshot in ec2_client.snapshots:
region = regional_client.region report = Check_Report(self.metadata)
if regional_client.snapshots: report.region = snapshot.region
for snapshot in regional_client.snapshots: if not snapshot.public:
report = Check_Report(self.metadata)
report.region = region
if not snapshot.public:
report.status = "PASS"
report.status_extended = (
f"EBS Snapshot {snapshot.id} is not Public"
)
report.resource_id = snapshot.id
else:
report.status = "FAIL"
report.status_extended = (
f"EBS Snapshot {snapshot.id} is currently Public"
)
report.resource_id = snapshot.id
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS" report.status = "PASS"
report.status_extended = "There are no EC2 EBS snapshots" report.status_extended = f"EBS Snapshot {snapshot.id} is not Public"
report.region = region report.resource_id = snapshot.id
else:
findings.append(report) report.status = "FAIL"
report.status_extended = (
f"EBS Snapshot {snapshot.id} is currently Public"
)
report.resource_id = snapshot.id
findings.append(report)
return findings return findings

View File

@@ -5,31 +5,17 @@ from providers.aws.services.ec2.ec2_service import ec2_client
class ec2_ebs_snapshots_encrypted(Check): class ec2_ebs_snapshots_encrypted(Check):
def execute(self): def execute(self):
findings = [] findings = []
for regional_client in ec2_client.regional_clients: for snapshot in ec2_client.snapshots:
region = regional_client.region report = Check_Report(self.metadata)
if regional_client.snapshots: report.region = snapshot.region
for snapshot in regional_client.snapshots: if snapshot.encrypted:
report = Check_Report(self.metadata)
report.region = region
if snapshot.encrypted:
report.status = "PASS"
report.status_extended = (
f"EBS Snapshot {snapshot.id} is encrypted"
)
report.resource_id = snapshot.id
else:
report.status = "FAIL"
report.status_extended = (
f"EBS Snapshot {snapshot.id} is unencrypted"
)
report.resource_id = snapshot.id
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS" report.status = "PASS"
report.status_extended = "There are no EC2 EBS snapshots" report.status_extended = f"EBS Snapshot {snapshot.id} is encrypted"
report.region = region report.resource_id = snapshot.id
else:
findings.append(report) report.status = "FAIL"
report.status_extended = f"EBS Snapshot {snapshot.id} is unencrypted"
report.resource_id = snapshot.id
findings.append(report)
return findings return findings

View File

@@ -5,29 +5,19 @@ from providers.aws.services.ec2.ec2_service import ec2_client
class ec2_instance_public_ip(Check): class ec2_instance_public_ip(Check):
def execute(self): def execute(self):
findings = [] findings = []
for regional_client in ec2_client.regional_clients: for instance in ec2_client.instances:
region = regional_client.region report = Check_Report(self.metadata)
if regional_client.instances: report.region = instance.region
for instance in regional_client.instances: if instance.public_ip:
report = Check_Report(self.metadata) report.status = "FAIL"
report.region = region report.status_extended = f"EC2 instance {instance.id} has a Public IP: {instance.public_ip} ({instance.public_dns})."
if instance.public_ip: report.resource_id = instance.id
report.status = "FAIL"
report.status_extended = f"EC2 instance {instance.id} has a Public IP: {instance.public_ip} ({instance.public_dns})."
report.resource_id = instance.id
else:
report.status = "PASS"
report.status_extended = (
f"EC2 instance {instance.id} has not a Public IP."
)
report.resource_id = instance.id
findings.append(report)
else: else:
report = Check_Report(self.metadata)
report.status = "PASS" report.status = "PASS"
report.status_extended = "There are no EC2 instances." report.status_extended = (
report.region = region f"EC2 instance {instance.id} has not a Public IP."
)
findings.append(report) report.resource_id = instance.id
findings.append(report)
return findings return findings

View File

@@ -6,40 +6,32 @@ class ec2_networkacl_allow_ingress_tcp_port_22(Check):
def execute(self): def execute(self):
findings = [] findings = []
check_port = 22 check_port = 22
for regional_client in ec2_client.regional_clients: for network_acl in ec2_client.network_acls:
region = regional_client.region public = False
if regional_client.network_acls: report = Check_Report(self.metadata)
for network_acl in regional_client.network_acls: report.region = network_acl.region
public = False for entry in network_acl.entries:
report = Check_Report(self.metadata) if (
report.region = region entry["CidrBlock"] == "0.0.0.0/0"
for entry in network_acl.entries: and entry["RuleAction"] == "allow"
if ( and not entry["Egress"]
entry["CidrBlock"] == "0.0.0.0/0" ):
and entry["RuleAction"] == "allow" if entry["Protocol"] == "-1":
and not entry["Egress"] public = True
and "PortRange" in entry elif (
and entry["Protocol"] == "6" # 6 relates to tcp protocol entry["PortRange"]["From"] == check_port
): and entry["PortRange"]["To"] == check_port
if ( and entry["Protocol"] == "6"
entry["PortRange"]["From"] == check_port ):
and entry["PortRange"]["To"] == check_port public = True
): if not public:
public = True
report.status = "FAIL"
report.status_extended = f"Network ACL {network_acl.id} has SSH port 22 open to the Internet."
report.resource_id = network_acl.id
if not public:
report.status = "PASS"
report.status_extended = f"Network ACL {network_acl.id} has not SSH port 22 open to the Internet."
report.resource_id = network_acl.id
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS" report.status = "PASS"
report.status_extended = "There are no EC2 network acls." report.status_extended = f"Network ACL {network_acl.id} has not SSH port 22 open to the Internet."
report.region = region report.resource_id = network_acl.id
else:
findings.append(report) report.status = "FAIL"
report.status_extended = f"Network ACL {network_acl.id} has SSH port 22 open to the Internet."
report.resource_id = network_acl.id
findings.append(report)
return findings return findings

View File

@@ -6,40 +6,32 @@ class ec2_networkacl_allow_ingress_tcp_port_3389(Check):
def execute(self): def execute(self):
findings = [] findings = []
check_port = 3389 check_port = 3389
for regional_client in ec2_client.regional_clients: for network_acl in ec2_client.network_acls:
region = regional_client.region public = False
if regional_client.network_acls: report = Check_Report(self.metadata)
for network_acl in regional_client.network_acls: report.region = network_acl.region
public = False for entry in network_acl.entries:
report = Check_Report(self.metadata) if (
report.region = region entry["CidrBlock"] == "0.0.0.0/0"
for entry in network_acl.entries: and entry["RuleAction"] == "allow"
if ( and not entry["Egress"]
entry["CidrBlock"] == "0.0.0.0/0" ):
and entry["RuleAction"] == "allow" if entry["Protocol"] == "-1":
and not entry["Egress"] public = True
and "PortRange" in entry elif (
and entry["Protocol"] == "6" # 6 relates to tcp protocol entry["PortRange"]["From"] == check_port
): and entry["PortRange"]["To"] == check_port
if ( and entry["Protocol"] == "6"
entry["PortRange"]["From"] == check_port ):
and entry["PortRange"]["To"] == check_port public = True
): if not public:
public = True
report.status = "FAIL"
report.status_extended = f"Network ACL {network_acl.id} has Microsoft RDP port 3389 open to the Internet."
report.resource_id = network_acl.id
if not public:
report.status = "PASS"
report.status_extended = f"Network ACL {network_acl.id} has not Microsoft RDP port 3389 open to the Internet."
report.resource_id = network_acl.id
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS" report.status = "PASS"
report.status_extended = "There are no EC2 network acls." report.status_extended = f"Network ACL {network_acl.id} has not Microsoft RDP port 3389 open to the Internet."
report.region = region report.resource_id = network_acl.id
else:
findings.append(report) report.status = "FAIL"
report.status_extended = f"Network ACL {network_acl.id} has Microsoft RDP port 3389 open to the Internet."
report.resource_id = network_acl.id
findings.append(report)
return findings return findings

View File

@@ -5,32 +5,22 @@ from providers.aws.services.ec2.ec2_service import check_security_group, ec2_cli
class ec2_securitygroup_allow_ingress_from_internet_to_any_port(Check): class ec2_securitygroup_allow_ingress_from_internet_to_any_port(Check):
def execute(self): def execute(self):
findings = [] findings = []
for regional_client in ec2_client.regional_clients: for security_group in ec2_client.security_groups:
region = regional_client.region public = False
if regional_client.security_groups: report = Check_Report(self.metadata)
for security_group in regional_client.security_groups: report.region = security_group.region
public = False # Loop through every security group's ingress rule and check it
report = Check_Report(self.metadata) for ingress_rule in security_group.ingress_rules:
report.region = region public = check_security_group(ingress_rule, "-1")
# Loop through every security group's ingress rule and check it # Check
for ingress_rule in security_group.ingress_rules: if public:
public = check_security_group(ingress_rule, "-1") report.status = "FAIL"
# Check report.status_extended = f"Security group {security_group.name} ({security_group.id}) has all ports open to the Internet."
if public: report.resource_id = security_group.id
report.status = "FAIL" else:
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has all ports open to the Internet." report.status = "PASS"
report.resource_id = security_group.id report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not all ports open to the Internet."
else: report.resource_id = security_group.id
report.status = "PASS"
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
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS"
report.status_extended = "There are no EC2 security groups."
report.region = region
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -6,32 +6,22 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22(Check):
def execute(self): def execute(self):
findings = [] findings = []
check_ports = [22] check_ports = [22]
for regional_client in ec2_client.regional_clients: for security_group in ec2_client.security_groups:
region = regional_client.region public = False
if regional_client.security_groups: report = Check_Report(self.metadata)
for security_group in regional_client.security_groups: report.region = security_group.region
public = False # Loop through every security group's ingress rule and check it
report = Check_Report(self.metadata) for ingress_rule in security_group.ingress_rules:
report.region = region public = check_security_group(ingress_rule, "tcp", check_ports)
# Loop through every security group's ingress rule and check it # Check
for ingress_rule in security_group.ingress_rules: if public:
public = check_security_group(ingress_rule, "tcp", check_ports) report.status = "FAIL"
# Check report.status_extended = f"Security group {security_group.name} ({security_group.id}) has the SSH port 22 open to the Internet."
if public: report.resource_id = security_group.id
report.status = "FAIL" else:
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has the SSH port 22 open to the Internet." report.status = "PASS"
report.resource_id = security_group.id report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not SSH port 22 open to the Internet."
else: report.resource_id = security_group.id
report.status = "PASS"
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
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS"
report.status_extended = "There are no EC2 security groups."
report.region = region
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -1,37 +1,26 @@
from lib.check.models import Check, Check_Report from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import check_security_group, ec2_client from providers.aws.services.ec2.ec2_service import check_security_group, ec2_client
class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389(Check): class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389(Check):
def execute(self): def execute(self):
findings = [] findings = []
check_ports = [3389] check_ports = [3389]
for regional_client in ec2_client.regional_clients: for security_group in ec2_client.security_groups:
region = regional_client.region public = False
if regional_client.security_groups: report = Check_Report(self.metadata)
for security_group in regional_client.security_groups: report.region = security_group.region
public = False # Loop through every security group's ingress rule and check it
report = Check_Report(self.metadata) for ingress_rule in security_group.ingress_rules:
report.region = region public = check_security_group(ingress_rule, "tcp", check_ports)
# Loop through every security group's ingress rule and check it # Check
for ingress_rule in security_group.ingress_rules: if public:
public = check_security_group(ingress_rule, "tcp", check_ports) report.status = "FAIL"
# Check report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Microsoft RDP port 3389 open to the Internet."
if public: report.resource_id = security_group.id
report.status = "FAIL" else:
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Microsoft RDP port 3389 open to the Internet." report.status = "PASS"
report.resource_id = security_group.id report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Microsoft RDP port 3389 open to the Internet."
else: report.resource_id = security_group.id
report.status = "PASS"
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
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS"
report.status_extended = "There are no EC2 security groups."
report.region = region
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -1,37 +1,26 @@
from lib.check.models import Check, Check_Report from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import check_security_group, ec2_client from providers.aws.services.ec2.ec2_service import check_security_group, ec2_client
class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306(Check): class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306(Check):
def execute(self): def execute(self):
findings = [] findings = []
check_ports = [3306] check_ports = [3306]
for regional_client in ec2_client.regional_clients: for security_group in ec2_client.security_groups:
region = regional_client.region public = False
if regional_client.security_groups: report = Check_Report(self.metadata)
for security_group in regional_client.security_groups: report.region = security_group.region
public = False # Loop through every security group's ingress rule and check it
report = Check_Report(self.metadata) for ingress_rule in security_group.ingress_rules:
report.region = region public = check_security_group(ingress_rule, "tcp", check_ports)
# Loop through every security group's ingress rule and check it # Check
for ingress_rule in security_group.ingress_rules: if public:
public = check_security_group(ingress_rule, "tcp", check_ports) report.status = "FAIL"
# Check report.status_extended = f"Security group {security_group.name} ({security_group.id}) has the MySQL port 3306 open to the Internet."
if public: report.resource_id = security_group.id
report.status = "FAIL" else:
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has the MySQL port 3306 open to the Internet." report.status = "PASS"
report.resource_id = security_group.id report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not MySQL port 3306 open to the Internet."
else: report.resource_id = security_group.id
report.status = "PASS"
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not MySQL port 3306 open to the Internet."
report.resource_id = security_group.id
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS"
report.status_extended = "There are no EC2 security groups."
report.region = region
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -1,37 +1,26 @@
from lib.check.models import Check, Check_Report from lib.check.models import Check, Check_Report
from providers.aws.services.ec2.ec2_service import check_security_group, ec2_client from providers.aws.services.ec2.ec2_service import check_security_group, ec2_client
class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483(Check): class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483(Check):
def execute(self): def execute(self):
findings = [] findings = []
check_ports = [1521, 2483] check_ports = [1521, 2483]
for regional_client in ec2_client.regional_clients: for security_group in ec2_client.security_groups:
region = regional_client.region public = False
if regional_client.security_groups: report = Check_Report(self.metadata)
for security_group in regional_client.security_groups: report.region = security_group.region
public = False # Loop through every security group's ingress rule and check it
report = Check_Report(self.metadata) for ingress_rule in security_group.ingress_rules:
report.region = region public = check_security_group(ingress_rule, "tcp", check_ports)
# Loop through every security group's ingress rule and check it # Check
for ingress_rule in security_group.ingress_rules: if public:
public = check_security_group(ingress_rule, "tcp", check_ports) report.status = "FAIL"
# Check report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Oracle ports 1521 and 2483 open to the Internet."
if public: report.resource_id = security_group.id
report.status = "FAIL" else:
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Oracle ports 1521 and 2483 open to the Internet." report.status = "PASS"
report.resource_id = security_group.id report.status_extended = f"Security group {security_group.name} ({security_group.id}) has not Oracle ports 1521 and 2483 open to the Internet."
else: report.resource_id = security_group.id
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."
report.resource_id = security_group.id
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS"
report.status_extended = "There are no EC2 security groups."
report.region = region
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -13,18 +13,22 @@ class EC2:
self.session = audit_info.audit_session self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account self.audited_account = audit_info.audited_account
self.regional_clients = generate_regional_clients(self.service, audit_info) self.regional_clients = generate_regional_clients(self.service, audit_info)
self.instances = []
self.__threading_call__(self.__describe_instances__) self.__threading_call__(self.__describe_instances__)
self.security_groups = []
self.__threading_call__(self.__describe_security_groups__) self.__threading_call__(self.__describe_security_groups__)
self.network_acls = []
self.__threading_call__(self.__describe_network_acls__) self.__threading_call__(self.__describe_network_acls__)
self.snapshots = []
self.__threading_call__(self.__describe_snapshots__) self.__threading_call__(self.__describe_snapshots__)
self.__threading_call__(self.__get_snapshot_public__) self.__get_snapshot_public__()
def __get_session__(self): def __get_session__(self):
return self.session return self.session
def __threading_call__(self, call): def __threading_call__(self, call):
threads = [] threads = []
for regional_client in self.regional_clients: for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,))) threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads: for t in threads:
t.start() t.start()
@@ -37,7 +41,6 @@ class EC2:
describe_instances_paginator = regional_client.get_paginator( describe_instances_paginator = regional_client.get_paginator(
"describe_instances" "describe_instances"
) )
instances = []
for page in describe_instances_paginator.paginate(): for page in describe_instances_paginator.paginate():
for reservation in page["Reservations"]: for reservation in page["Reservations"]:
for instance in reservation["Instances"]: for instance in reservation["Instances"]:
@@ -45,9 +48,10 @@ class EC2:
"PublicDnsName" in instance "PublicDnsName" in instance
and "PublicIpAddress" in instance and "PublicIpAddress" in instance
): ):
instances.append( self.instances.append(
Instance( Instance(
instance["InstanceId"], instance["InstanceId"],
regional_client.region,
instance["InstanceType"], instance["InstanceType"],
instance["ImageId"], instance["ImageId"],
instance["LaunchTime"], instance["LaunchTime"],
@@ -58,9 +62,10 @@ class EC2:
) )
) )
else: else:
instances.append( self.instances.append(
Instance( Instance(
instance["InstanceId"], instance["InstanceId"],
regional_client.region,
instance["InstanceType"], instance["InstanceType"],
instance["ImageId"], instance["ImageId"],
instance["LaunchTime"], instance["LaunchTime"],
@@ -74,9 +79,6 @@ class EC2:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}: {error}" f"{regional_client.region} -- {error.__class__.__name__}: {error}"
) )
regional_client.instances = []
else:
regional_client.instances = instances
def __describe_security_groups__(self, regional_client): def __describe_security_groups__(self, regional_client):
logger.info("EC2 - Describing Security Groups...") logger.info("EC2 - Describing Security Groups...")
@@ -84,12 +86,12 @@ class EC2:
describe_security_groups_paginator = regional_client.get_paginator( describe_security_groups_paginator = regional_client.get_paginator(
"describe_security_groups" "describe_security_groups"
) )
security_groups = []
for page in describe_security_groups_paginator.paginate(): for page in describe_security_groups_paginator.paginate():
for sg in page["SecurityGroups"]: for sg in page["SecurityGroups"]:
security_groups.append( self.security_groups.append(
SecurityGroup( SecurityGroup(
sg["GroupName"], sg["GroupName"],
regional_client.region,
sg["GroupId"], sg["GroupId"],
sg["IpPermissions"], sg["IpPermissions"],
sg["IpPermissionsEgress"], sg["IpPermissionsEgress"],
@@ -99,9 +101,6 @@ class EC2:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}: {error}" f"{regional_client.region} -- {error.__class__.__name__}: {error}"
) )
regional_client.security_groups = []
else:
regional_client.security_groups = security_groups
def __describe_network_acls__(self, regional_client): def __describe_network_acls__(self, regional_client):
logger.info("EC2 - Describing Security Groups...") logger.info("EC2 - Describing Security Groups...")
@@ -109,19 +108,19 @@ class EC2:
describe_network_acls_paginator = regional_client.get_paginator( describe_network_acls_paginator = regional_client.get_paginator(
"describe_network_acls" "describe_network_acls"
) )
network_acls = []
for page in describe_network_acls_paginator.paginate(): for page in describe_network_acls_paginator.paginate():
for nacl in page["NetworkAcls"]: for nacl in page["NetworkAcls"]:
network_acls.append( self.network_acls.append(
NetworkACL(nacl["NetworkAclId"], nacl["Entries"]) NetworkACL(
nacl["NetworkAclId"],
regional_client.region,
nacl["Entries"],
)
) )
except Exception as error: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}: {error}" f"{regional_client.region} -- {error.__class__.__name__}: {error}"
) )
regional_client.network_acls = []
else:
regional_client.network_acls = network_acls
def __describe_snapshots__(self, regional_client): def __describe_snapshots__(self, regional_client):
logger.info("EC2 - Describing Snapshots...") logger.info("EC2 - Describing Snapshots...")
@@ -129,7 +128,6 @@ class EC2:
describe_snapshots_paginator = regional_client.get_paginator( describe_snapshots_paginator = regional_client.get_paginator(
"describe_snapshots" "describe_snapshots"
) )
snapshots = []
encrypted = False encrypted = False
for page in describe_snapshots_paginator.paginate( for page in describe_snapshots_paginator.paginate(
OwnerIds=[self.audited_account] OwnerIds=[self.audited_account]
@@ -137,36 +135,36 @@ class EC2:
for snapshot in page["Snapshots"]: for snapshot in page["Snapshots"]:
if snapshot["Encrypted"]: if snapshot["Encrypted"]:
encrypted = True encrypted = True
snapshots.append(Snapshot(snapshot["SnapshotId"], encrypted)) self.snapshots.append(
Snapshot(
snapshot["SnapshotId"], regional_client.region, encrypted
)
)
except Exception as error: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}: {error}" f"{regional_client.region} -- {error.__class__.__name__}: {error}"
) )
regional_client.snapshots = []
else:
regional_client.snapshots = snapshots
def __get_snapshot_public__(self, regional_client): def __get_snapshot_public__(self):
logger.info("EC2 - Get snapshots encryption...") logger.info("EC2 - Get snapshots encryption...")
try: try:
if hasattr(regional_client, "snapshots"): for snapshot in self.snapshots:
for snapshot in regional_client.snapshots: regional_client = self.regional_clients[snapshot.region]
snapshot_public = regional_client.describe_snapshot_attribute( snapshot_public = regional_client.describe_snapshot_attribute(
Attribute="createVolumePermission", SnapshotId=snapshot.id Attribute="createVolumePermission", SnapshotId=snapshot.id
) )
for permission in snapshot_public["CreateVolumePermissions"]: for permission in snapshot_public["CreateVolumePermissions"]:
if "Group" in permission: if "Group" in permission:
if permission["Group"] == "all": if permission["Group"] == "all":
snapshot.public = True snapshot.public = True
except Exception as error: except Exception as error:
logger.error( logger.error(f"{error.__class__.__name__}: {error}")
f"{regional_client.region} -- {error.__class__.__name__}: {error}"
)
@dataclass @dataclass
class Instance: class Instance:
id: str id: str
region: str
type: str type: str
image_id: str image_id: str
launch_time: str launch_time: str
@@ -178,6 +176,7 @@ class Instance:
def __init__( def __init__(
self, self,
id, id,
region,
type, type,
image_id, image_id,
launch_time, launch_time,
@@ -187,6 +186,7 @@ class Instance:
public_ip, public_ip,
): ):
self.id = id self.id = id
self.region = region
self.type = type self.type = type
self.image_id = image_id self.image_id = image_id
self.launch_time = launch_time self.launch_time = launch_time
@@ -199,11 +199,13 @@ class Instance:
@dataclass @dataclass
class Snapshot: class Snapshot:
id: str id: str
region: str
encrypted: bool encrypted: bool
public: bool public: bool
def __init__(self, id, encrypted): def __init__(self, id, region, encrypted):
self.id = id self.id = id
self.region = region
self.encrypted = encrypted self.encrypted = encrypted
self.public = False self.public = False
@@ -211,12 +213,14 @@ class Snapshot:
@dataclass @dataclass
class SecurityGroup: class SecurityGroup:
name: str name: str
region: str
id: str id: str
ingress_rules: list[dict] ingress_rules: list[dict]
egress_rules: list[dict] egress_rules: list[dict]
def __init__(self, name, id, ingress_rules, egress_rules): def __init__(self, name, region, id, ingress_rules, egress_rules):
self.name = name self.name = name
self.region = region
self.id = id self.id = id
self.ingress_rules = ingress_rules self.ingress_rules = ingress_rules
self.egress_rules = egress_rules self.egress_rules = egress_rules
@@ -227,8 +231,9 @@ class NetworkACL:
id: str id: str
entries: list[dict] entries: list[dict]
def __init__(self, id, entries): def __init__(self, id, region, entries):
self.id = id self.id = id
self.region = region
self.entries = entries self.entries = entries

View File

@@ -7,58 +7,50 @@ class iam_administrator_access_with_mfa(Check):
findings = [] findings = []
response = iam_client.groups response = iam_client.groups
if response: for group in response:
for group in response:
report = Check_Report(self.metadata)
report.resource_id = group.name
report.resource_arn = group.arn
report.region = iam_client.region
if group.attached_policies:
admin_policy = False
for group_policy in group.attached_policies:
if (
group_policy["PolicyArn"]
== "arn:aws:iam::aws:policy/AdministratorAccess"
):
admin_policy = True
# users in group are Administrators
if group.users:
for group_user in group.users:
for user in iam_client.credential_report:
if (
user["user"] == group_user.name
and user["mfa_active"] == "false"
):
report.status = "FAIL"
report.status_extended = f"Group {group.name} provides administrator access to User {group_user.name} with MFA disabled."
findings.append(report)
elif (
user["user"] == group_user.name
and user["mfa_active"] == "true"
):
report.status = "PASS"
report.status_extended = f"Group {group.name} provides administrator access to User {group_user.name} with MFA enabled."
findings.append(report)
else:
report.status = "PASS"
report.status_extended = f"Group {group.name} provides administrative access but does not have users."
findings.append(report)
if not admin_policy:
report.status = "PASS"
report.status_extended = (
f"Group {group.name} provides non-administrative access."
)
findings.append(report)
else:
report.status = "PASS"
report.status_extended = f"Group {group.name} has no policies."
findings.append(report)
else:
report = Check_Report(self.metadata) report = Check_Report(self.metadata)
report.status = "PASS" report.resource_id = group.name
report.status_extended = "There is no IAM groups." report.resource_arn = group.arn
report.region = iam_client.region report.region = iam_client.region
findings.append(report) if group.attached_policies:
admin_policy = False
for group_policy in group.attached_policies:
if (
group_policy["PolicyArn"]
== "arn:aws:iam::aws:policy/AdministratorAccess"
):
admin_policy = True
# users in group are Administrators
if group.users:
for group_user in group.users:
for user in iam_client.credential_report:
if (
user["user"] == group_user.name
and user["mfa_active"] == "false"
):
report.status = "FAIL"
report.status_extended = f"Group {group.name} provides administrator access to User {group_user.name} with MFA disabled."
findings.append(report)
elif (
user["user"] == group_user.name
and user["mfa_active"] == "true"
):
report.status = "PASS"
report.status_extended = f"Group {group.name} provides administrator access to User {group_user.name} with MFA enabled."
findings.append(report)
else:
report.status = "PASS"
report.status_extended = f"Group {group.name} provides administrative access but does not have users."
findings.append(report)
if not admin_policy:
report.status = "PASS"
report.status_extended = (
f"Group {group.name} provides non-administrative access."
)
findings.append(report)
else:
report.status = "PASS"
report.status_extended = f"Group {group.name} has no policies."
findings.append(report)
return findings return findings

View File

@@ -11,51 +11,50 @@ class iam_avoid_root_usage(Check):
findings = [] findings = []
response = iam_client.credential_report response = iam_client.credential_report
if response: for user in response:
for user in response: if user["user"] == "<root_account>":
if user["user"] == "<root_account>": report = Check_Report(self.metadata)
report = Check_Report(self.metadata) report.region = iam_client.region
report.region = iam_client.region report.resource_id = user["user"]
report.resource_id = user["user"] report.resource_arn = user["arn"]
report.resource_arn = user["arn"] if (
if ( user["password_last_used"] != "no_information"
user["password_last_used"] != "no_information" or user["access_key_1_last_used_date"] != "N/A"
or user["access_key_1_last_used_date"] != "N/A" or user["access_key_2_last_used_date"] != "N/A"
or user["access_key_2_last_used_date"] != "N/A" ):
): if user["password_last_used"] != "no_information":
if user["password_last_used"] != "no_information": days_since_accessed = (
days_since_accessed = ( datetime.datetime.now()
datetime.datetime.now() - datetime.datetime.strptime(
- datetime.datetime.strptime( user["password_last_used"],
user["password_last_used"], "%Y-%m-%dT%H:%M:%S+00:00",
"%Y-%m-%dT%H:%M:%S+00:00", )
) ).days
).days elif user["access_key_1_last_used_date"] != "N/A":
elif user["access_key_1_last_used_date"] != "N/A": days_since_accessed = (
days_since_accessed = ( datetime.datetime.now()
datetime.datetime.now() - datetime.datetime.strptime(
- datetime.datetime.strptime( user["access_key_1_last_used_date"],
user["access_key_1_last_used_date"], "%Y-%m-%dT%H:%M:%S+00:00",
"%Y-%m-%dT%H:%M:%S+00:00", )
) ).days
).days elif user["access_key_2_last_used_date"] != "N/A":
elif user["access_key_2_last_used_date"] != "N/A": days_since_accessed = (
days_since_accessed = ( datetime.datetime.now()
datetime.datetime.now() - datetime.datetime.strptime(
- datetime.datetime.strptime( user["access_key_2_last_used_date"],
user["access_key_2_last_used_date"], "%Y-%m-%dT%H:%M:%S+00:00",
"%Y-%m-%dT%H:%M:%S+00:00", )
) ).days
).days if days_since_accessed > maximum_access_days:
if days_since_accessed > maximum_access_days: report.status = "FAIL"
report.status = "FAIL" report.status_extended = f"Root user in the account was last accessed {days_since_accessed} days ago."
report.status_extended = f"Root user in the account was last accessed {days_since_accessed} days ago."
else:
report.status = "PASS"
report.status_extended = f"Root user in the account wasn't accessed in the last {maximum_access_days} days."
else: else:
report.status = "PASS" report.status = "PASS"
report.status_extended = f"Root user in the account wasn't accessed in the last {maximum_access_days} days." report.status_extended = f"Root user in the account wasn't accessed in the last {maximum_access_days} days."
findings.append(report) else:
report.status = "PASS"
report.status_extended = f"Root user in the account wasn't accessed in the last {maximum_access_days} days."
findings.append(report)
return findings return findings

View File

@@ -11,42 +11,35 @@ class iam_disable_30_days_credentials(Check):
findings = [] findings = []
response = iam_client.users response = iam_client.users
if response: for user in response:
for user in response:
report = Check_Report(self.metadata)
report.resource_id = user.name
report.resource_arn = user.arn
report.region = iam_client.region
if user.password_last_used and user.password_last_used != "":
try:
time_since_insertion = (
datetime.datetime.now()
- datetime.datetime.strptime(
str(user.password_last_used), "%Y-%m-%d %H:%M:%S+00:00"
)
)
if time_since_insertion.days > maximum_expiration_days:
report.status = "FAIL"
report.status_extended = f"User {user.name} has not logged into the console in the past 30 days."
else:
report.status = "PASS"
report.status_extended = f"User {user.name} has logged into the console in the past 30 days."
except KeyError:
pass
else:
report.status = "PASS"
report.status_extended = (
f"User {user.name} has not a console password or is unused."
)
# Append report
findings.append(report)
else:
report = Check_Report(self.metadata) report = Check_Report(self.metadata)
report.status = "PASS" report.resource_id = user.name
report.status_extended = "There is no IAM users." report.resource_arn = user.arn
report.region = iam_client.region report.region = iam_client.region
if user.password_last_used and user.password_last_used != "":
try:
time_since_insertion = (
datetime.datetime.now()
- datetime.datetime.strptime(
str(user.password_last_used), "%Y-%m-%d %H:%M:%S+00:00"
)
)
if time_since_insertion.days > maximum_expiration_days:
report.status = "FAIL"
report.status_extended = f"User {user.name} has not logged into the console in the past 30 days."
else:
report.status = "PASS"
report.status_extended = f"User {user.name} has logged into the console in the past 30 days."
except KeyError:
pass
else:
report.status = "PASS"
report.status_extended = (
f"User {user.name} has not a console password or is unused."
)
# Append report
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -11,42 +11,35 @@ class iam_disable_90_days_credentials(Check):
findings = [] findings = []
response = iam_client.users response = iam_client.users
if response: for user in response:
for user in response:
report = Check_Report(self.metadata)
report.region = iam_client.region
report.resource_id = user.name
report.resource_arn = user.arn
if user.password_last_used and user.password_last_used != "":
try:
time_since_insertion = (
datetime.datetime.now()
- datetime.datetime.strptime(
str(user.password_last_used), "%Y-%m-%d %H:%M:%S+00:00"
)
)
if time_since_insertion.days > maximum_expiration_days:
report.status = "FAIL"
report.status_extended = f"User {user.name} has not logged into the console in the past 90 days."
else:
report.status = "PASS"
report.status_extended = f"User {user.name} has logged into the console in the past 90 days."
except KeyError:
pass
else:
report.status = "PASS"
report.status_extended = (
f"User {user.name} has not a console password or is unused."
)
# Append report
findings.append(report)
else:
report = Check_Report(self.metadata) report = Check_Report(self.metadata)
report.status = "PASS"
report.status_extended = "There is no IAM users."
report.region = iam_client.region report.region = iam_client.region
report.resource_id = user.name
report.resource_arn = user.arn
if user.password_last_used and user.password_last_used != "":
try:
time_since_insertion = (
datetime.datetime.now()
- datetime.datetime.strptime(
str(user.password_last_used), "%Y-%m-%d %H:%M:%S+00:00"
)
)
if time_since_insertion.days > maximum_expiration_days:
report.status = "FAIL"
report.status_extended = f"User {user.name} has not logged into the console in the past 90 days."
else:
report.status = "PASS"
report.status_extended = f"User {user.name} has logged into the console in the past 90 days."
except KeyError:
pass
else:
report.status = "PASS"
report.status_extended = (
f"User {user.name} has not a console password or is unused."
)
# Append report
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -6,19 +6,18 @@ class iam_root_mfa_enabled(Check):
def execute(self) -> Check_Report: def execute(self) -> Check_Report:
findings = [] findings = []
if iam_client.credential_report: for user in iam_client.credential_report:
for user in iam_client.credential_report: if user["user"] == "<root_account>":
if user["user"] == "<root_account>": report = Check_Report(self.metadata)
report = Check_Report(self.metadata) report.region = iam_client.region
report.region = iam_client.region report.resource_id = user["user"]
report.resource_id = user["user"] report.resource_arn = user["arn"]
report.resource_arn = user["arn"] if user["mfa_active"] == "false":
if user["mfa_active"] == "false": report.status = "FAIL"
report.status = "FAIL" report.status_extended = "MFA is not enabled for root account."
report.status_extended = "MFA is not enabled for root account." else:
else: report.status = "PASS"
report.status = "PASS" report.status_extended = "MFA is enabled for root account."
report.status_extended = "MFA is enabled for root account." findings.append(report)
findings.append(report)
return findings return findings

View File

@@ -11,53 +11,48 @@ class iam_rotate_access_key_90_days(Check):
findings = [] findings = []
response = iam_client.credential_report response = iam_client.credential_report
if response: for user in response:
for user in response:
report = Check_Report(self.metadata)
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
if (
user["access_key_1_last_rotated"] == "N/A"
and user["access_key_2_last_rotated"] == "N/A"
):
report.status = "PASS"
report.status_extended = f"User {user['user']} has not access keys."
else:
old_access_keys = False
if user["access_key_1_last_rotated"] != "N/A":
access_key_1_last_rotated = (
datetime.datetime.now()
- datetime.datetime.strptime(
user["access_key_1_last_rotated"],
"%Y-%m-%dT%H:%M:%S+00:00",
)
)
if access_key_1_last_rotated.days > maximum_expiration_days:
old_access_keys = True
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not rotated access key 1 in over 90 days ({access_key_1_last_rotated.days} days)."
if user["access_key_2_last_rotated"] != "N/A":
access_key_2_last_rotated = (
datetime.datetime.now()
- datetime.datetime.strptime(
user["access_key_2_last_rotated"],
"%Y-%m-%dT%H:%M:%S+00:00",
)
)
if access_key_2_last_rotated.days > maximum_expiration_days:
old_access_keys = True
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not rotated access key 2 in over 90 days ({access_key_2_last_rotated.days} days)."
if not old_access_keys:
report.status = "PASS"
report.status_extended = f"User {user['user']} has access keys not older than 90 days."
findings.append(report)
else:
report = Check_Report(self.metadata) report = Check_Report(self.metadata)
report.status = "PASS"
report.status_extended = "There is no IAM users."
report.region = iam_client.region report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
if (
user["access_key_1_last_rotated"] == "N/A"
and user["access_key_2_last_rotated"] == "N/A"
):
report.status = "PASS"
report.status_extended = f"User {user['user']} has not access keys."
else:
old_access_keys = False
if user["access_key_1_last_rotated"] != "N/A":
access_key_1_last_rotated = (
datetime.datetime.now()
- datetime.datetime.strptime(
user["access_key_1_last_rotated"],
"%Y-%m-%dT%H:%M:%S+00:00",
)
)
if access_key_1_last_rotated.days > maximum_expiration_days:
old_access_keys = True
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not rotated access key 1 in over 90 days ({access_key_1_last_rotated.days} days)."
if user["access_key_2_last_rotated"] != "N/A":
access_key_2_last_rotated = (
datetime.datetime.now()
- datetime.datetime.strptime(
user["access_key_2_last_rotated"],
"%Y-%m-%dT%H:%M:%S+00:00",
)
)
if access_key_2_last_rotated.days > maximum_expiration_days:
old_access_keys = True
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not rotated access key 2 in over 90 days ({access_key_2_last_rotated.days} days)."
if not old_access_keys:
report.status = "PASS"
report.status_extended = (
f"User {user['user']} has access keys not older than 90 days."
)
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -7,35 +7,28 @@ class iam_user_hardware_mfa_enabled(Check):
findings = [] findings = []
response = iam_client.users response = iam_client.users
if response: for user in response:
for user in response:
report = Check_Report(self.metadata)
report.resource_id = user.name
report.resource_arn = user.arn
report.region = iam_client.region
if user.mfa_devices:
for mfa_device in user.mfa_devices:
if mfa_device.type == "mfa" or mfa_device.type == "sms-mfa":
report.status = "FAIL"
report.status_extended = f"User {user.name} has a virtual MFA instead of a hardware MFA enabled."
findings.append(report)
else:
report.status = "PASS"
report.status_extended = (
f"User {user.name} has hardware MFA enabled."
)
findings.append(report)
else:
report.status = "FAIL"
report.status_extended = (
f"User {user.name} has not any type of MFA enabled."
)
findings.append(report)
else:
report = Check_Report(self.metadata) report = Check_Report(self.metadata)
report.status = "PASS" report.resource_id = user.name
report.status_extended = "There is no IAM users." report.resource_arn = user.arn
report.region = iam_client.region report.region = iam_client.region
findings.append(report) if user.mfa_devices:
for mfa_device in user.mfa_devices:
if mfa_device.type == "mfa" or mfa_device.type == "sms-mfa":
report.status = "FAIL"
report.status_extended = f"User {user.name} has a virtual MFA instead of a hardware MFA enabled."
findings.append(report)
else:
report.status = "PASS"
report.status_extended = (
f"User {user.name} has hardware MFA enabled."
)
findings.append(report)
else:
report.status = "FAIL"
report.status_extended = (
f"User {user.name} has not any type of MFA enabled."
)
findings.append(report)
return findings return findings

View File

@@ -6,31 +6,23 @@ class iam_user_mfa_enabled_console_access(Check):
def execute(self) -> Check_Report: def execute(self) -> Check_Report:
findings = [] findings = []
response = iam_client.credential_report response = iam_client.credential_report
for user in response:
if response: report = Check_Report(self.metadata)
for user in response: report.resource_id = user["user"]
report = Check_Report(self.metadata) report.resource_arn = user["arn"]
report.resource_id = user["user"] report.region = iam_client.region
report.resource_arn = user["arn"] if user["password_enabled"] != "not_supported":
report.region = iam_client.region if user["mfa_active"] == "false":
if user["password_enabled"] != "not_supported": report.status = "FAIL"
if user["mfa_active"] == "false": report.status_extended = f"User {user['user']} has Console Password enabled but MFA disabled."
report.status = "FAIL"
report.status_extended = f"User {user['user']} has Console Password enabled but MFA disabled."
else:
report.status = "PASS"
report.status_extended = f"User {user['user']} has Console Password enabled and MFA enabled."
else: else:
report.status = "PASS" report.status = "PASS"
report.status_extended = ( report.status_extended = f"User {user['user']} has Console Password enabled and MFA enabled."
f"User {user['user']} has not Console Password enabled." else:
) report.status = "PASS"
findings.append(report) report.status_extended = (
else: f"User {user['user']} has not Console Password enabled."
report = Check_Report(self.metadata) )
report.status = "PASS"
report.status_extended = "There is no IAM users."
report.region = iam_client.region
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -6,33 +6,24 @@ class iam_user_two_active_access_key(Check):
def execute(self) -> Check_Report: def execute(self) -> Check_Report:
findings = [] findings = []
response = iam_client.credential_report response = iam_client.credential_report
for user in response:
if response:
for user in response:
report = Check_Report(self.metadata)
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.region = iam_client.region
if (
user["access_key_1_active"] == "true"
and user["access_key_2_active"] == "true"
):
report.status = "FAIL"
report.status_extended = (
f"User {user['user']} has 2 active access keys."
)
findings.append(report)
else:
report.status = "PASS"
report.status_extended = (
f"User {user['user']} has not 2 active access keys."
)
findings.append(report)
else:
report = Check_Report(self.metadata) report = Check_Report(self.metadata)
report.status = "PASS" report.resource_id = user["user"]
report.status_extended = "There is no IAM users." report.resource_arn = user["arn"]
report.region = iam_client.region report.region = iam_client.region
if (
user["access_key_1_active"] == "true"
and user["access_key_2_active"] == "true"
):
report.status = "FAIL"
report.status_extended = (
f"User {user['user']} has 2 active access keys."
)
else:
report.status = "PASS"
report.status_extended = (
f"User {user['user']} has not 2 active access keys."
)
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -5,29 +5,20 @@ from providers.aws.services.s3.s3_service import s3_client
class s3_bucket_object_versioning(Check): class s3_bucket_object_versioning(Check):
def execute(self): def execute(self):
findings = [] findings = []
for regional_client in s3_client.regional_clients: for bucket in s3_client.buckets:
region = regional_client.region report = Check_Report(self.metadata)
if regional_client.buckets: report.region = bucket.region
for bucket in regional_client.buckets: report.resource_id = bucket.name
report = Check_Report(self.metadata) if bucket.versioning:
report.region = region
report.resource_id = bucket.name
if bucket.versioning:
report.status = "PASS"
report.status_extended = (
f"S3 Bucket {bucket.name} has versioning enabled."
)
else:
report.status = "FAIL"
report.status_extended = (
f"S3 Bucket {bucket.name} has versioning disabled."
)
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS" report.status = "PASS"
report.status_extended = "There are no S3 buckets." report.status_extended = (
report.region = region f"S3 Bucket {bucket.name} has versioning enabled."
findings.append(report) )
else:
report.status = "FAIL"
report.status_extended = (
f"S3 Bucket {bucket.name} has versioning disabled."
)
findings.append(report)
return findings return findings

View File

@@ -5,25 +5,20 @@ from providers.aws.services.s3.s3_service import s3_client
class s3_bucket_server_access_logging_enabled(Check): class s3_bucket_server_access_logging_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for regional_client in s3_client.regional_clients: for bucket in s3_client.buckets:
region = regional_client.region report = Check_Report(self.metadata)
if regional_client.buckets: report.region = bucket.region
for bucket in regional_client.buckets: report.resource_id = bucket.name
report = Check_Report(self.metadata) if bucket.logging:
report.region = region
report.resource_id = bucket.name
if bucket.logging:
report.status = "PASS"
report.status_extended = f"S3 Bucket {bucket.name} has server access logging enabled."
else:
report.status = "FAIL"
report.status_extended = f"S3 Bucket {bucket.name} has server access logging disabled."
findings.append(report)
else:
report = Check_Report(self.metadata)
report.status = "PASS" report.status = "PASS"
report.status_extended = "There are no S3 buckets." report.status_extended = (
report.region = region f"S3 Bucket {bucket.name} has server access logging enabled."
findings.append(report) )
else:
report.status = "FAIL"
report.status_extended = (
f"S3 Bucket {bucket.name} has server access logging disabled."
)
findings.append(report)
return findings return findings

View File

@@ -10,9 +10,10 @@ class S3:
def __init__(self, audit_info): def __init__(self, audit_info):
self.service = "s3" self.service = "s3"
self.session = audit_info.audit_session self.session = audit_info.audit_session
self.client = self.session.client(self.service)
self.audited_account = audit_info.audited_account self.audited_account = audit_info.audited_account
self.regional_clients = generate_regional_clients(self.service, audit_info) self.regional_clients = generate_regional_clients(self.service, audit_info)
self.__threading_call__(self.__list_buckets__) self.buckets = self.__list_buckets__()
self.__threading_call__(self.__get_bucket_versioning__) self.__threading_call__(self.__get_bucket_versioning__)
self.__threading_call__(self.__get_bucket_logging__) self.__threading_call__(self.__get_bucket_logging__)
@@ -21,64 +22,50 @@ class S3:
def __threading_call__(self, call): def __threading_call__(self, call):
threads = [] threads = []
for regional_client in self.regional_clients: for bucket in self.buckets:
threads.append(threading.Thread(target=call, args=(regional_client,))) threads.append(threading.Thread(target=call, args=(bucket,)))
for t in threads: for t in threads:
t.start() t.start()
for t in threads: for t in threads:
t.join() t.join()
def __list_buckets__(self, regional_client): def __list_buckets__(self):
logger.info("S3 - Listing buckets...") logger.info("S3 - Listing buckets...")
try: try:
list_buckets = regional_client.list_buckets()
buckets = [] buckets = []
list_buckets = self.client.list_buckets()
for bucket in list_buckets["Buckets"]: for bucket in list_buckets["Buckets"]:
try: bucket_region = self.client.get_bucket_location(Bucket=bucket["Name"])[
bucket_region = regional_client.get_bucket_location( "LocationConstraint"
Bucket=bucket["Name"] ]
)["LocationConstraint"] if not bucket_region: # If us-east-1, bucket_region is none
if regional_client.region == bucket_region or ( buckets.append(Bucket(bucket["Name"], "us-east-1"))
regional_client.region == "us-east-1" and not bucket_region else:
): # If us-east-1, bucket_region is none buckets.append(Bucket(bucket["Name"], bucket_region))
buckets.append(Bucket(bucket["Name"])) return buckets
except Exception as error:
if error.__class__.__name__ != "NoSuchBucket":
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}: {error}"
)
regional_client.buckets = buckets
except Exception as error: except Exception as error:
logger.error( logger.error(f"{bucket_region} -- {error.__class__.__name__}: {error}")
f"{regional_client.region} -- {error.__class__.__name__}: {error}"
)
def __get_bucket_versioning__(self, regional_client): def __get_bucket_versioning__(self, bucket):
logger.info("S3 - Get buckets versioning...") logger.info("S3 - Get buckets versioning...")
try: try:
if hasattr(regional_client, "buckets"): regional_client = self.regional_clients[bucket.region]
for bucket in regional_client.buckets: bucket_versioning = regional_client.get_bucket_versioning(
bucket_versioning = regional_client.get_bucket_versioning( Bucket=bucket.name
Bucket=bucket.name
)
if "Status" in bucket_versioning:
if "Enabled" == bucket_versioning["Status"]:
bucket.versioning = True
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}: {error}"
) )
if "Status" in bucket_versioning:
if "Enabled" == bucket_versioning["Status"]:
bucket.versioning = True
except Exception as error:
logger.error(f"{bucket.region} -- {error.__class__.__name__}: {error}")
def __get_bucket_logging__(self, regional_client): def __get_bucket_logging__(self, bucket):
logger.info("S3 - Get buckets logging...") logger.info("S3 - Get buckets logging...")
try: try:
if hasattr(regional_client, "buckets"): regional_client = self.regional_clients[bucket.region]
for bucket in regional_client.buckets: bucket_logging = regional_client.get_bucket_logging(Bucket=bucket.name)
bucket_logging = regional_client.get_bucket_logging( if "LoggingEnabled" in bucket_logging:
Bucket=bucket.name bucket.logging = True
)
if "LoggingEnabled" in bucket_logging:
bucket.logging = True
except Exception as error: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}: {error}" f"{regional_client.region} -- {error.__class__.__name__}: {error}"
@@ -90,11 +77,13 @@ class Bucket:
name: str name: str
versioning: bool versioning: bool
logging: bool logging: bool
region: str
def __init__(self, name): def __init__(self, name, region):
self.name = name self.name = name
self.versioning = False self.versioning = False
self.logging = False self.logging = False
self.region = region
s3_client = S3(current_audit_info) s3_client = S3(current_audit_info)