mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
chore(backport): include latest changes of v4 in v3 (#4027)
This commit is contained in:
@@ -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
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",
|
||||
|
||||
@@ -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"
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2157,7 +2157,7 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"ec2_securitygroup_allow_ingress_from_internet_to_any_port"
|
||||
"ec2_securitygroup_allow_ingress_from_internet_to_all_ports"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+34
@@ -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": ""
|
||||
}
|
||||
+29
@@ -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
|
||||
+10
-4
@@ -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
|
||||
|
||||
|
||||
+4
-1
@@ -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:
|
||||
|
||||
+4
@@ -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
|
||||
|
||||
+6
-4
@@ -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": ""
|
||||
|
||||
+38
-13
@@ -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):
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -58,6 +58,7 @@ config_aws = {
|
||||
],
|
||||
"organizations_enabled_regions": [],
|
||||
"organizations_trusted_delegated_administrators": [],
|
||||
"check_rds_instance_replicas": False,
|
||||
}
|
||||
|
||||
config_azure = {"shodan_api_key": None}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
+333
@@ -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
|
||||
+47
-20
@@ -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)
|
||||
|
||||
+142
-10
@@ -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 == []
|
||||
|
||||
+183
-9
@@ -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"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user