-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
feat(aws): Add new check to ensure Aurora MySQL DB Clusters publish audit logs to CloudWatch logs #4916
feat(aws): Add new check to ensure Aurora MySQL DB Clusters publish audit logs to CloudWatch logs #4916
Changes from 4 commits
bd50df5
fca7217
72c8de0
40af6d5
146b80e
0f2e5f4
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 |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"Provider": "aws", | ||
"CheckID": "rds_cluster_integration_cloudwatch_logs", | ||
"CheckTitle": "Check if RDS Aurora MySQL cluster is integrated with CloudWatch Logs.", | ||
"CheckType": [], | ||
"ServiceName": "rds", | ||
"SubServiceName": "", | ||
"ResourceIdTemplate": "arn:aws:rds:region:account-id:db-cluster", | ||
"Severity": "medium", | ||
"ResourceType": "AwsRdsDbCluster", | ||
"Description": "Check if RDS Aurora MySQL cluster is integrated with CloudWatch Logs.", | ||
"Risk": "If logs are not enabled, monitoring of service use and threat analysis is not possible.", | ||
"RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_LogAccess.html", | ||
"Remediation": { | ||
"Code": { | ||
"CLI": "aws rds modify-db-cluster --db-cluster-identifier <db_cluster_id> --cloudwatch-logs-export-configuration {'EnableLogTypes':['audit',error','general','slowquery']} --apply-immediately", | ||
"NativeIaC": "", | ||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/rds-controls.html#rds-34", | ||
"Terraform": "" | ||
}, | ||
"Recommendation": { | ||
"Text": "Use CloudWatch Logs to perform real-time analysis of the log data. Create alarms and view metrics.", | ||
"Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/publishing_cloudwatchlogs.html" | ||
} | ||
}, | ||
"Categories": [], | ||
"DependsOn": [], | ||
"RelatedTo": [], | ||
"Notes": "" | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,24 @@ | ||||||
from prowler.lib.check.models import Check, Check_Report_AWS | ||||||
from prowler.providers.aws.services.rds.rds_client import rds_client | ||||||
|
||||||
|
||||||
class rds_cluster_integration_cloudwatch_logs(Check): | ||||||
def execute(self): | ||||||
findings = [] | ||||||
for db_cluster_arn, db_cluster in rds_client.db_clusters.items(): | ||||||
if db_cluster.engine == "aurora-mysql": | ||||||
report = Check_Report_AWS(self.metadata()) | ||||||
report.region = db_cluster.region | ||||||
report.resource_id = db_cluster.id | ||||||
report.resource_arn = db_cluster_arn | ||||||
report.resource_tags = db_cluster.tags | ||||||
if db_cluster.cloudwatch_logs: | ||||||
report.status = "PASS" | ||||||
report.status_extended = f"Aurora MySQL Cluster {db_cluster.id} is shipping {', '.join(db_cluster.cloudwatch_logs)} logs to CloudWatch Logs." | ||||||
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
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. Done. |
||||||
else: | ||||||
report.status = "FAIL" | ||||||
report.status_extended = f"Aurora MySQL Cluster {db_cluster.id} does not have CloudWatch Logs enabled." | ||||||
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
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. Done. |
||||||
|
||||||
findings.append(report) | ||||||
|
||||||
return findings |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
from unittest import mock | ||
|
||
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, | ||
) | ||
|
||
|
||
class Test_rds_cluster_integration_cloudwatch_logs: | ||
@mock_aws | ||
def test_rds_no_clusters(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_cluster_integration_cloudwatch_logs.rds_cluster_integration_cloudwatch_logs.rds_client", | ||
new=RDS(aws_provider), | ||
): | ||
# Test Check | ||
from prowler.providers.aws.services.rds.rds_cluster_integration_cloudwatch_logs.rds_cluster_integration_cloudwatch_logs import ( | ||
rds_cluster_integration_cloudwatch_logs, | ||
) | ||
|
||
check = rds_cluster_integration_cloudwatch_logs() | ||
result = check.execute() | ||
|
||
assert len(result) == 0 | ||
|
||
@mock_aws | ||
def test_rds_no_aurora_cluster(self): | ||
conn = client("rds", region_name=AWS_REGION_US_EAST_1) | ||
conn.create_db_cluster( | ||
DBClusterIdentifier="cluster-1", | ||
Engine="mysql", | ||
MasterUsername="admin", | ||
MasterUserPassword="password", | ||
) | ||
|
||
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_cluster_integration_cloudwatch_logs.rds_cluster_integration_cloudwatch_logs.rds_client", | ||
new=RDS(aws_provider), | ||
): | ||
# Test Check | ||
from prowler.providers.aws.services.rds.rds_cluster_integration_cloudwatch_logs.rds_cluster_integration_cloudwatch_logs import ( | ||
rds_cluster_integration_cloudwatch_logs, | ||
) | ||
|
||
check = rds_cluster_integration_cloudwatch_logs() | ||
result = check.execute() | ||
|
||
assert len(result) == 0 | ||
|
||
@mock_aws | ||
def test_rds_cluster_no_logs(self): | ||
conn = client("rds", region_name=AWS_REGION_US_EAST_1) | ||
conn.create_db_cluster( | ||
DBClusterIdentifier="aurora-cluster-1", | ||
Engine="aurora-mysql", | ||
MasterUsername="admin", | ||
MasterUserPassword="password", | ||
) | ||
|
||
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_cluster_integration_cloudwatch_logs.rds_cluster_integration_cloudwatch_logs.rds_client", | ||
new=RDS(aws_provider), | ||
): | ||
# Test Check | ||
from prowler.providers.aws.services.rds.rds_cluster_integration_cloudwatch_logs.rds_cluster_integration_cloudwatch_logs import ( | ||
rds_cluster_integration_cloudwatch_logs, | ||
) | ||
|
||
check = rds_cluster_integration_cloudwatch_logs() | ||
result = check.execute() | ||
|
||
assert len(result) == 1 | ||
assert result[0].status == "FAIL" | ||
assert ( | ||
result[0].status_extended | ||
== "Aurora MySQL Cluster aurora-cluster-1 does not have CloudWatch Logs enabled." | ||
) | ||
assert result[0].resource_id == "aurora-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:aurora-cluster-1" | ||
) | ||
assert result[0].resource_tags == [] | ||
|
||
@mock_aws | ||
def test_rds_cluster_with_logs(self): | ||
conn = client("rds", region_name=AWS_REGION_US_EAST_1) | ||
conn.create_db_cluster( | ||
DBClusterIdentifier="aurora-cluster-1", | ||
Engine="aurora-mysql", | ||
MasterUsername="admin", | ||
MasterUserPassword="password", | ||
EnableCloudwatchLogsExports=["audit", "error"], | ||
) | ||
|
||
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_cluster_integration_cloudwatch_logs.rds_cluster_integration_cloudwatch_logs.rds_client", | ||
new=RDS(aws_provider), | ||
): | ||
# Test Check | ||
from prowler.providers.aws.services.rds.rds_cluster_integration_cloudwatch_logs.rds_cluster_integration_cloudwatch_logs import ( | ||
rds_cluster_integration_cloudwatch_logs, | ||
) | ||
|
||
check = rds_cluster_integration_cloudwatch_logs() | ||
result = check.execute() | ||
|
||
assert len(result) == 1 | ||
assert result[0].status == "PASS" | ||
assert ( | ||
result[0].status_extended | ||
== "Aurora MySQL Cluster aurora-cluster-1 is shipping audit, error logs to CloudWatch Logs." | ||
) | ||
assert result[0].resource_id == "aurora-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:aurora-cluster-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.
According to https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_CreateDBCluster.html, it is valid for:
Please, make the check look for these engines too.
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.
Done!