From 6601b4231d83ead704fbcf5089da55c3eb6e822a Mon Sep 17 00:00:00 2001 From: madereddy <49539048+madereddy@users.noreply.github.com> Date: Fri, 10 May 2024 04:23:54 -0400 Subject: [PATCH] chore(rds): support more AWS RDS DB Instance engines in encryption check (#3968) --- ...instance_transport_encrypted.metadata.json | 10 +- .../rds_instance_transport_encrypted.py | 51 +++-- .../rds_instance_transport_encrypted_test.py | 192 +++++++++++++++++- .../aws/services/rds/rds_service_test.py | 2 + 4 files changed, 229 insertions(+), 26 deletions(-) diff --git a/prowler/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted.metadata.json b/prowler/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted.metadata.json index 6553f29743..7992aece90 100644 --- a/prowler/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted.metadata.json +++ b/prowler/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted.metadata.json @@ -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": "" diff --git a/prowler/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted.py b/prowler/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted.py index 647ba7ae5a..d8ea85169d 100644 --- a/prowler/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted.py +++ b/prowler/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted.py @@ -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 diff --git a/tests/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted_test.py b/tests/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted_test.py index ceeb5f4fad..e4cd0b884d 100644 --- a/tests/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted_test.py +++ b/tests/providers/aws/services/rds/rds_instance_transport_encrypted/rds_instance_transport_encrypted_test.py @@ -1,4 +1,3 @@ -from re import search from unittest import mock import botocore @@ -75,6 +74,57 @@ class Test_rds_instance_transport_encrypted: aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_instance_transport_encrypted.rds_instance_transport_encrypted.rds_client", + new=RDS(aws_provider), + ): + # 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 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + with mock.patch( "prowler.providers.common.common.get_global_provider", return_value=aws_provider, @@ -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 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_instance_transport_encrypted.rds_instance_transport_encrypted.rds_client", + new=RDS(aws_provider), + ): + # 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 + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_instance_transport_encrypted.rds_instance_transport_encrypted.rds_client", + new=RDS(aws_provider), + ): + # 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 diff --git a/tests/providers/aws/services/rds/rds_service_test.py b/tests/providers/aws/services/rds/rds_service_test.py index 0bd3618167..bee0102e74 100644 --- a/tests/providers/aws/services/rds/rds_service_test.py +++ b/tests/providers/aws/services/rds/rds_service_test.py @@ -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"}, ]