From 680781656b52604ab2acbed8184a155622ece4e5 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:04:27 +0200 Subject: [PATCH] feat(aws): Add new RDS check to verify that cluster minor version upgrade is enabled (#4725) --- .../__init__.py | 0 ...inor_version_upgrade_enabled.metadata.json | 30 +++ ...s_cluster_minor_version_upgrade_enabled.py | 25 +++ ...ster_minor_version_upgrade_enabled_test.py | 173 ++++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/__init__.py create mode 100644 prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled.metadata.json create mode 100644 prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled.py create mode 100644 tests/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled_test.py diff --git a/prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/__init__.py b/prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled.metadata.json b/prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled.metadata.json new file mode 100644 index 0000000000..ad9ccae31c --- /dev/null +++ b/prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "aws", + "CheckID": "rds_cluster_minor_version_upgrade_enabled", + "CheckTitle": "Ensure RDS clusters have minor version upgrade enabled.", + "CheckType": [], + "ServiceName": "rds", + "SubServiceName": "", + "ResourceIdTemplate": "arn:aws:rds:region:account-id:db-cluster", + "Severity": "medium", + "ResourceType": "AwsRdsDbCluster", + "Description": "Ensure RDS clusters have minor version upgrade enabled.", + "Risk": "Auto Minor Version Upgrade is a feature that you can enable to have your database automatically upgraded when a new minor database engine version is available. Minor version upgrades often patch security vulnerabilities and fix bugs and therefore should be applied.", + "RelatedUrl": "https://aws.amazon.com/blogs/database/best-practices-for-upgrading-amazon-rds-to-major-and-minor-versions-of-postgresql/", + "Remediation": { + "Code": { + "CLI": "aws rds modify-db-cluster --db-cluster-identifier --auto-minor-version-upgrade --apply-immediately", + "NativeIaC": "https://docs.prowler.com/checks/aws/general-policies/ensure-aws-db-instance-gets-all-minor-upgrades-automatically#cloudformation", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/rds-controls.html#rds-35", + "Terraform": "https://docs.prowler.com/checks/aws/general-policies/ensure-aws-db-instance-gets-all-minor-upgrades-automatically#terraform" + }, + "Recommendation": { + "Text": "Enable auto minor version upgrade for all databases and environments.", + "Url": "https://aws.amazon.com/blogs/database/best-practices-for-upgrading-amazon-rds-to-major-and-minor-versions-of-postgresql/" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled.py b/prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled.py new file mode 100644 index 0000000000..8638b8fef9 --- /dev/null +++ b/prowler/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled.py @@ -0,0 +1,25 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.rds.rds_client import rds_client + + +class rds_cluster_minor_version_upgrade_enabled(Check): + def execute(self): + findings = [] + for db_cluster in rds_client.db_clusters: + # Auto minor version upgrade is only available for non-Aurora Multi-AZ DB clusters + if rds_client.db_clusters[db_cluster].multi_az: + report = Check_Report_AWS(self.metadata()) + report.region = rds_client.db_clusters[db_cluster].region + report.resource_id = rds_client.db_clusters[db_cluster].id + report.resource_arn = rds_client.db_clusters[db_cluster].arn + report.resource_tags = rds_client.db_clusters[db_cluster].tags + if rds_client.db_clusters[db_cluster].auto_minor_version_upgrade: + report.status = "PASS" + report.status_extended = f"RDS Cluster {rds_client.db_clusters[db_cluster].id} has minor version upgrade enabled." + else: + report.status = "FAIL" + report.status_extended = f"RDS Cluster {rds_client.db_clusters[db_cluster].id} does not have minor version upgrade enabled." + + findings.append(report) + + return findings diff --git a/tests/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled_test.py b/tests/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled_test.py new file mode 100644 index 0000000000..3d7a257fbc --- /dev/null +++ b/tests/providers/aws/services/rds/rds_cluster_minor_version_upgrade_enabled/rds_cluster_minor_version_upgrade_enabled_test.py @@ -0,0 +1,173 @@ +from unittest import mock + +from prowler.providers.aws.services.rds.rds_service import DBCluster +from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER, AWS_REGION_US_EAST_1 + + +class Test_rds_cluster_minor_version_upgrade_enabled: + def test_rds_no_clusters(self): + rds_client = mock.MagicMock + rds_client.db_clusters = {} + + with mock.patch( + "prowler.providers.aws.services.rds.rds_service.RDS", + new=rds_client, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_minor_version_upgrade_enabled.rds_cluster_minor_version_upgrade_enabled.rds_client", + new=rds_client, + ): + from prowler.providers.aws.services.rds.rds_cluster_minor_version_upgrade_enabled.rds_cluster_minor_version_upgrade_enabled import ( + rds_cluster_minor_version_upgrade_enabled, + ) + + check = rds_cluster_minor_version_upgrade_enabled() + result = check.execute() + + assert len(result) == 0 + + def test_rds_cluster_no_multi(self): + rds_client = mock.MagicMock + rds_client.db_clusters = { + "db-cluster-1": DBCluster( + id="db-cluster-1", + arn=f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:cluster:db-cluster-1", + endpoint="", + engine="postgres", + status="available", + public=False, + encrypted=True, + auto_minor_version_upgrade=False, + backup_retention_period=7, + backtrack=0, + cloudwatch_logs=[], + deletion_protection=False, + parameter_group="default.postgres10", + multi_az=False, + username="admin", + iam_auth=False, + region=AWS_REGION_US_EAST_1, + tags=[], + ) + } + + with mock.patch( + "prowler.providers.aws.services.rds.rds_service.RDS", + new=rds_client, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_minor_version_upgrade_enabled.rds_cluster_minor_version_upgrade_enabled.rds_client", + new=rds_client, + ): + from prowler.providers.aws.services.rds.rds_cluster_minor_version_upgrade_enabled.rds_cluster_minor_version_upgrade_enabled import ( + rds_cluster_minor_version_upgrade_enabled, + ) + + check = rds_cluster_minor_version_upgrade_enabled() + result = check.execute() + + assert len(result) == 0 + + def test_rds_cluster_no_auto_upgrade(self): + rds_client = mock.MagicMock + rds_client.db_clusters = { + "db-cluster-1": DBCluster( + id="db-cluster-1", + arn=f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:cluster:db-cluster-1", + endpoint="", + engine="postgres", + status="available", + public=False, + encrypted=True, + auto_minor_version_upgrade=False, + backup_retention_period=7, + backtrack=0, + cloudwatch_logs=[], + deletion_protection=False, + parameter_group="default.postgres10", + multi_az=True, + username="admin", + iam_auth=False, + region=AWS_REGION_US_EAST_1, + tags=[], + ) + } + + with mock.patch( + "prowler.providers.aws.services.rds.rds_service.RDS", + new=rds_client, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_minor_version_upgrade_enabled.rds_cluster_minor_version_upgrade_enabled.rds_client", + new=rds_client, + ): + from prowler.providers.aws.services.rds.rds_cluster_minor_version_upgrade_enabled.rds_cluster_minor_version_upgrade_enabled import ( + rds_cluster_minor_version_upgrade_enabled, + ) + + check = rds_cluster_minor_version_upgrade_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "RDS Cluster db-cluster-1 does not have minor version upgrade enabled." + ) + assert result[0].resource_id == "db-cluster-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}:cluster:db-cluster-1" + ) + assert result[0].resource_tags == [] + + def test_rds_cluster_with_auto_upgrade(self): + rds_client = mock.MagicMock + rds_client.db_clusters = { + "db-cluster-1": DBCluster( + id="db-cluster-1", + arn=f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:cluster:db-cluster-1", + endpoint="", + engine="postgres", + status="available", + public=False, + encrypted=True, + auto_minor_version_upgrade=True, + backup_retention_period=7, + backtrack=0, + cloudwatch_logs=[], + deletion_protection=False, + parameter_group="default.postgres10", + multi_az=True, + username="admin", + iam_auth=False, + region=AWS_REGION_US_EAST_1, + tags=[], + ) + } + + with mock.patch( + "prowler.providers.aws.services.rds.rds_service.RDS", + new=rds_client, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_minor_version_upgrade_enabled.rds_cluster_minor_version_upgrade_enabled.rds_client", + new=rds_client, + ): + from prowler.providers.aws.services.rds.rds_cluster_minor_version_upgrade_enabled.rds_cluster_minor_version_upgrade_enabled import ( + rds_cluster_minor_version_upgrade_enabled, + ) + + check = rds_cluster_minor_version_upgrade_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "RDS Cluster db-cluster-1 has minor version upgrade enabled." + ) + assert result[0].resource_id == "db-cluster-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}:cluster:db-cluster-1" + ) + assert result[0].resource_tags == []