chore(backport): include latest changes of v4 in v3 (#4027)

This commit is contained in:
Sergio Garcia
2024-05-21 13:17:16 -04:00
committed by GitHub
parent a3cfca56bc
commit fd46c704e4
40 changed files with 1464 additions and 585 deletions
+6 -1
View File
@@ -33,7 +33,7 @@ The following list includes all the AWS checks with configurable variables that
| `drs_job_exist` | `allowlist_non_default_regions` | Boolean |
| `guardduty_is_enabled` | `allowlist_non_default_regions` | Boolean |
| `securityhub_enabled` | `allowlist_non_default_regions` | Boolean |
| `rds_instance_backup_enabled` | `check_rds_instance_replicas` | Boolean |
## Azure
### Configurable Checks
@@ -144,6 +144,11 @@ aws:
# trustedadvisor_premium_support_plan_subscribed
verify_premium_support_plans: True
# AWS RDS
# aws.rds_instance_backup_enabled
# Whether to check RDS instance replicas or not
check_rds_instance_replicas: False
# Azure Configuration
azure:
# Azure Network Configuration
Generated
+398 -438
View File
File diff suppressed because it is too large Load Diff
@@ -363,7 +363,7 @@
"Checks": [
"ec2_ami_public",
"ec2_instance_public_ip",
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
@@ -719,7 +719,7 @@
"ec2_networkacl_allow_ingress_any_port",
"ec2_networkacl_allow_ingress_tcp_port_22",
"ec2_networkacl_allow_ingress_tcp_port_3389",
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
+1 -1
View File
@@ -1168,7 +1168,7 @@
"Id": "5.2",
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 to remote server administration ports",
"Checks": [
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389"
],
+2 -2
View File
@@ -1252,7 +1252,7 @@
"Id": "5.2",
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 to remote server administration ports",
"Checks": [
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389"
],
@@ -1275,7 +1275,7 @@
"Id": "5.3",
"Description": "Ensure no security groups allow ingress from ::/0 to remote server administration ports",
"Checks": [
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389"
],
+2 -2
View File
@@ -1250,7 +1250,7 @@
"Id": "5.2",
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 to remote server administration ports",
"Checks": [
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389"
],
@@ -1273,7 +1273,7 @@
"Id": "5.3",
"Description": "Ensure no security groups allow ingress from ::/0 to remote server administration ports",
"Checks": [
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389"
],
+2 -2
View File
@@ -1208,7 +1208,7 @@
"Id": "5.2",
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 to remote server administration ports",
"Checks": [
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389"
],
@@ -1231,7 +1231,7 @@
"Id": "5.3",
"Description": "Ensure no security groups allow ingress from ::/0 to remote server administration ports",
"Checks": [
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389"
],
+2 -2
View File
@@ -134,7 +134,7 @@
"vpc_endpoint_connections_trust_boundaries",
"ec2_securitygroup_default_restrict_traffic",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
]
},
{
@@ -297,7 +297,7 @@
"vpc_flow_logs_enabled",
"ec2_networkacl_allow_ingress_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
]
},
{
+1 -1
View File
@@ -2157,7 +2157,7 @@
}
],
"Checks": [
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
]
},
{
+5 -5
View File
@@ -106,7 +106,7 @@
"ec2_networkacl_allow_ingress_any_port",
"ec2_networkacl_allow_ingress_tcp_port_22",
"ec2_networkacl_allow_ingress_tcp_port_3389",
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
@@ -1022,7 +1022,7 @@
"ec2_networkacl_allow_ingress_any_port",
"ec2_networkacl_allow_ingress_tcp_port_22",
"ec2_networkacl_allow_ingress_tcp_port_3389",
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
@@ -1468,7 +1468,7 @@
"ec2_networkacl_allow_ingress_any_port",
"ec2_networkacl_allow_ingress_tcp_port_22",
"ec2_networkacl_allow_ingress_tcp_port_3389",
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
@@ -1648,7 +1648,7 @@
"ec2_networkacl_allow_ingress_any_port",
"ec2_networkacl_allow_ingress_tcp_port_22",
"ec2_networkacl_allow_ingress_tcp_port_3389",
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
@@ -1900,7 +1900,7 @@
"ec2_networkacl_allow_ingress_any_port",
"ec2_networkacl_allow_ingress_tcp_port_22",
"ec2_networkacl_allow_ingress_tcp_port_3389",
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21",
"ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22",
+5
View File
@@ -96,6 +96,11 @@ aws:
# trustedadvisor_premium_support_plan_subscribed
verify_premium_support_plans: True
# AWS RDS
# aws.rds_instance_backup_enabled
# Whether to check RDS instance replicas or not
check_rds_instance_replicas: False
# Azure Configuration
azure:
# Azure Network Configuration
+40 -12
View File
@@ -1,10 +1,13 @@
import os
import pathlib
import sys
from datetime import datetime
from boto3 import client, session
from botocore.credentials import RefreshableCredentials
from botocore.session import get_session
from pytz import utc
from tzlocal import get_localzone
from prowler.config.config import aws_services_json_file
from prowler.lib.check.check import list_modules, recover_checks_from_service
@@ -14,6 +17,7 @@ from prowler.providers.aws.config import (
AWS_STS_GLOBAL_ENDPOINT_REGION,
ROLE_SESSION_NAME,
)
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.lib.audit_info.models import AWS_Assume_Role, AWS_Audit_Info
from prowler.providers.aws.lib.credentials.credentials import create_sts_session
@@ -98,18 +102,31 @@ class AWS_Provider:
# https://github.com/boto/botocore/blob/098cc255f81a25b852e1ecdeb7adebd94c7b1b73/botocore/credentials.py#L570
def refresh_credentials(self):
logger.info("Refreshing assumed credentials...")
response = assume_role(self.aws_session, self.role_info)
refreshed_credentials = dict(
# Keys of the dict has to be the same as those that are being searched in the parent class
# https://github.com/boto/botocore/blob/098cc255f81a25b852e1ecdeb7adebd94c7b1b73/botocore/credentials.py#L609
access_key=response["Credentials"]["AccessKeyId"],
secret_key=response["Credentials"]["SecretAccessKey"],
token=response["Credentials"]["SessionToken"],
expiry_time=response["Credentials"]["Expiration"].isoformat(),
)
logger.info("Refreshed Credentials:")
logger.info(refreshed_credentials)
current_credentials = current_audit_info.credentials
refreshed_credentials = {
"access_key": current_credentials.aws_access_key_id,
"secret_key": current_credentials.aws_secret_access_key,
"token": current_credentials.aws_session_token,
"expiry_time": (
current_credentials.expiration.isoformat()
if hasattr(current_credentials, "expiration")
else current_credentials.expiry_time.isoformat()
),
}
if datetime.fromisoformat(refreshed_credentials["expiry_time"]) <= datetime.now(
get_localzone()
):
response = assume_role(self.aws_session, self.role_info)
refreshed_credentials = dict(
# Keys of the dict has to be the same as those that are being searched in the parent class
# https://github.com/boto/botocore/blob/098cc255f81a25b852e1ecdeb7adebd94c7b1b73/botocore/credentials.py#L609
access_key=response["Credentials"]["AccessKeyId"],
secret_key=response["Credentials"]["SecretAccessKey"],
token=response["Credentials"]["SessionToken"],
expiry_time=response["Credentials"]["Expiration"].isoformat(),
)
logger.info("Refreshed Credentials:")
logger.info(refreshed_credentials)
return refreshed_credentials
@@ -146,6 +163,17 @@ def assume_role(
sts_client = create_sts_session(session, sts_endpoint_region)
assumed_credentials = sts_client.assume_role(**assume_role_arguments)
# Convert the UTC datetime object to your local timezone
credentials_expiration_local_time = (
assumed_credentials["Credentials"]["Expiration"]
.replace(tzinfo=utc)
.astimezone(get_localzone())
)
assumed_credentials["Credentials"][
"Expiration"
] = credentials_expiration_local_time
except Exception as error:
logger.critical(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- {error}"
@@ -6161,6 +6161,7 @@
"ap-southeast-3",
"ap-southeast-4",
"ca-central-1",
"ca-west-1",
"eu-central-1",
"eu-central-2",
"eu-north-1",
@@ -7073,6 +7074,8 @@
"mwaa": {
"regions": {
"aws": [
"af-south-1",
"ap-east-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-south-1",
@@ -7081,12 +7084,15 @@
"ca-central-1",
"eu-central-1",
"eu-north-1",
"eu-south-1",
"eu-west-1",
"eu-west-2",
"eu-west-3",
"me-south-1",
"sa-east-1",
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2"
],
"aws-cn": [
@@ -7407,10 +7413,13 @@
"regions": {
"aws": [
"ap-northeast-1",
"ap-south-1",
"ap-southeast-1",
"ap-southeast-2",
"eu-central-1",
"eu-west-1",
"eu-west-2",
"eu-west-3",
"us-east-1",
"us-east-2",
"us-west-2"
@@ -8326,7 +8335,12 @@
"repostspace": {
"regions": {
"aws": [
"ap-southeast-1",
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
"eu-west-1",
"us-east-1",
"us-west-2"
],
"aws-cn": [],
@@ -8777,6 +8791,7 @@
"ap-southeast-3",
"ap-southeast-4",
"ca-central-1",
"ca-west-1",
"eu-central-1",
"eu-central-2",
"eu-north-1",
@@ -10667,6 +10682,8 @@
"regions": {
"aws": [
"ap-northeast-1",
"ap-northeast-2",
"ap-south-1",
"ap-southeast-1",
"ap-southeast-2",
"ca-central-1",
@@ -185,14 +185,23 @@ def is_allowlisted_in_check(
# map lambda to awslambda
allowlisted_check = re.sub("^lambda", "awslambda", allowlisted_check)
check_match = (
"*" == allowlisted_check
or check == allowlisted_check
or re.search(allowlisted_check, check)
)
# Check if the finding is excepted
exceptions = allowlisted_check_info.get("Exceptions")
if is_excepted(
exceptions,
audited_account,
finding_region,
finding_resource,
finding_tags,
if (
is_excepted(
exceptions,
audited_account,
finding_region,
finding_resource,
finding_tags,
)
and check_match
):
# Break loop and return default value since is excepted
break
@@ -205,11 +214,7 @@ def is_allowlisted_in_check(
allowlisted_tags = "*"
# If there is a *, it affects to all checks
if (
"*" == allowlisted_check
or check == allowlisted_check
or re.search(allowlisted_check, check)
):
if check_match:
allowlisted_in_check = True
allowlisted_in_region = is_allowlisted_in_region(
allowlisted_regions, finding_region
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "ec2_securitygroup_allow_ingress_from_internet_to_all_ports",
"CheckTitle": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to all ports.",
"CheckType": [
"Infrastructure Security"
],
"ServiceName": "ec2",
"SubServiceName": "securitygroup",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "high",
"ResourceType": "AwsEc2SecurityGroup",
"Description": "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to all ports.",
"Risk": "If Security groups are not properly configured the attack surface is increased. An attacker could exploit this misconfiguration to gain unauthorized access to resources.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": "https://docs.prowler.com/checks/aws/networking-policies/ensure-aws-security-group-does-not-allow-all-traffic-on-all-ports/"
},
"Recommendation": {
"Text": "Use a Zero Trust approach. Narrow ingress traffic as much as possible. Consider north-south as well as east-west traffic.",
"Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html"
}
},
"Categories": [
"internet-exposed"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,29 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
from prowler.providers.aws.services.vpc.vpc_client import vpc_client
class ec2_securitygroup_allow_ingress_from_internet_to_all_ports(Check):
def execute(self):
findings = []
for security_group in ec2_client.security_groups:
# Check if ignoring flag is set and if the VPC and the SG is in use
if not ec2_client.audit_info.ignore_unused_services or (
security_group.vpc_id in vpc_client.vpcs
and vpc_client.vpcs[security_group.vpc_id].in_use
and len(security_group.network_interfaces) > 0
):
report = Check_Report_AWS(self.metadata())
report.region = security_group.region
report.status = "PASS"
report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have all ports open to the Internet."
report.resource_details = security_group.name
report.resource_id = security_group.id
report.resource_arn = security_group.arn
report.resource_tags = security_group.tags
if security_group.public_ports:
report.status = "FAIL"
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has all ports open to the Internet."
findings.append(report)
return findings
@@ -1,5 +1,6 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
from prowler.providers.aws.services.vpc.vpc_client import vpc_client
@@ -16,14 +17,19 @@ class ec2_securitygroup_allow_ingress_from_internet_to_any_port(Check):
report = Check_Report_AWS(self.metadata())
report.region = security_group.region
report.status = "PASS"
report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have all ports open to the Internet."
report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have any port open to the Internet."
report.resource_details = security_group.name
report.resource_id = security_group.id
report.resource_arn = security_group.arn
report.resource_tags = security_group.tags
if security_group.public_ports:
report.status = "FAIL"
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has all ports open to the Internet."
if not security_group.public_ports:
# 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", ports=None, any_address=True
):
report.status = "FAIL"
report.status_extended = f"Security group {security_group.name} ({security_group.id}) has at least one port open to the Internet."
findings.append(report)
return findings
@@ -128,7 +128,7 @@ class EC2(AWSService):
check_security_group(
ingress_rule, "-1", any_address=True
)
and "ec2_securitygroup_allow_ingress_from_internet_to_any_port"
and "ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
in self.audited_checks
):
all_public_ports = True
@@ -28,12 +28,14 @@ def check_security_group(
'ToPort': 123,
}
@param procotol: Protocol to check.
@param protocol: Protocol to check. If -1, all protocols will be checked.
@param ports: List of ports to check. (Default: [])
@param ports: List of ports to check. If empty, any port will be checked. If None, any port will be checked. (Default: [])
@param any_address: If True, only 0.0.0.0/0 or "::/0" will be public and do not search for public addresses. (Default: False)
@return: True if the security group has public access to the check_ports using the protocol
"""
# Check for all traffic ingress rules regardless of the protocol
if ingress_rule["IpProtocol"] == "-1":
@@ -70,9 +72,12 @@ def check_security_group(
and ingress_rule["IpProtocol"] == protocol
):
return True
# If no input ports check if all ports are open
# If empty input ports check if all ports are open
if len(set(ingress_port_range)) == 65536:
return True
# If None input ports check if any port is open
if ports is None:
return True
# IPv6
for ip_ingress_rule in ingress_rule["Ipv6Ranges"]:
@@ -85,9 +90,12 @@ def check_security_group(
and ingress_rule["IpProtocol"] == protocol
):
return True
# If no input ports check if all ports are open
# If empty input ports check if all ports are open
if len(set(ingress_port_range)) == 65536:
return True
# If None input ports check if any port is open
if ports is None:
return True
return False
@@ -22,7 +22,10 @@ class elasticache_cluster_uses_public_subnet(Check):
public_subnets = []
for subnet in cluster.subnets:
if vpc_client.vpc_subnets[subnet].public:
if (
subnet in vpc_client.vpc_subnets
and vpc_client.vpc_subnets[subnet].public
):
public_subnets.append(vpc_client.vpc_subnets[subnet].id)
if len(public_subnets) > 0:
@@ -20,6 +20,10 @@ class rds_instance_backup_enabled(Check):
f"RDS Instance {db_instance.id} does not have backup enabled."
)
if db_instance.replica_source and not rds_client.audit_config.get(
"check_rds_instance_replicas", False
):
continue
findings.append(report)
return findings
@@ -1,14 +1,14 @@
{
"Provider": "aws",
"CheckID": "rds_instance_transport_encrypted",
"CheckTitle": "Check if RDS instances client connections are encrypted (Microsoft SQL Server and PostgreSQL).",
"CheckTitle": "Check if RDS instances client connections are encrypted (Microsoft SQL Server, PostgreSQL, MySQL, MariaDB, Aurora PostgreSQL, and Aurora MySQL).",
"CheckType": [],
"ServiceName": "rds",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance",
"Severity": "high",
"ResourceType": "AwsRdsDbInstance",
"Description": "Check if RDS instances client connections are encrypted (Microsoft SQL Server and PostgreSQL).",
"Description": "For SQL Server, PostgreSQL and Aurora PostgreSQL databases, if the rds.force_ssl parameter value is set to 0, the Transport Encryption feature is not enabled. For MySQL, Aurora MySQL and MariaDB databases, if the require_secure_transport parameter value is set to OFF, the Transport Encryption feature is not enabled.",
"Risk": "If not enabled sensitive information at transit is not protected.",
"RelatedUrl": "https://aws.amazon.com/premiumsupport/knowledge-center/rds-connect-ssl-connection/",
"Remediation": {
@@ -19,11 +19,13 @@
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure that Microsoft SQL Server and PostgreSQL instances provisioned with Amazon RDS have Transport Encryption feature enabled in order to meet security and compliance requirements.",
"Text": "Ensure that instances provisioned with Amazon RDS have Transport Encryption feature enabled in order to meet security and compliance requirements.",
"Url": "https://aws.amazon.com/premiumsupport/knowledge-center/rds-connect-ssl-connection/"
}
},
"Categories": [ "encryption" ],
"Categories": [
"encryption"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
@@ -5,7 +5,17 @@ from prowler.providers.aws.services.rds.rds_client import rds_client
class rds_instance_transport_encrypted(Check):
def execute(self):
findings = []
supported_engines = ["sqlserver", "postgres"]
supported_engines = [
"sqlserver-se",
"sqlserver-ee",
"sqlserver-ex",
"sqlserver-web",
"postgres",
"aurora-postgresql",
"mysql",
"mariadb",
"aurora-mysql",
]
for db_instance in rds_client.db_instances:
report = Check_Report_AWS(self.metadata())
report.region = db_instance.region
@@ -16,19 +26,34 @@ class rds_instance_transport_encrypted(Check):
report.status_extended = (
f"RDS Instance {db_instance.id} connections are not encrypted."
)
# Check only RDS SQL Server or PostgreSQL engines (Aurora not supported)
if (
any(engine in db_instance.engine for engine in supported_engines)
and "aurora" not in db_instance.engine
):
for parameter in db_instance.parameters:
if (
parameter["ParameterName"] == "rds.force_ssl"
and parameter["ParameterValue"] == "1"
):
report.status = "PASS"
report.status_extended = f"RDS Instance {db_instance.id} connections use SSL encryption."
# Check only RDS DB instances that support parameter group encryption
if not db_instance.cluster_id and any(
engine in db_instance.engine for engine in supported_engines
):
if db_instance.engine in [
"sqlserver-se",
"sqlserver-ee",
"sqlserver-ex",
"sqlserver-web",
"postgres",
"aurora-postgresql",
]:
for parameter in db_instance.parameters:
if (
parameter["ParameterName"] == "rds.force_ssl"
and parameter["ParameterValue"] == "1"
):
report.status = "PASS"
report.status_extended = f"RDS Instance {db_instance.id} connections use SSL encryption."
else:
for parameter in db_instance.parameters:
if (
parameter["ParameterName"] == "require_secure_transport"
and parameter["ParameterValue"] == "1"
):
report.status = "PASS"
report.status_extended = f"RDS Instance {db_instance.id} connections use SSL encryption."
findings.append(report)
return findings
@@ -77,6 +77,9 @@ class RDS(AWSService):
cluster_arn=f"arn:{self.audited_partition}:rds:{regional_client.region}:{self.audited_account}:cluster:{instance.get('DBClusterIdentifier')}",
region=regional_client.region,
tags=instance.get("TagList", []),
replica_source=instance.get(
"ReadReplicaSourceDBInstanceIdentifier"
),
)
)
except Exception as error:
@@ -305,6 +308,7 @@ class DBInstance(BaseModel):
cluster_arn: Optional[str]
region: str
tags: Optional[list] = []
replica_source: Optional[str]
class DBCluster(BaseModel):
+13 -19
View File
@@ -1,38 +1,32 @@
from azure.identity import AzureAuthorityHosts
from msrestazure.azure_cloud import (
AZURE_CHINA_CLOUD,
AZURE_GERMAN_CLOUD,
AZURE_US_GOV_CLOUD,
)
AZURE_CHINA_CLOUD = "https://management.chinacloudapi.cn"
AZURE_US_GOV_CLOUD = "https://management.usgovcloudapi.net"
AZURE_GERMAN_CLOUD = "https://management.microsoftazure.de"
AZURE_GENERIC_CLOUD = "https://management.azure.com"
def get_regions_config(region):
allowed_regions = {
"AzureCloud": {
"authority": None,
"base_url": "https://management.azure.com",
"credential_scopes": ["https://management.azure.com/.default"],
"base_url": AZURE_GENERIC_CLOUD,
"credential_scopes": [AZURE_GENERIC_CLOUD + "/.default"],
},
"AzureChinaCloud": {
"authority": AzureAuthorityHosts.AZURE_CHINA,
"base_url": AZURE_CHINA_CLOUD.endpoints.resource_manager,
"credential_scopes": [
AZURE_CHINA_CLOUD.endpoints.resource_manager + "/.default"
],
"base_url": AZURE_CHINA_CLOUD,
"credential_scopes": [AZURE_CHINA_CLOUD + "/.default"],
},
"AzureUSGovernment": {
"authority": AzureAuthorityHosts.AZURE_GOVERNMENT,
"base_url": AZURE_US_GOV_CLOUD.endpoints.resource_manager,
"credential_scopes": [
AZURE_US_GOV_CLOUD.endpoints.resource_manager + "/.default"
],
"base_url": AZURE_US_GOV_CLOUD,
"credential_scopes": [AZURE_US_GOV_CLOUD + "/.default"],
},
"AzureGermanCloud": {
"authority": AzureAuthorityHosts.AZURE_GERMANY,
"base_url": AZURE_GERMAN_CLOUD.endpoints.resource_manager,
"credential_scopes": [
AZURE_GERMAN_CLOUD.endpoints.resource_manager + "/.default"
],
"base_url": AZURE_GERMAN_CLOUD,
"credential_scopes": [AZURE_GERMAN_CLOUD + "/.default"],
},
}
return allowed_regions[region]
+2 -1
View File
@@ -54,13 +54,14 @@ google-auth-httplib2 = ">=0.1,<0.3"
jsonschema = "4.22.0"
microsoft-kiota-abstractions = "1.3.2"
msgraph-sdk = "1.4.0"
msrestazure = "0.6.4"
pydantic = "1.10.15"
python = ">=3.9,<3.13"
pytz = "2024.1"
schema = "0.7.7"
shodan = "1.31.0"
slack-sdk = "3.27.2"
tabulate = "0.9.0"
tzlocal = "5.2"
[tool.poetry.group.dev.dependencies]
bandit = "1.7.8"
+1
View File
@@ -58,6 +58,7 @@ config_aws = {
],
"organizations_enabled_regions": [],
"organizations_trusted_delegated_administrators": [],
"check_rds_instance_replicas": False,
}
config_azure = {"shodan_api_key": None}
+4
View File
@@ -63,6 +63,10 @@ aws:
organizations_enabled_regions: []
organizations_trusted_delegated_administrators: []
# aws.rds_instance_backup_enabled
# Whether to check RDS instance replicas or not
check_rds_instance_replicas: False
# Azure Configuration
azure:
# Azure Network Configuration
+4
View File
@@ -60,3 +60,7 @@ obsolete_lambda_runtimes:
# ]
organizations_enabled_regions: []
organizations_trusted_delegated_administrators: []
# aws.rds_instance_backup_enabled
# Whether to check RDS instance replicas or not
check_rds_instance_replicas: False
@@ -447,6 +447,41 @@ class Test_Allowlist:
)
)
def test_is_allowlisted_exceptions_before_match(self):
# Allowlist example
allowlist = {
"Accounts": {
"*": {
"Checks": {
"accessanalyzer_enabled": {
"Exceptions": {
"Accounts": [],
"Regions": [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1],
"Resources": [],
"Tags": [],
},
"Regions": ["*"],
"Resources": ["*"],
"Tags": ["*"],
},
"sns_*": {
"Regions": ["*"],
"Resources": ["aws-controltower-*"],
},
}
}
}
}
assert is_allowlisted(
allowlist,
AWS_ACCOUNT_NUMBER,
"sns_topics_not_publicly_accessible",
AWS_REGION_EU_WEST_1,
"aws-controltower-AggregateSecurityNotifications",
"",
)
def test_is_allowlisted_all_and_single_account(self):
# Allowlist example
allowlist = {
@@ -0,0 +1,333 @@
from unittest import mock
from boto3 import client, resource
from moto import mock_aws
from tests.providers.aws.audit_info_utils import (
AWS_REGION_EU_WEST_1,
AWS_REGION_US_EAST_1,
set_mocked_aws_audit_info,
)
class Test_ec2_securitygroup_allow_ingress_from_internet_to_all_ports:
@mock_aws
def test_ec2_default_sgs(self):
# Create EC2 Mocked Resources
ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1)
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.vpc.vpc_service import VPC
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
],
)
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_client",
new=EC2(current_audit_info),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.vpc_client",
new=VPC(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import (
ec2_securitygroup_allow_ingress_from_internet_to_all_ports,
)
check = ec2_securitygroup_allow_ingress_from_internet_to_all_ports()
result = check.execute()
# One default sg per region
assert len(result) == 3
# All are compliant by default
assert result[0].status == "PASS"
assert result[1].status == "PASS"
assert result[2].status == "PASS"
@mock_aws
def test_ec2_non_compliant_default_sg(self):
# Create EC2 Mocked Resources
ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1)
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[
"SecurityGroups"
][0]
default_sg_id = default_sg["GroupId"]
default_sg_name = default_sg["GroupName"]
ec2_client.authorize_security_group_ingress(
GroupId=default_sg_id,
IpPermissions=[
{
"IpProtocol": "-1",
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
}
],
)
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.vpc.vpc_service import VPC
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
],
)
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_client",
new=EC2(current_audit_info),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.vpc_client",
new=VPC(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import (
ec2_securitygroup_allow_ingress_from_internet_to_all_ports,
)
check = ec2_securitygroup_allow_ingress_from_internet_to_all_ports()
result = check.execute()
# One default sg per region
assert len(result) == 3
# Search changed sg
for sg in result:
if sg.resource_id == default_sg_id:
assert sg.status == "FAIL"
assert sg.region == AWS_REGION_US_EAST_1
assert (
sg.status_extended
== f"Security group {default_sg_name} ({default_sg_id}) has all ports open to the Internet."
)
assert (
sg.resource_arn
== f"arn:{current_audit_info.audited_partition}:ec2:{AWS_REGION_US_EAST_1}:{current_audit_info.audited_account}:security-group/{default_sg_id}"
)
assert sg.resource_details == default_sg_name
assert sg.resource_tags == []
@mock_aws
def test_ec2_compliant_default_sg(self):
# Create EC2 Mocked Resources
ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1)
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[
"SecurityGroups"
][0]
default_sg_id = default_sg["GroupId"]
default_sg_name = default_sg["GroupName"]
ec2_client.authorize_security_group_ingress(
GroupId=default_sg_id,
IpPermissions=[
{
"IpProtocol": "-1",
"IpRanges": [{"CidrIp": "123.123.123.123/32"}],
}
],
)
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.vpc.vpc_service import VPC
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
],
)
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_client",
new=EC2(current_audit_info),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.vpc_client",
new=VPC(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import (
ec2_securitygroup_allow_ingress_from_internet_to_all_ports,
)
check = ec2_securitygroup_allow_ingress_from_internet_to_all_ports()
result = check.execute()
# One default sg per region
assert len(result) == 3
# Search changed sg
for sg in result:
if sg.resource_id == default_sg_id:
assert sg.status == "PASS"
assert sg.region == AWS_REGION_US_EAST_1
assert (
sg.status_extended
== f"Security group {default_sg_name} ({default_sg_id}) does not have all ports open to the Internet."
)
assert (
sg.resource_arn
== f"arn:{current_audit_info.audited_partition}:ec2:{AWS_REGION_US_EAST_1}:{current_audit_info.audited_account}:security-group/{default_sg_id}"
)
assert sg.resource_details == default_sg_name
assert sg.resource_tags == []
@mock_aws
def test_ec2_compliant_default_sg_only_open_to_one_port(self):
# Create EC2 Mocked Resources
ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1)
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[
"SecurityGroups"
][0]
default_sg_id = default_sg["GroupId"]
default_sg_name = default_sg["GroupName"]
ec2_client.authorize_security_group_ingress(
GroupId=default_sg_id,
IpPermissions=[
{
"FromPort": 80,
"IpProtocol": "tcp",
"IpRanges": [{"CidrIp": "0.0.0.0/0"}],
"Ipv6Ranges": [],
"PrefixListIds": [],
"ToPort": 80,
"UserIdGroupPairs": [],
}
],
)
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.vpc.vpc_service import VPC
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
],
)
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_client",
new=EC2(current_audit_info),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.vpc_client",
new=VPC(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import (
ec2_securitygroup_allow_ingress_from_internet_to_all_ports,
)
check = ec2_securitygroup_allow_ingress_from_internet_to_all_ports()
result = check.execute()
# One default sg per region
assert len(result) == 3
# Search changed sg
for sg in result:
if sg.resource_id == default_sg_id:
assert sg.status == "PASS"
assert sg.region == AWS_REGION_US_EAST_1
assert (
sg.status_extended
== f"Security group {default_sg_name} ({default_sg_id}) does not have all ports open to the Internet."
)
assert (
sg.resource_arn
== f"arn:{current_audit_info.audited_partition}:ec2:{AWS_REGION_US_EAST_1}:{current_audit_info.audited_account}:security-group/{default_sg_id}"
)
assert sg.resource_details == default_sg_name
assert sg.resource_tags == []
@mock_aws
def test_ec2_default_sgs_ignoring(self):
# Create EC2 Mocked Resources
ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1)
ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.vpc.vpc_service import VPC
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
ignore_unused_services=True,
)
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_client",
new=EC2(current_audit_info),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.vpc_client",
new=VPC(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import (
ec2_securitygroup_allow_ingress_from_internet_to_all_ports,
)
check = ec2_securitygroup_allow_ingress_from_internet_to_all_ports()
result = check.execute()
assert len(result) == 0
@mock_aws
def test_ec2_default_sgs_ignoring_vpc_in_use(self):
# Create EC2 Mocked Resources
ec2 = resource("ec2", region_name=AWS_REGION_US_EAST_1)
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
ec2.create_network_interface(SubnetId=subnet.id)
ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1)
default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[
"SecurityGroups"
][0]
default_sg["GroupId"]
default_sg["GroupName"]
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.vpc.vpc_service import VPC
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
ignore_unused_services=True,
)
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_client",
new=EC2(current_audit_info),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.vpc_client",
new=VPC(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import (
ec2_securitygroup_allow_ingress_from_internet_to_all_ports,
)
check = ec2_securitygroup_allow_ingress_from_internet_to_all_ports()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].region == AWS_REGION_US_EAST_1
@@ -22,9 +22,6 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
],
)
with mock.patch(
@@ -76,9 +73,6 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
],
)
with mock.patch(
@@ -108,7 +102,7 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
assert sg.region == AWS_REGION_US_EAST_1
assert (
sg.status_extended
== f"Security group {default_sg_name} ({default_sg_id}) has all ports open to the Internet."
== f"Security group {default_sg_name} ({default_sg_id}) has at least one port open to the Internet."
)
assert (
sg.resource_arn
@@ -141,9 +135,6 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
],
)
with mock.patch(
@@ -173,7 +164,7 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
assert sg.region == AWS_REGION_US_EAST_1
assert (
sg.status_extended
== f"Security group {default_sg_name} ({default_sg_id}) does not have all ports open to the Internet."
== f"Security group {default_sg_name} ({default_sg_id}) does not have any port open to the Internet."
)
assert (
sg.resource_arn
@@ -211,9 +202,6 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
],
)
with mock.patch(
@@ -239,11 +227,11 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
# Search changed sg
for sg in result:
if sg.resource_id == default_sg_id:
assert sg.status == "PASS"
assert sg.status == "FAIL"
assert sg.region == AWS_REGION_US_EAST_1
assert (
sg.status_extended
== f"Security group {default_sg_name} ({default_sg_id}) does not have all ports open to the Internet."
== f"Security group {default_sg_name} ({default_sg_id}) has at least one port open to the Internet."
)
assert (
sg.resource_arn
@@ -262,9 +250,6 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
],
ignore_unused_services=True,
)
@@ -303,10 +288,52 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_any_port:
default_sg["GroupName"]
from prowler.providers.aws.services.ec2.ec2_service import EC2
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
ignore_unused_services=True,
)
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_client",
new=EC2(current_audit_info),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_securitygroup_allow_ingress_from_internet_to_any_port.vpc_client",
new=VPC(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_any_port.ec2_securitygroup_allow_ingress_from_internet_to_any_port import (
ec2_securitygroup_allow_ingress_from_internet_to_any_port,
)
check = ec2_securitygroup_allow_ingress_from_internet_to_any_port()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].region == AWS_REGION_US_EAST_1
@mock_aws
def test_ec2_default_sgs_with_all_ports_check(self):
# Create EC2 Mocked Resources
ec2 = resource("ec2", region_name=AWS_REGION_US_EAST_1)
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
ec2.create_network_interface(SubnetId=subnet.id)
ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1)
default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[
"SecurityGroups"
][0]
default_sg["GroupId"]
default_sg["GroupName"]
from prowler.providers.aws.services.ec2.ec2_service import EC2
current_audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
],
ignore_unused_services=True,
)
@@ -139,7 +139,7 @@ class Test_EC2_Service:
audit_info = set_mocked_aws_audit_info(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
expected_checks=[
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
],
)
ec2 = EC2(audit_info)
@@ -338,7 +338,9 @@ class Test_check_security_group:
ingress_rule = self.ingress_rule_generator(
0, 65535, TRANSPORT_PROTOCOL_TCP, [IP_V4_ALL_CIDRS], []
)
assert check_security_group(ingress_rule, TRANSPORT_PROTOCOL_TCP, None, True)
assert check_security_group(
ingress_rule, TRANSPORT_PROTOCOL_TCP, any_address=True
)
# TCP Protocol - IP_V6_ALL_CIDRS - All Ports - check None - Any Address - Open
def test_all_public_ipv6_address_open_all_ports_check_all_tcp_any_address(
@@ -347,4 +349,44 @@ class Test_check_security_group:
ingress_rule = self.ingress_rule_generator(
0, 65535, TRANSPORT_PROTOCOL_TCP, [], [IP_V6_ALL_CIDRS]
)
assert check_security_group(ingress_rule, TRANSPORT_PROTOCOL_TCP, None, True)
assert check_security_group(
ingress_rule, TRANSPORT_PROTOCOL_TCP, any_address=True
)
# ALL (-1) Protocol - IP_V4_ALL_CIDRS - Any Port - check None - Any Address - Open
def test_all_public_ipv4_address_open_any_port_check_none_any_address(
self,
):
ingress_rule = self.ingress_rule_generator(
0, 65535, TRANSPORT_PROTOCOL_ALL, [IP_V4_ALL_CIDRS], []
)
assert check_security_group(ingress_rule, TRANSPORT_PROTOCOL_ALL, None, True)
# ALL (-1) Protocol - IP_V6_ALL_CIDRS - Any Port - check None - Any Address - Open
def test_all_public_ipv6_address_open_any_port_check_none_any_address(
self,
):
ingress_rule = self.ingress_rule_generator(
0, 65535, TRANSPORT_PROTOCOL_ALL, [], [IP_V6_ALL_CIDRS]
)
assert check_security_group(ingress_rule, TRANSPORT_PROTOCOL_ALL, None, True)
# ALL (-1) Protocol - IP_V4_ALL_CIDRS - Any Port - check None - Any Address - Open
def test_all_public_ipv4_address_open_22_port_check_none_any_address(
self,
):
port = 22
ingress_rule = self.ingress_rule_generator(
port, port, TRANSPORT_PROTOCOL_ALL, [IP_V4_ALL_CIDRS], []
)
assert check_security_group(ingress_rule, TRANSPORT_PROTOCOL_ALL, None, True)
# ALL (-1) Protocol - IP_V6_ALL_CIDRS - Any Port - check None - Any Address - Open
def test_all_public_ipv6_address_open_22_port_check_none_any_address(
self,
):
port = 22
ingress_rule = self.ingress_rule_generator(
port, port, TRANSPORT_PROTOCOL_ALL, [], [IP_V6_ALL_CIDRS]
)
assert check_security_group(ingress_rule, TRANSPORT_PROTOCOL_ALL, None, True)
@@ -1,4 +1,3 @@
from re import search
from unittest import mock
import botocore
@@ -58,8 +57,9 @@ class Test_rds_instance_backup_enabled:
@mock_aws
def test_rds_instance_no_backup(self):
conn = client("rds", region_name=AWS_REGION_US_EAST_1)
instance_id = "db-master-1"
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
DBInstanceIdentifier=instance_id,
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
@@ -90,9 +90,9 @@ class Test_rds_instance_backup_enabled:
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"does not have backup enabled",
result[0].status_extended,
assert (
result[0].status_extended
== f"RDS Instance {instance_id} does not have backup enabled."
)
assert result[0].resource_id == "db-master-1"
assert result[0].region == AWS_REGION_US_EAST_1
@@ -105,13 +105,15 @@ class Test_rds_instance_backup_enabled:
@mock_aws
def test_rds_instance_with_backup(self):
conn = client("rds", region_name=AWS_REGION_US_EAST_1)
instance_id = "db-master-1"
retention_period = 10
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
DBInstanceIdentifier=instance_id,
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
BackupRetentionPeriod=10,
BackupRetentionPeriod=retention_period,
)
from prowler.providers.aws.services.rds.rds_service import RDS
@@ -135,9 +137,9 @@ class Test_rds_instance_backup_enabled:
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has backup enabled",
result[0].status_extended,
assert (
result[0].status_extended
== f"RDS Instance {instance_id} has backup enabled with retention period {retention_period} days."
)
assert result[0].resource_id == "db-master-1"
assert result[0].region == AWS_REGION_US_EAST_1
@@ -146,3 +148,133 @@ class Test_rds_instance_backup_enabled:
== f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-master-1"
)
assert result[0].resource_tags == []
@mock_aws
def test_rds_instance_replica_with_backup_checking_replicas(self):
conn = client("rds", region_name=AWS_REGION_US_EAST_1)
instance_id = "db-master-1"
retention_period = 10
conn.create_db_instance(
DBInstanceIdentifier=instance_id,
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
BackupRetentionPeriod=retention_period,
)
replica_id = "db-replica-1"
conn.create_db_instance_read_replica(
DBInstanceIdentifier=replica_id,
SourceDBInstanceIdentifier="db-master-1",
DBInstanceClass="db.m1.small",
)
from prowler.providers.aws.services.rds.rds_service import RDS
audit_info = set_mocked_aws_audit_info(
[AWS_REGION_US_EAST_1], audit_config={"check_rds_instance_replicas": True}
)
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled.rds_client",
new=RDS(audit_info),
):
# Test Check
from prowler.providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled import (
rds_instance_backup_enabled,
)
check = rds_instance_backup_enabled()
result = check.execute()
assert len(result) == 2
for finding in result:
if finding.resource_id == instance_id:
assert finding.status == "PASS"
assert (
finding.status_extended
== f"RDS Instance {instance_id} has backup enabled with retention period {retention_period} days."
)
assert finding.resource_id == instance_id
assert finding.region == AWS_REGION_US_EAST_1
assert (
finding.resource_arn
== f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:{instance_id}"
)
assert finding.resource_tags == []
if finding.resource_id == replica_id:
assert finding.status == "PASS"
assert (
finding.status_extended
== f"RDS Instance {replica_id} has backup enabled with retention period {retention_period} days."
)
assert finding.resource_id == replica_id
assert finding.region == AWS_REGION_US_EAST_1
assert (
finding.resource_arn
== f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:{replica_id}"
)
assert finding.resource_tags == []
@mock_aws
def test_rds_instance_replica_with_backup_default_config(self):
conn = client("rds", region_name=AWS_REGION_US_EAST_1)
instance_id = "db-master-1"
retention_period = 10
conn.create_db_instance(
DBInstanceIdentifier=instance_id,
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
BackupRetentionPeriod=retention_period,
)
replica_id = "db-replica-1"
conn.create_db_instance_read_replica(
DBInstanceIdentifier=replica_id,
SourceDBInstanceIdentifier=instance_id,
DBInstanceClass="db.m1.small",
)
from prowler.providers.aws.services.rds.rds_service import RDS
audit_info = set_mocked_aws_audit_info(
[AWS_REGION_US_EAST_1],
)
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled.rds_client",
new=RDS(audit_info),
):
# Test Check
from prowler.providers.aws.services.rds.rds_instance_backup_enabled.rds_instance_backup_enabled import (
rds_instance_backup_enabled,
)
check = rds_instance_backup_enabled()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"RDS Instance {instance_id} has backup enabled with retention period {retention_period} days."
)
assert result[0].resource_id == instance_id
assert result[0].region == AWS_REGION_US_EAST_1
assert (
result[0].resource_arn
== f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:{instance_id}"
)
assert result[0].resource_tags == []
@@ -1,4 +1,3 @@
from re import search
from unittest import mock
import botocore
@@ -75,6 +74,57 @@ class Test_rds_instance_transport_encrypted:
audit_info = set_mocked_aws_audit_info([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.rds.rds_instance_transport_encrypted.rds_instance_transport_encrypted.rds_client",
new=RDS(audit_info),
):
# Test Check
from prowler.providers.aws.services.rds.rds_instance_transport_encrypted.rds_instance_transport_encrypted import (
rds_instance_transport_encrypted,
)
check = rds_instance_transport_encrypted()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "RDS Instance db-master-1 connections are not encrypted."
)
assert result[0].resource_id == "db-master-1"
assert result[0].region == AWS_REGION_US_EAST_1
assert (
result[0].resource_arn
== f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-master-1"
)
assert result[0].resource_tags == []
@mock_aws
def test_rds_clustered_instance(self):
conn = client("rds", region_name=AWS_REGION_US_EAST_1)
conn.create_db_parameter_group(
DBParameterGroupName="test",
DBParameterGroupFamily="default.aurora-postgresql14",
Description="test parameter group",
)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="aurora-postgresql",
DBName="aurora-postgres",
DBInstanceClass="db.m1.small",
DBParameterGroupName="test",
DBClusterIdentifier="cluster-postgres",
)
from prowler.providers.aws.services.rds.rds_service import RDS
audit_info = set_mocked_aws_audit_info([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
@@ -94,7 +144,7 @@ class Test_rds_instance_transport_encrypted:
assert len(result) == 0
@mock_aws
def test_rds_instance_no_ssl(self):
def test_postgres_rds_instance_no_ssl(self):
conn = client("rds", region_name=AWS_REGION_US_EAST_1)
conn.create_db_parameter_group(
DBParameterGroupName="test",
@@ -143,9 +193,9 @@ class Test_rds_instance_transport_encrypted:
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"connections are not encrypted",
result[0].status_extended,
assert (
result[0].status_extended
== "RDS Instance db-master-1 connections are not encrypted."
)
assert result[0].resource_id == "db-master-1"
assert result[0].region == AWS_REGION_US_EAST_1
@@ -156,7 +206,131 @@ class Test_rds_instance_transport_encrypted:
assert result[0].resource_tags == []
@mock_aws
def test_rds_instance_with_ssl(self):
def test_mysql_rds_instance_no_ssl(self):
conn = client("rds", region_name=AWS_REGION_US_EAST_1)
conn.create_db_parameter_group(
DBParameterGroupName="test",
DBParameterGroupFamily="default.mysql8.0",
Description="test parameter group",
)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="mysql",
DBName="staging-mysql",
DBInstanceClass="db.m1.small",
DBParameterGroupName="test",
)
conn.modify_db_parameter_group(
DBParameterGroupName="test",
Parameters=[
{
"ParameterName": "require_secure_transport",
"ParameterValue": "0",
"ApplyMethod": "immediate",
},
],
)
from prowler.providers.aws.services.rds.rds_service import RDS
audit_info = set_mocked_aws_audit_info([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.rds.rds_instance_transport_encrypted.rds_instance_transport_encrypted.rds_client",
new=RDS(audit_info),
):
# Test Check
from prowler.providers.aws.services.rds.rds_instance_transport_encrypted.rds_instance_transport_encrypted import (
rds_instance_transport_encrypted,
)
check = rds_instance_transport_encrypted()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "RDS Instance db-master-1 connections are not encrypted."
)
assert result[0].resource_id == "db-master-1"
assert result[0].region == AWS_REGION_US_EAST_1
assert (
result[0].resource_arn
== f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-master-1"
)
assert result[0].resource_tags == []
@mock_aws
def test_mysql_rds_instance_with_ssl(self):
conn = client("rds", region_name=AWS_REGION_US_EAST_1)
conn.create_db_parameter_group(
DBParameterGroupName="test",
DBParameterGroupFamily="default.mysql8.0",
Description="test parameter group",
)
conn.create_db_instance(
DBInstanceIdentifier="db-master-1",
AllocatedStorage=10,
Engine="mysql",
DBName="staging-mysql",
DBInstanceClass="db.m1.small",
DBParameterGroupName="test",
)
conn.modify_db_parameter_group(
DBParameterGroupName="test",
Parameters=[
{
"ParameterName": "require_secure_transport",
"ParameterValue": "1",
"ApplyMethod": "immediate",
},
],
)
from prowler.providers.aws.services.rds.rds_service import RDS
audit_info = set_mocked_aws_audit_info([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.rds.rds_instance_transport_encrypted.rds_instance_transport_encrypted.rds_client",
new=RDS(audit_info),
):
# Test Check
from prowler.providers.aws.services.rds.rds_instance_transport_encrypted.rds_instance_transport_encrypted import (
rds_instance_transport_encrypted,
)
check = rds_instance_transport_encrypted()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "RDS Instance db-master-1 connections use SSL encryption."
)
assert result[0].resource_id == "db-master-1"
assert result[0].region == AWS_REGION_US_EAST_1
assert (
result[0].resource_arn
== f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-master-1"
)
assert result[0].resource_tags == []
@mock_aws
def test_postgres_rds_instance_with_ssl(self):
conn = client("rds", region_name=AWS_REGION_US_EAST_1)
conn.create_db_parameter_group(
DBParameterGroupName="test",
@@ -205,9 +379,9 @@ class Test_rds_instance_transport_encrypted:
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"connections use SSL encryption",
result[0].status_extended,
assert (
result[0].status_extended
== "RDS Instance db-master-1 connections use SSL encryption."
)
assert result[0].resource_id == "db-master-1"
assert result[0].region == AWS_REGION_US_EAST_1
@@ -88,6 +88,7 @@ class Test_RDS_Service:
EnableCloudwatchLogsExports=["audit", "error"],
MultiAZ=True,
DBParameterGroupName="test",
DBClusterIdentifier="cluster-postgres",
Tags=[
{"Key": "test", "Value": "test"},
],
@@ -110,6 +111,7 @@ class Test_RDS_Service:
assert rds.db_instances[0].deletion_protection
assert rds.db_instances[0].auto_minor_version_upgrade
assert rds.db_instances[0].multi_az
assert rds.db_instances[0].cluster_id
assert rds.db_instances[0].tags == [
{"Key": "test", "Value": "test"},
]
@@ -1,12 +1,13 @@
from azure.identity import AzureAuthorityHosts
from msrestazure.azure_cloud import (
from prowler.providers.azure.lib.regions.regions import (
AZURE_CHINA_CLOUD,
AZURE_GENERIC_CLOUD,
AZURE_GERMAN_CLOUD,
AZURE_US_GOV_CLOUD,
get_regions_config,
)
from prowler.providers.azure.lib.regions.regions import get_regions_config
class Test_azure_regions:
def test_get_regions_config(self):
@@ -19,29 +20,23 @@ class Test_azure_regions:
expected_output = {
"AzureCloud": {
"authority": None,
"base_url": "https://management.azure.com",
"credential_scopes": ["https://management.azure.com/.default"],
"base_url": AZURE_GENERIC_CLOUD,
"credential_scopes": [AZURE_GENERIC_CLOUD + "/.default"],
},
"AzureChinaCloud": {
"authority": AzureAuthorityHosts.AZURE_CHINA,
"base_url": AZURE_CHINA_CLOUD.endpoints.resource_manager,
"credential_scopes": [
AZURE_CHINA_CLOUD.endpoints.resource_manager + "/.default"
],
"base_url": AZURE_CHINA_CLOUD,
"credential_scopes": [AZURE_CHINA_CLOUD + "/.default"],
},
"AzureUSGovernment": {
"authority": AzureAuthorityHosts.AZURE_GOVERNMENT,
"base_url": AZURE_US_GOV_CLOUD.endpoints.resource_manager,
"credential_scopes": [
AZURE_US_GOV_CLOUD.endpoints.resource_manager + "/.default"
],
"base_url": AZURE_US_GOV_CLOUD,
"credential_scopes": [AZURE_US_GOV_CLOUD + "/.default"],
},
"AzureGermanCloud": {
"authority": AzureAuthorityHosts.AZURE_GERMANY,
"base_url": AZURE_GERMAN_CLOUD.endpoints.resource_manager,
"credential_scopes": [
AZURE_GERMAN_CLOUD.endpoints.resource_manager + "/.default"
],
"base_url": AZURE_GERMAN_CLOUD,
"credential_scopes": [AZURE_GERMAN_CLOUD + "/.default"],
},
}