feat(aws): Add new RDS check to verify that cluster minor version upgrade is enabled (#4725)

This commit is contained in:
Daniel Barranquero
2024-08-14 15:04:27 +02:00
committed by GitHub
parent 21382efd07
commit 680781656b
4 changed files with 228 additions and 0 deletions
@@ -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 <db_cluster_id> --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": ""
}
@@ -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
@@ -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 == []