-
Notifications
You must be signed in to change notification settings - Fork 2k
feat(aws): add check rds_instance_extended_support #9865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
912bf04
190be08
d7a6b7b
a11165a
ad1bbde
8c6e970
c66fc61
2e1ee30
4259a32
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,3 +9,7 @@ component_management: | |
|
|
||
| comment: | ||
| layout: "header, diff, flags, components" | ||
|
|
||
| flags: | ||
| api: | ||
| carryforward: true | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, move it to the current UNRELEASED version and follow the same style as other new checks in the Added section |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,41 @@ | ||||||
| { | ||||||
| "Provider": "aws", | ||||||
| "CheckID": "rds_instance_extended_support", | ||||||
| "CheckTitle": "RDS instances are not enrolled in RDS Extended Support", | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| "CheckType": [ | ||||||
| "Software and Configuration Checks/Patch Management", | ||||||
| "Software and Configuration Checks/AWS Security Best Practices" | ||||||
| ], | ||||||
| "ServiceName": "rds", | ||||||
| "SubServiceName": "", | ||||||
| "ResourceIdTemplate": "", | ||||||
| "Severity": "medium", | ||||||
| "ResourceType": "AwsRdsDbInstance", | ||||||
| "ResourceGroup": "database", | ||||||
| "Description": "Checks whether Amazon RDS DB instances are enrolled in Amazon RDS Extended Support. If the instance reports `EngineLifecycleSupport` as `open-source-rds-extended-support`, it is enrolled and the check fails. Otherwise, the check passes.", | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| "Risk": "DB instances enrolled in RDS Extended Support can incur additional charges after the end of standard support for the running database major version. Remaining on older major versions can also delay necessary upgrades, increasing operational and security risk.", | ||||||
| "RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support.html", | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| "AdditionalURLs": [ | ||||||
| "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support-viewing.html", | ||||||
| "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support-charges.html", | ||||||
| "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support-creating-db-instance.html" | ||||||
| ], | ||||||
| "Remediation": { | ||||||
| "Code": { | ||||||
| "CLI": "aws rds modify-db-instance --db-instance-identifier <DB_INSTANCE_IDENTIFIER> --engine-version <TARGET_ENGINE_VERSION> --allow-major-version-upgrade --apply-immediately\n# For new DB instances created via automation, prevent enrollment by setting the lifecycle option:\naws rds create-db-instance ... --engine-lifecycle-support open-source-rds-extended-support-disabled", | ||||||
| "NativeIaC": "```yaml\n# CloudFormation: upgrade RDS engine version for an existing instance\nResources:\n <example_resource_name>:\n Type: AWS::RDS::DBInstance\n Properties:\n DBInstanceIdentifier: <example_resource_id>\n Engine: <engine>\n DBInstanceClass: db.t3.micro\n EngineVersion: <SUPPORTED_ENGINE_VERSION> # CRITICAL: move to a supported engine version\n AllowMajorVersionUpgrade: true # CRITICAL: required if upgrading major version\n ApplyImmediately: true # CRITICAL: apply change now to pass the check\n```", | ||||||
| "Other": "If your automation (CloudFormation/Terraform/SDK) creates or restores DB instances, set EngineLifecycleSupport/LifeCycleSupport to open-source-rds-extended-support-disabled where supported, and ensure your upgrade process keeps engines within standard support.", | ||||||
| "Terraform": "```hcl\n# Upgrade RDS engine version\nresource \"aws_db_instance\" \"<example_resource_name>\" {\n identifier = \"<example_resource_id>\"\n engine = \"<engine>\"\n instance_class = \"db.t3.micro\"\n allocated_storage = 20\n\n engine_version = \"<SUPPORTED_ENGINE_VERSION>\" # CRITICAL: use a supported version\n allow_major_version_upgrade = true # CRITICAL: needed for major upgrades\n apply_immediately = true # CRITICAL: apply now to pass the check\n}\n```" | ||||||
| }, | ||||||
| "Recommendation": { | ||||||
| "Text": "Upgrade enrolled DB instances to an engine version covered under standard support to stop Extended Support charges. For new DB instances and restores created via automation, explicitly set the engine lifecycle support option to avoid unintended enrollment in RDS Extended Support when that is your policy.", | ||||||
| "Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support-charges.html" | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
| }, | ||||||
| "Categories": [ | ||||||
| "vulnerabilities" | ||||||
| ], | ||||||
| "DependsOn": [], | ||||||
| "RelatedTo": [], | ||||||
| "Notes": "" | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| """ | ||
| Prowler check: rds_instance_extended_support | ||
|
|
||
| This check fails when an RDS DB instance is enrolled in Amazon RDS Extended Support. | ||
| Enrollment is exposed via the "EngineLifecycleSupport" attribute returned by DescribeDBInstances. | ||
| """ | ||
|
|
||
| from prowler.lib.check.models import Check, Check_Report_AWS | ||
| from prowler.providers.aws.services.rds.rds_client import rds_client | ||
|
|
||
|
|
||
| class rds_instance_extended_support(Check): | ||
| def execute(self): | ||
| findings = [] | ||
|
|
||
| for db_instance in rds_client.db_instances.values(): | ||
| report = Check_Report_AWS(metadata=self.metadata(), resource=db_instance) | ||
|
|
||
| # EngineLifecycleSupport can be absent when Extended Support is not applicable. | ||
| lifecycle_support = getattr(db_instance, "engine_lifecycle_support", None) | ||
|
|
||
| if lifecycle_support == "open-source-rds-extended-support": | ||
| report.status = "FAIL" | ||
| report.status_extended = ( | ||
| f"RDS instance {db_instance.id} ({db_instance.engine} {db_instance.engine_version}) " | ||
| f"is enrolled in RDS Extended Support (EngineLifecycleSupport={lifecycle_support})." | ||
| ) | ||
| else: | ||
| report.status = "PASS" | ||
| report.status_extended = ( | ||
| f"RDS instance {db_instance.id} ({db_instance.engine} {db_instance.engine_version}) " | ||
| "is not enrolled in RDS Extended Support." | ||
| ) | ||
|
|
||
| findings.append(report) | ||
|
|
||
| return findings |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| from unittest import mock | ||
| from unittest.mock import patch | ||
|
|
||
| import botocore | ||
| from boto3 import client | ||
| from moto import mock_aws | ||
|
|
||
| from tests.providers.aws.utils import ( | ||
| AWS_ACCOUNT_NUMBER, | ||
| AWS_REGION_US_EAST_1, | ||
| set_mocked_aws_provider, | ||
| ) | ||
|
|
||
| make_api_call = botocore.client.BaseClient._make_api_call | ||
|
|
||
|
|
||
| def mock_make_api_call(self, operation_name, kwarg): | ||
| """ | ||
| Moto's RDS implementation does not currently expose EngineLifecycleSupport on DescribeDBInstances. | ||
| This patch injects it into the response so that Prowler's RDS service can map it onto the DBInstance model. | ||
|
|
||
| The check under test fails when: | ||
| EngineLifecycleSupport == "open-source-rds-extended-support" | ||
| """ | ||
| response = make_api_call(self, operation_name, kwarg) | ||
|
|
||
| if operation_name == "DescribeDBInstances": | ||
| for instance in response.get("DBInstances", []): | ||
| if instance.get("DBInstanceIdentifier") == "db-extended-1": | ||
| instance["EngineLifecycleSupport"] = "open-source-rds-extended-support" | ||
| return response | ||
|
|
||
| return response | ||
|
|
||
|
|
||
| @patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) | ||
| class Test_rds_instance_extended_support: | ||
| @mock_aws | ||
| def test_rds_no_instances(self): | ||
| 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_extended_support.rds_instance_extended_support.rds_client", | ||
| new=RDS(aws_provider), | ||
| ): | ||
| # Test Check | ||
| from prowler.providers.aws.services.rds.rds_instance_extended_support.rds_instance_extended_support import ( | ||
| rds_instance_extended_support, | ||
| ) | ||
|
|
||
| check = rds_instance_extended_support() | ||
| result = check.execute() | ||
|
|
||
| assert len(result) == 0 | ||
|
|
||
| @mock_aws | ||
| def test_rds_instance_not_enrolled_in_extended_support(self): | ||
| conn = client("rds", region_name=AWS_REGION_US_EAST_1) | ||
| conn.create_db_instance( | ||
| DBInstanceIdentifier="db-standard-1", | ||
| AllocatedStorage=10, | ||
| Engine="postgres", | ||
| EngineVersion="8.0.32", | ||
| DBName="staging-postgres", | ||
| DBInstanceClass="db.m1.small", | ||
| PubliclyAccessible=False, | ||
| ) | ||
|
|
||
| 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_extended_support.rds_instance_extended_support.rds_client", | ||
| new=RDS(aws_provider), | ||
| ): | ||
| # Test Check | ||
| from prowler.providers.aws.services.rds.rds_instance_extended_support.rds_instance_extended_support import ( | ||
| rds_instance_extended_support, | ||
| ) | ||
|
|
||
| check = rds_instance_extended_support() | ||
| result = check.execute() | ||
|
|
||
| assert len(result) == 1 | ||
| assert result[0].status == "PASS" | ||
| assert ( | ||
| result[0].status_extended | ||
| == "RDS instance db-standard-1 (postgres 8.0.32) is not enrolled in RDS Extended Support." | ||
| ) | ||
| assert result[0].resource_id == "db-standard-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-standard-1" | ||
| ) | ||
| assert result[0].resource_tags == [] | ||
|
|
||
| @mock_aws | ||
| def test_rds_instance_enrolled_in_extended_support(self): | ||
| conn = client("rds", region_name=AWS_REGION_US_EAST_1) | ||
| conn.create_db_instance( | ||
| DBInstanceIdentifier="db-extended-1", | ||
| AllocatedStorage=10, | ||
| Engine="postgres", | ||
| EngineVersion="8.0.32", | ||
| DBName="staging-postgres", | ||
| DBInstanceClass="db.m1.small", | ||
| PubliclyAccessible=False, | ||
| ) | ||
|
|
||
| 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_extended_support.rds_instance_extended_support.rds_client", | ||
| new=RDS(aws_provider), | ||
| ): | ||
| # Test Check | ||
| from prowler.providers.aws.services.rds.rds_instance_extended_support.rds_instance_extended_support import ( | ||
| rds_instance_extended_support, | ||
| ) | ||
|
|
||
| check = rds_instance_extended_support() | ||
| result = check.execute() | ||
|
|
||
| assert len(result) == 1 | ||
| assert result[0].status == "FAIL" | ||
| assert ( | ||
| result[0].status_extended | ||
| == "RDS instance db-extended-1 (postgres 8.0.32) is enrolled in RDS Extended Support " | ||
| "(EngineLifecycleSupport=open-source-rds-extended-support)." | ||
| ) | ||
| assert result[0].resource_id == "db-extended-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-extended-1" | ||
| ) | ||
| assert result[0].resource_tags == [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, remove this changes. Don't worry about codecov since it's not a blocker for merging PRs, I'll talk with the team about this to see if there is a problem with the current settings.