From a2144ad353fbf7edc6dec0f0ce0c51fa75ce36af Mon Sep 17 00:00:00 2001 From: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:33:25 +0200 Subject: [PATCH] chore(rds): Revert changes on inherited instance checks (#4827) --- .../rds_cluster_default_admin.metadata.json | 2 +- ...r_iam_authentication_enabled.metadata.json | 2 +- .../rds_instance_default_admin.metadata.json | 2 +- .../rds_instance_default_admin.py | 35 +++-- ...e_iam_authentication_enabled.metadata.json | 2 +- ...rds_instance_iam_authentication_enabled.py | 26 ++-- .../rds_instance_default_admin_test.py | 127 +++++++++++++++--- ...nstance_iam_authentication_enabled_test.py | 82 +++++++---- 8 files changed, 207 insertions(+), 71 deletions(-) diff --git a/prowler/providers/aws/services/rds/rds_cluster_default_admin/rds_cluster_default_admin.metadata.json b/prowler/providers/aws/services/rds/rds_cluster_default_admin/rds_cluster_default_admin.metadata.json index 547972d409..27b3647eda 100644 --- a/prowler/providers/aws/services/rds/rds_cluster_default_admin/rds_cluster_default_admin.metadata.json +++ b/prowler/providers/aws/services/rds/rds_cluster_default_admin/rds_cluster_default_admin.metadata.json @@ -8,7 +8,7 @@ "ResourceIdTemplate": "arn:aws:rds:region:account-id:db-cluster", "Severity": "medium", "ResourceType": "AwsRdsDbCluster", - "Description": "Ensure that your Amazon RDS clusters are not using the default master username. Instances that belongs to a cluster are covered too by this check.", + "Description": "Ensure that your Amazon RDS clusters are not using the default master username.", "Risk": "Since admin is the Amazon's example for the RDS database master username and postgres is the default PostgreSQL master username. Many AWS customers will use this username for their RDS database instances in production. Malicious users can use this information to their advantage and frequently try to use default master username during brute-force attacks.", "RelatedUrl": "https://docs.aws.amazon.com/securityhub/latest/userguide/rds-controls.html#rds-24", "Remediation": { diff --git a/prowler/providers/aws/services/rds/rds_cluster_iam_authentication_enabled/rds_cluster_iam_authentication_enabled.metadata.json b/prowler/providers/aws/services/rds/rds_cluster_iam_authentication_enabled/rds_cluster_iam_authentication_enabled.metadata.json index 4223f037b9..08c0463fe4 100644 --- a/prowler/providers/aws/services/rds/rds_cluster_iam_authentication_enabled/rds_cluster_iam_authentication_enabled.metadata.json +++ b/prowler/providers/aws/services/rds/rds_cluster_iam_authentication_enabled/rds_cluster_iam_authentication_enabled.metadata.json @@ -8,7 +8,7 @@ "ResourceIdTemplate": "arn:aws:rds:region:account-id:db-cluster", "Severity": "medium", "ResourceType": "AwsRdsDbCluster", - "Description": "Check if RDS clusters have IAM authentication enabled. Instances that belongs to a cluster are covered too by this check.", + "Description": "Check if RDS clusters have IAM authentication enabled.", "Risk": "Ensure that the IAM Database Authentication feature is enabled for your RDS database clusters in order to use the Identity and Access Management (IAM) service to manage database access to your MySQL and PostgreSQL database clusters. With this feature enabled, you don't have to use a password when you connect to your MySQL/PostgreSQL database, instead you can use an authentication token. An authentication token is a unique string of characters with a lifetime of 15 minutes that Amazon RDS generates on your request. IAM Database Authentication removes the need of storing user credentials within the database configuration, because authentication is managed externally using Amazon IAM.", "RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Enabling.html", "Remediation": { diff --git a/prowler/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin.metadata.json b/prowler/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin.metadata.json index 84a3f73f5b..357db2146c 100644 --- a/prowler/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin.metadata.json +++ b/prowler/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin.metadata.json @@ -8,7 +8,7 @@ "ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance", "Severity": "medium", "ResourceType": "AwsRdsDbInstance", - "Description": "Ensure that your Amazon RDS instances that are not clustered are not using the default master username, the clustered ones are covered by the rds_cluster_default_admin check.", + "Description": "Ensure that your Amazon RDS instances are not using the default master username.", "Risk": "Since admin is the Amazon's example for the RDS database master username and postgres is the default PostgreSQL master username. Many AWS customers will use this username for their RDS database instances in production. Malicious users can use this information to their advantage and frequently try to use default master username during brute-force attacks.", "RelatedUrl": "https://docs.aws.amazon.com/securityhub/latest/userguide/rds-controls.html#rds-25", "Remediation": { diff --git a/prowler/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin.py b/prowler/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin.py index b72b50a4d9..bb0e4ab605 100644 --- a/prowler/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin.py +++ b/prowler/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin.py @@ -6,20 +6,31 @@ class rds_instance_default_admin(Check): def execute(self): findings = [] for db_instance in rds_client.db_instances: - # Check only RDS DB instances that are not clustered - if not db_instance.cluster_id: - report = Check_Report_AWS(self.metadata()) - report.region = db_instance.region - report.resource_id = db_instance.id - report.resource_arn = db_instance.arn - report.resource_tags = db_instance.tags - report.status = "FAIL" - report.status_extended = f"RDS Instance {db_instance.id} which is not clustered is using the default master username." - + report = Check_Report_AWS(self.metadata()) + report.region = db_instance.region + report.resource_id = db_instance.id + report.resource_arn = db_instance.arn + report.resource_tags = db_instance.tags + # Check if is member of a cluster + if db_instance.cluster_id: + if ( + db_instance.cluster_arn in rds_client.db_clusters + and rds_client.db_clusters[db_instance.cluster_arn].username + not in ["admin", "postgres"] + ): + report.status = "PASS" + report.status_extended = f"RDS Instance {db_instance.id} is not using the default master username at cluster {db_instance.cluster_id} level." + else: + report.status = "FAIL" + report.status_extended = f"RDS Instance {db_instance.id} is using the default master username at cluster {db_instance.cluster_id} level." + else: if db_instance.username not in ["admin", "postgres"]: report.status = "PASS" - report.status_extended = f"RDS Instance {db_instance.id} which is not clustered is not using the default master username." + report.status_extended = f"RDS Instance {db_instance.id} is not using the default master username." + else: + report.status = "FAIL" + report.status_extended = f"RDS Instance {db_instance.id} is using the default master username." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled.metadata.json b/prowler/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled.metadata.json index e5c0e66770..82238bdd23 100644 --- a/prowler/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled.metadata.json +++ b/prowler/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled.metadata.json @@ -8,7 +8,7 @@ "ResourceIdTemplate": "arn:aws:rds:region:account-id:db-instance", "Severity": "medium", "ResourceType": "AwsRdsDbInstance", - "Description": "Check if RDS instances that are not clustered have IAM authentication enabled, the clustered ones are covered by the rds_cluster_iam_authentication check.", + "Description": "Check if RDS instances have IAM authentication enabled.", "Risk": "Ensure that the IAM Database Authentication feature is enabled for your RDS database instances in order to use the Identity and Access Management (IAM) service to manage database access to your MySQL and PostgreSQL database instances. With this feature enabled, you don't have to use a password when you connect to your MySQL/PostgreSQL database, instead you can use an authentication token. An authentication token is a unique string of characters with a lifetime of 15 minutes that Amazon RDS generates on your request. IAM Database Authentication removes the need of storing user credentials within the database configuration, because authentication is managed externally using Amazon IAM.", "RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Enabling.html", "Remediation": { diff --git a/prowler/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled.py b/prowler/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled.py index 731c820330..6e937b767b 100644 --- a/prowler/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled.py +++ b/prowler/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled.py @@ -14,22 +14,28 @@ class rds_instance_iam_authentication_enabled(Check): ] findings = [] for db_instance in rds_client.db_instances: - # Check DB Instance to make sure its not part of a cluster. - if not db_instance.cluster_id and any( - engine in db_instance.engine for engine in supported_engines - ): + if any(engine in db_instance.engine for engine in supported_engines): report = Check_Report_AWS(self.metadata()) report.region = db_instance.region report.resource_id = db_instance.id report.resource_arn = db_instance.arn report.resource_tags = db_instance.tags - - if db_instance.iam_auth: - report.status = "PASS" - report.status_extended = f"RDS Instance {db_instance.id} which is not clustered has IAM authentication enabled." + # Check if is member of a cluster + if db_instance.cluster_id: + if db_instance.iam_auth: + report.status = "PASS" + report.status_extended = f"RDS Instance {db_instance.id} has IAM authentication enabled at cluster {db_instance.cluster_id} level." + else: + report.status = "FAIL" + report.status_extended = f"RDS Instance {db_instance.id} does not have IAM authentication enabled at cluster {db_instance.cluster_id} level." else: - report.status = "FAIL" - report.status_extended = f"RDS Instance {db_instance.id} which is not clustered does not have IAM authentication enabled." + if db_instance.iam_auth: + report.status = "PASS" + report.status_extended = f"RDS Instance {db_instance.id} has IAM authentication enabled." + else: + report.status = "FAIL" + report.status_extended = f"RDS Instance {db_instance.id} does not have IAM authentication enabled." findings.append(report) + return findings diff --git a/tests/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin_test.py b/tests/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin_test.py index f193fb1ea0..eb110680bc 100644 --- a/tests/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin_test.py +++ b/tests/providers/aws/services/rds/rds_instance_default_admin/rds_instance_default_admin_test.py @@ -1,6 +1,5 @@ from unittest import mock -import botocore from boto3 import client from moto import mock_aws @@ -10,25 +9,7 @@ from tests.providers.aws.utils import ( set_mocked_aws_provider, ) -make_api_call = botocore.client.BaseClient._make_api_call - -def mock_make_api_call(self, operation_name, kwarg): - if operation_name == "DescribeDBEngineVersions": - return { - "DBEngineVersions": [ - { - "Engine": "mysql", - "EngineVersion": "8.0.32", - "DBEngineDescription": "description", - "DBEngineVersionDescription": "description", - }, - ] - } - return make_api_call(self, operation_name, kwarg) - - -@mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) class Test_rds_instance_default_admin: @mock_aws def test_rds_no_instances(self): @@ -95,7 +76,7 @@ class Test_rds_instance_default_admin: assert result[0].status == "FAIL" assert ( result[0].status_extended - == "RDS Instance db-master-1 which is not clustered is using the default master username." + == "RDS Instance db-master-1 is using the default master username." ) assert result[0].resource_id == "db-master-1" assert result[0].region == AWS_REGION_US_EAST_1 @@ -146,7 +127,111 @@ class Test_rds_instance_default_admin: assert result[0].status == "PASS" assert ( result[0].status_extended - == "RDS Instance db-master-1 which is not clustered is not using the default master username." + == "RDS Instance db-master-1 is not using the default master username." + ) + 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_cluster_instance_with_default_username(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_cluster( + DBClusterIdentifier="db-cluster-1", + Engine="aurora-postgresql", + MasterUsername="postgres", + MasterUserPassword="defaultpassword", + ) + conn.create_db_instance( + DBInstanceIdentifier="db-master-1", + DBClusterIdentifier="db-cluster-1", + AllocatedStorage=10, + Engine="aurora-postgresql", + DBName="aurora-postgres", + MasterUsername="postgres", + DBInstanceClass="db.m1.small", + ) + 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.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_instance_default_admin.rds_instance_default_admin.rds_client", + new=RDS(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.rds.rds_instance_default_admin.rds_instance_default_admin import ( + rds_instance_default_admin, + ) + + check = rds_instance_default_admin() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "RDS Instance db-master-1 is using the default master username at cluster db-cluster-1 level." + ) + 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_cluster_instance_without_default_username(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_cluster( + DBClusterIdentifier="db-cluster-1", + Engine="aurora-postgresql", + MasterUsername="custom", + MasterUserPassword="defaultpassword", + ) + conn.create_db_instance( + DBInstanceIdentifier="db-master-1", + DBClusterIdentifier="db-cluster-1", + AllocatedStorage=10, + Engine="aurora-postgresql", + DBName="aurora-postgres", + MasterUsername="postgres2", + DBInstanceClass="db.m1.small", + ) + 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.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_instance_default_admin.rds_instance_default_admin.rds_client", + new=RDS(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.rds.rds_instance_default_admin.rds_instance_default_admin import ( + rds_instance_default_admin, + ) + + check = rds_instance_default_admin() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "RDS Instance db-master-1 is not using the default master username at cluster db-cluster-1 level." ) 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_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled_test.py b/tests/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled_test.py index ddff4f2238..42f4c6dabb 100644 --- a/tests/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled_test.py +++ b/tests/providers/aws/services/rds/rds_instance_iam_authentication_enabled/rds_instance_iam_authentication_enabled_test.py @@ -1,6 +1,5 @@ from unittest import mock -import botocore from boto3 import client from moto import mock_aws @@ -10,26 +9,7 @@ from tests.providers.aws.utils import ( set_mocked_aws_provider, ) -make_api_call = botocore.client.BaseClient._make_api_call - -def mock_make_api_call(self, operation_name, kwarg): - if operation_name == "DescribeDBEngineVersions": - return { - "DBEngineVersions": [ - { - "Engine": "mysql", - "EngineVersion": "8.0.32", - "DBEngineDescription": "description", - "DBEngineVersionDescription": "description", - }, - ] - } - - return make_api_call(self, operation_name, kwarg) - - -@mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) class Test_rds_instance_iam_authentication_enabled: @mock_aws def test_rds_no_instances(self): @@ -96,7 +76,7 @@ class Test_rds_instance_iam_authentication_enabled: assert result[0].status == "FAIL" assert ( result[0].status_extended - == "RDS Instance db-master-1 which is not clustered does not have IAM authentication enabled." + == "RDS Instance db-master-1 does not have IAM authentication enabled." ) assert result[0].resource_id == "db-master-1" assert result[0].region == AWS_REGION_US_EAST_1 @@ -148,7 +128,7 @@ class Test_rds_instance_iam_authentication_enabled: assert result[0].status == "PASS" assert ( result[0].status_extended - == "RDS Instance db-master-1 which is not clustered has IAM authentication enabled." + == "RDS Instance db-master-1 has IAM authentication enabled." ) assert result[0].resource_id == "db-master-1" assert result[0].region == AWS_REGION_US_EAST_1 @@ -200,7 +180,7 @@ class Test_rds_instance_iam_authentication_enabled: assert result[0].status == "PASS" assert ( result[0].status_extended - == "RDS Instance db-master-1 which is not clustered has IAM authentication enabled." + == "RDS Instance db-master-1 has IAM authentication enabled." ) assert result[0].resource_id == "db-master-1" assert result[0].region == AWS_REGION_US_EAST_1 @@ -252,7 +232,7 @@ class Test_rds_instance_iam_authentication_enabled: assert result[0].status == "PASS" assert ( result[0].status_extended - == "RDS Instance db-master-1 which is not clustered has IAM authentication enabled." + == "RDS Instance db-master-1 has IAM authentication enabled." ) assert result[0].resource_id == "db-master-1" assert result[0].region == AWS_REGION_US_EAST_1 @@ -300,3 +280,57 @@ class Test_rds_instance_iam_authentication_enabled: result = check.execute() assert len(result) == 0 + + @mock_aws + def test_cluster_instance_without_iam_authentication(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_cluster( + DBClusterIdentifier="db-cluster-1", + Engine="mysql", + DBSubnetGroupName="default", + EngineMode="provisioned", + MasterUsername="admin", + MasterUserPassword="password", + ) + conn.create_db_instance( + DBInstanceIdentifier="db-instance-1", + DBClusterIdentifier="db-cluster-1", + AllocatedStorage=10, + Engine="mysql", + DBName="staging-mysql", + DBInstanceClass="db.m1.small", + ) + + 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.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_instance_iam_authentication_enabled.rds_instance_iam_authentication_enabled.rds_client", + new=RDS(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.rds.rds_instance_iam_authentication_enabled.rds_instance_iam_authentication_enabled import ( + rds_instance_iam_authentication_enabled, + ) + + check = rds_instance_iam_authentication_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "RDS Instance db-instance-1 does not have IAM authentication enabled at cluster db-cluster-1 level." + ) + assert result[0].resource_id == "db-instance-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-instance-1" + ) + assert result[0].resource_tags == []