mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 06:45:08 +00:00
feat(CloudWatch): add CloudWatch service and checks (#1456)
Co-authored-by: sergargar <sergio@verica.io> Co-authored-by: Nacho Rivera <59198746+n4ch04@users.noreply.github.com> Co-authored-by: Pepe Fagoaga <pepe@verica.io>
This commit is contained in:
@@ -292,11 +292,15 @@ def generate_regional_clients(service: str, audit_info: AWS_Audit_Info) -> dict:
|
||||
data = parse_json_file(f)
|
||||
# Check if it is a subservice
|
||||
if service == "accessanalyzer":
|
||||
json_regions = data["services"]['iam']["regions"][audit_info.audited_partition]
|
||||
json_regions = data["services"]["iam"]["regions"][audit_info.audited_partition]
|
||||
elif service == "apigatewayv2":
|
||||
json_regions = data["services"]["apigateway"]["regions"][
|
||||
audit_info.audited_partition
|
||||
]
|
||||
elif service == "logs":
|
||||
json_regions = data["services"]["cloudwatch"]["regions"][
|
||||
audit_info.audited_partition
|
||||
]
|
||||
else:
|
||||
json_regions = data["services"][service]["regions"][
|
||||
audit_info.audited_partition
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# ec2_elastic_ip_shodan
|
||||
shodan_api_key: null
|
||||
|
||||
# Single account environment: No action required. The AWS account number will be automatically added by the checks.
|
||||
@@ -5,6 +6,9 @@ shodan_api_key: null
|
||||
# trusted_account_ids : ["123456789012", "098765432109", "678901234567"]
|
||||
trusted_account_ids : []
|
||||
|
||||
# cloudwatch_log_group_retention_policy_specific_days_enabled --> by default is 365 days
|
||||
log_group_retention_days: 365
|
||||
|
||||
# AppStream Session Configuration
|
||||
max_idle_disconnect_timeout_in_seconds: 600 # 10 Minutes
|
||||
max_disconnect_timeout_in_seconds: 300 # 5 Minutes
|
||||
|
||||
@@ -19,7 +19,7 @@ def parse_allowlist_file(audit_info, allowlist_file):
|
||||
)["Allowlist"]
|
||||
# Check if file is a DynamoDB ARN
|
||||
elif re.search(
|
||||
"^arn:[aws\|aws\-cn\|aws\-us\-gov]+:dynamodb:[a-z]{2}-[north\|south\|east\|west\|central]+-[1-9]{1}:[0-9]{12}:table\/[a-zA-Z0-9._-]+$",
|
||||
r"^arn:aws(-cn|-us-gov)?:dynamodb:[a-z]{2}-[a-z-]+-[1-9]{1}:[0-9]{12}:table\/[a-zA-Z0-9._-]+$",
|
||||
allowlist_file,
|
||||
):
|
||||
allowlist = {"Accounts": {}}
|
||||
|
||||
@@ -40,10 +40,12 @@ class Cloudtrail:
|
||||
describe_trails = regional_client.describe_trails()["trailList"]
|
||||
if describe_trails:
|
||||
for trail in describe_trails:
|
||||
kms_key_id = None
|
||||
log_group_arn = None
|
||||
if "KmsKeyId" in trail:
|
||||
kms_key_id = trail["KmsKeyId"]
|
||||
else:
|
||||
kms_key_id = None
|
||||
if "CloudWatchLogsLogGroupArn" in trail:
|
||||
log_group_arn = trail["CloudWatchLogsLogGroupArn"]
|
||||
self.trails.append(
|
||||
Trail(
|
||||
name=trail["Name"],
|
||||
@@ -58,6 +60,7 @@ class Cloudtrail:
|
||||
latest_cloudwatch_delivery_time=None,
|
||||
s3_bucket=trail["S3BucketName"],
|
||||
kms_key=kms_key_id,
|
||||
log_group_arn=log_group_arn,
|
||||
data_events=[],
|
||||
)
|
||||
)
|
||||
@@ -74,6 +77,7 @@ class Cloudtrail:
|
||||
latest_cloudwatch_delivery_time=None,
|
||||
s3_bucket=None,
|
||||
kms_key=None,
|
||||
log_group_arn=None,
|
||||
data_events=[],
|
||||
)
|
||||
)
|
||||
@@ -129,6 +133,7 @@ class Trail:
|
||||
latest_cloudwatch_delivery_time: datetime
|
||||
s3_bucket: str
|
||||
kms_key: str
|
||||
log_group_arn: str
|
||||
data_events: list
|
||||
|
||||
def __init__(
|
||||
@@ -143,6 +148,7 @@ class Trail:
|
||||
latest_cloudwatch_delivery_time,
|
||||
s3_bucket,
|
||||
kms_key,
|
||||
log_group_arn,
|
||||
data_events,
|
||||
):
|
||||
self.name = name
|
||||
@@ -155,4 +161,5 @@ class Trail:
|
||||
self.latest_cloudwatch_delivery_time = latest_cloudwatch_delivery_time
|
||||
self.s3_bucket = s3_bucket
|
||||
self.kms_key = kms_key
|
||||
self.log_group_arn = log_group_arn
|
||||
self.data_events = data_events
|
||||
|
||||
0
providers/aws/services/cloudwatch/__init__.py
Normal file
0
providers/aws/services/cloudwatch/__init__.py
Normal file
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/MyCloudTrailLG \
|
||||
# --filter-name AWSAuthorizationFailures \
|
||||
# --filter-pattern '{ $.errorCode = "*UnauthorizedOperation" || $.errorCode = "AccessDenied*" }' \
|
||||
# --metric-transformations metricName=AuthorizationFailureCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name "Authorization Failures" \
|
||||
# --alarm-description "Alarm triggered when unauthorized API calls are made" \
|
||||
# --metric-name AuthorizationFailureCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check31="3.1"
|
||||
CHECK_TITLE_check31="[check31] Ensure a log metric filter and alarm exist for unauthorized API calls"
|
||||
CHECK_SCORED_check31="SCORED"
|
||||
CHECK_CIS_LEVEL_check31="LEVEL1"
|
||||
CHECK_SEVERITY_check31="Medium"
|
||||
CHECK_ASFF_TYPE_check31="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check31="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check301="check31"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_check31="ens-op.exp.8.aws.trail.2"
|
||||
CHECK_SERVICENAME_check31="iam"
|
||||
CHECK_RISK_check31='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check31='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check31='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check31='Logging and Monitoring'
|
||||
|
||||
check31(){
|
||||
check3x '\$\.errorCode\s*=\s*"\*UnauthorizedOperation".+\$\.errorCode\s*=\s*"AccessDenied\*"'
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name SecurityGroupConfigChanges \
|
||||
# --filter-pattern '{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup) }' \
|
||||
# --metric-transformations metricName=SecurityGroupEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name SecurityGroupConfigChangesAlarm \
|
||||
# --alarm-description "Triggered by AWS security group(s) config changes." \
|
||||
# --metric-name SecurityGroupEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check310="3.10"
|
||||
CHECK_TITLE_check310="[check310] Ensure a log metric filter and alarm exist for security group changes"
|
||||
CHECK_SCORED_check310="SCORED"
|
||||
CHECK_CIS_LEVEL_check310="LEVEL2"
|
||||
CHECK_SEVERITY_check310="Medium"
|
||||
CHECK_ASFF_TYPE_check310="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check310="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check310="check310"
|
||||
CHECK_SERVICENAME_check310="ec2"
|
||||
CHECK_RISK_check310='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check310='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check310='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check310='Logging and Monitoring'
|
||||
|
||||
check310(){
|
||||
check3x '\$\.eventName\s*=\s*AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*RevokeSecurityGroupIngress.+\$\.eventName\s*=\s*RevokeSecurityGroupEgress.+\$\.eventName\s*=\s*CreateSecurityGroup.+\$\.eventName\s*=\s*DeleteSecurityGroup'
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name NetworkACLConfigChanges \
|
||||
# --filter-pattern '{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }' \
|
||||
# --metric-transformations metricName=NetworkAclEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name NetworkACLConfigChangesAlarm \
|
||||
# --alarm-description "Triggered by AWS Network ACL(s) config changes." \
|
||||
# --metric-name NetworkAclEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check311="3.11"
|
||||
CHECK_TITLE_check311="[check311] Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL)"
|
||||
CHECK_SCORED_check311="SCORED"
|
||||
CHECK_CIS_LEVEL_check311="LEVEL2"
|
||||
CHECK_SEVERITY_check311="Medium"
|
||||
CHECK_ASFF_TYPE_check311="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check311="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check311="check311"
|
||||
CHECK_SERVICENAME_check311="vpc"
|
||||
CHECK_RISK_check311='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check311='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check311='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check311='Logging and Monitoring'
|
||||
|
||||
check311(){
|
||||
check3x '\$\.eventName\s*=\s*CreateNetworkAcl.+\$\.eventName\s*=\s*CreateNetworkAclEntry.+\$\.eventName\s*=\s*DeleteNetworkAcl.+\$\.eventName\s*=\s*DeleteNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclAssociation'
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name VPCGatewayConfigChanges \
|
||||
# --filter-pattern '{ ($.eventName = CreateCustomerGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = DetachInternetGateway) }' \
|
||||
# --metric-transformations metricName=GatewayEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name VPCGatewayConfigChangesAlarm \
|
||||
# --alarm-description "Triggered by VPC Customer/Internet Gateway changes." \
|
||||
# --metric-name GatewayEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check312="3.12"
|
||||
CHECK_TITLE_check312="[check312] Ensure a log metric filter and alarm exist for changes to network gateways"
|
||||
CHECK_SCORED_check312="SCORED"
|
||||
CHECK_CIS_LEVEL_check312="LEVEL1"
|
||||
CHECK_SEVERITY_check312="Medium"
|
||||
CHECK_ASFF_TYPE_check312="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check312="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check312="check312"
|
||||
CHECK_SERVICENAME_check312="vpc"
|
||||
CHECK_RISK_check312='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check312='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check312='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check312='Logging and Monitoring'
|
||||
|
||||
check312(){
|
||||
check3x '\$\.eventName\s*=\s*CreateCustomerGateway.+\$\.eventName\s*=\s*DeleteCustomerGateway.+\$\.eventName\s*=\s*AttachInternetGateway.+\$\.eventName\s*=\s*CreateInternetGateway.+\$\.eventName\s*=\s*DeleteInternetGateway.+\$\.eventName\s*=\s*DetachInternetGateway'
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name RouteTableConfigChanges \
|
||||
# --filter-pattern '{ ($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) || ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation) || ($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DisassociateRouteTable) }' \
|
||||
# --metric-transformations metricName=RouteTableEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name RouteTableConfigChangesAlarm \
|
||||
# --alarm-description "Triggered by AWS Route Table config changes." \
|
||||
# --metric-name RouteTableEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check313="3.13"
|
||||
CHECK_TITLE_check313="[check313] Ensure a log metric filter and alarm exist for route table changes"
|
||||
CHECK_SCORED_check313="SCORED"
|
||||
CHECK_CIS_LEVEL_check313="LEVEL1"
|
||||
CHECK_SEVERITY_check313="Medium"
|
||||
CHECK_ASFF_TYPE_check313="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check313="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check313="check313"
|
||||
CHECK_SERVICENAME_check313="vpc"
|
||||
CHECK_RISK_check313='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check313='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check313='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check313='Logging and Monitoring'
|
||||
|
||||
check313(){
|
||||
check3x '\$\.eventName\s*=\s*CreateRoute.+\$\.eventName\s*=\s*CreateRouteTable.+\$\.eventName\s*=\s*ReplaceRoute.+\$\.eventName\s*=\s*ReplaceRouteTableAssociation.+\$\.eventName\s*=\s*DeleteRouteTable.+\$\.eventName\s*=\s*DeleteRoute.+\$\.eventName\s*=\s*DisassociateRouteTable'
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name VPCNetworkConfigChanges \
|
||||
# --filter-pattern '{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }' \
|
||||
# --metric-transformations metricName=VpcEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name VPCNetworkConfigChangesAlarm \
|
||||
# --alarm-description "Triggered by AWS VPC(s) environment config changes." \
|
||||
# --metric-name VpcEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check314="3.14"
|
||||
CHECK_TITLE_check314="[check314] Ensure a log metric filter and alarm exist for VPC changes"
|
||||
CHECK_SCORED_check314="SCORED"
|
||||
CHECK_CIS_LEVEL_check314="LEVEL1"
|
||||
CHECK_SEVERITY_check314="Medium"
|
||||
CHECK_ASFF_TYPE_check314="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check314="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check314="check314"
|
||||
CHECK_SERVICENAME_check314="vpc"
|
||||
CHECK_RISK_check314='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check314='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check314='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check314='Logging and Monitoring'
|
||||
|
||||
check314(){
|
||||
check3x '\$\.eventName\s*=\s*CreateVpc.+\$\.eventName\s*=\s*DeleteVpc.+\$\.eventName\s*=\s*ModifyVpcAttribute.+\$\.eventName\s*=\s*AcceptVpcPeeringConnection.+\$\.eventName\s*=\s*CreateVpcPeeringConnection.+\$\.eventName\s*=\s*DeleteVpcPeeringConnection.+\$\.eventName\s*=\s*RejectVpcPeeringConnection.+\$\.eventName\s*=\s*AttachClassicLinkVpc.+\$\.eventName\s*=\s*DetachClassicLinkVpc.+\$\.eventName\s*=\s*DisableVpcClassicLink.+\$\.eventName\s*=\s*EnableVpcClassicLink'
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name ConsoleSignInWithoutMfaCount \
|
||||
# --filter-pattern '{ $.eventName = "ConsoleLogin" && $.additionalEventData.MFAUsed != "Yes" }' \
|
||||
# --metric-transformations metricName=ConsoleSignInWithoutMfaCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name ConsoleSignInWithoutMfaAlarm \
|
||||
# --alarm-description "Triggered by sign-in requests made without MFA." \
|
||||
# --metric-name ConsoleSignInWithoutMfaCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check32="3.2"
|
||||
CHECK_TITLE_check32="[check32] Ensure a log metric filter and alarm exist for Management Console sign-in without MFA"
|
||||
CHECK_SCORED_check32="SCORED"
|
||||
CHECK_CIS_LEVEL_check32="LEVEL1"
|
||||
CHECK_SEVERITY_check32="Medium"
|
||||
CHECK_ASFF_TYPE_check32="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check32="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check302="check32"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_check32="ens-op.exp.8.aws.trail.4"
|
||||
CHECK_SERVICENAME_check32="iam"
|
||||
CHECK_RISK_check32='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check32='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check32='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check32='Logging and Monitoring'
|
||||
|
||||
check32(){
|
||||
check3x '\$\.eventName\s*=\s*"ConsoleLogin".+\$\.additionalEventData\.MFAUsed\s*!=\s*"Yes"'
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name RootAccountUsage \
|
||||
# --filter-pattern '{ $.userIdentity.type = "Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType != "AwsServiceEvent" }' \
|
||||
# --metric-transformations metricName=RootAccountUsageEventCount,metricNamespace=CloudTrailMetrics,metricValue=1 \
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name RootAccountUsageAlarm \
|
||||
# --alarm-description "Triggered by AWS Root Account usage." \
|
||||
# --metric-name RootAccountUsageEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check33="3.3"
|
||||
CHECK_TITLE_check33="[check33] Ensure a log metric filter and alarm exist for usage of root account"
|
||||
CHECK_SCORED_check33="SCORED"
|
||||
CHECK_CIS_LEVEL_check33="LEVEL1"
|
||||
CHECK_SEVERITY_check33="Medium"
|
||||
CHECK_ASFF_TYPE_check33="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check33="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check303="check33"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_check33="ens-op.exp.8.aws.trail.5"
|
||||
CHECK_SERVICENAME_check33="iam"
|
||||
CHECK_RISK_check33='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check33='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check33='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check33='Logging and Monitoring'
|
||||
|
||||
check33(){
|
||||
if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
|
||||
textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
|
||||
else
|
||||
check3x '\$\.userIdentity\.type\s*=\s*"Root".+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*"AwsServiceEvent"'
|
||||
fi
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name IAMAuthConfigChanges \
|
||||
# --filter-pattern '{ ($.eventName = DeleteGroupPolicy) || ($.eventName = DeleteRolePolicy) || ($.eventName = DeleteUserPolicy) || ($.eventName = PutGroupPolicy) || ($.eventName = PutRolePolicy) || ($.eventName = PutUserPolicy) || ($.eventName = CreatePolicy) || ($.eventName = DeletePolicy) || ($.eventName = CreatePolicyVersion) || ($.eventName = DeletePolicyVersion) || ($.eventName = AttachRolePolicy) || ($.eventName = DetachRolePolicy) || ($.eventName = AttachUserPolicy) || ($.eventName = DetachUserPolicy) || ($.eventName = AttachGroupPolicy) || ($.eventName = DetachGroupPolicy) }' \
|
||||
# --metric-transformations metricName=IAMPolicyEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name IAMAuthorizationActivityAlarm \
|
||||
# --alarm-description "Triggered by AWS IAM authorization config changes." \
|
||||
# --metric-name IAMPolicyEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check34="3.4"
|
||||
CHECK_TITLE_check34="[check34] Ensure a log metric filter and alarm exist for IAM policy changes"
|
||||
CHECK_SCORED_check34="SCORED"
|
||||
CHECK_CIS_LEVEL_check34="LEVEL1"
|
||||
CHECK_SEVERITY_check34="Medium"
|
||||
CHECK_ASFF_TYPE_check34="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check34="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check304="check34"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_check34="ens-op.exp.8.aws.trail.6"
|
||||
CHECK_SERVICENAME_check34="iam"
|
||||
CHECK_RISK_check34='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check34='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check34='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check34='IAM'
|
||||
|
||||
check34(){
|
||||
check3x '\$\.eventName\s*=\s*DeleteGroupPolicy.+\$\.eventName\s*=\s*DeleteRolePolicy.+\$\.eventName\s*=\s*DeleteUserPolicy.+\$\.eventName\s*=\s*PutGroupPolicy.+\$\.eventName\s*=\s*PutRolePolicy.+\$\.eventName\s*=\s*PutUserPolicy.+\$\.eventName\s*=\s*CreatePolicy.+\$\.eventName\s*=\s*DeletePolicy.+\$\.eventName\s*=\s*CreatePolicyVersion.+\$\.eventName\s*=\s*DeletePolicyVersion.+\$\.eventName\s*=\s*AttachRolePolicy.+\$\.eventName\s*=\s*DetachRolePolicy.+\$\.eventName\s*=\s*AttachUserPolicy.+\$\.eventName\s*=\s*DetachUserPolicy.+\$\.eventName\s*=\s*AttachGroupPolicy.+\$\.eventName\s*=\s*DetachGroupPolicy'
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/MyCloudTrailLG \
|
||||
# --filter-name AWSCloudTrailChanges \
|
||||
# --filter-pattern '{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }' \
|
||||
# --metric-transformations metricName=CloudTrailEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name "CloudTrail Changes" \
|
||||
# --alarm-description "Triggered by AWS CloudTrail configuration changes." \
|
||||
# --metric-name CloudTrailEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check35="3.5"
|
||||
CHECK_TITLE_check35="[check35] Ensure a log metric filter and alarm exist for CloudTrail configuration changes"
|
||||
CHECK_SCORED_check35="SCORED"
|
||||
CHECK_CIS_LEVEL_check35="LEVEL1"
|
||||
CHECK_SEVERITY_check35="Medium"
|
||||
CHECK_ASFF_TYPE_check35="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check35="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check305="check35"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_check35="ens-op.exp.8.aws.trail.1"
|
||||
CHECK_SERVICENAME_check35="cloudtrail"
|
||||
CHECK_RISK_check35='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check35='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check35='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check35='Logging and Monitoring'
|
||||
|
||||
check35(){
|
||||
check3x '\$\.eventName\s*=\s*CreateTrail.+\$\.eventName\s*=\s*UpdateTrail.+\$\.eventName\s*=\s*DeleteTrail.+\$\.eventName\s*=\s*StartLogging.+\$\.eventName\s*=\s*StopLogging'
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/MyCloudTrailLG \
|
||||
# --filter-name AWSConsoleSignInFailures \
|
||||
# --filter-pattern '{ ($.eventName = ConsoleLogin) && ($.errorMessage = "Failed authentication") }' \
|
||||
# --metric-transformations metricName=ConsoleSigninFailureCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name "Console Sign-in Failures" \
|
||||
# --alarm-description "AWS Management Console Sign-in Failure Alarm." \
|
||||
# --metric-name ConsoleSigninFailureCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 3 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check36="3.6"
|
||||
CHECK_TITLE_check36="[check36] Ensure a log metric filter and alarm exist for AWS Management Console authentication failures"
|
||||
CHECK_SCORED_check36="SCORED"
|
||||
CHECK_CIS_LEVEL_check36="LEVEL2"
|
||||
CHECK_SEVERITY_check36="Medium"
|
||||
CHECK_ASFF_TYPE_check36="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check36="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check306="check36"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_check36="ens-op.exp.8.aws.trail.3"
|
||||
CHECK_SERVICENAME_check36="iam"
|
||||
CHECK_RISK_check36='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check36='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check36='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check36='Logging and Monitoring'
|
||||
|
||||
check36(){
|
||||
check3x '\$\.eventName\s*=\s*ConsoleLogin.+\$\.errorMessage\s*=\s*"Failed authentication"'
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name AWSCMKChanges \
|
||||
# --filter-pattern '{ ($.eventSource = kms.amazonaws.com) && (($.eventName = DisableKey) || ($.eventName = ScheduleKeyDeletion)) }' \
|
||||
# --metric-transformations metricName=CMKEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name AWSCMKChangesAlarm \
|
||||
# --alarm-description "Triggered by AWS CMK changes." \
|
||||
# --metric-name CMKEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check37="3.7"
|
||||
CHECK_TITLE_check37="[check37] Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created KMS CMKs"
|
||||
CHECK_SCORED_check37="SCORED"
|
||||
CHECK_CIS_LEVEL_check37="LEVEL2"
|
||||
CHECK_SEVERITY_check37="Medium"
|
||||
CHECK_ASFF_TYPE_check37="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check37="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check307="check37"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_check37="ens-op.exp.11.aws.kms.1"
|
||||
CHECK_SERVICENAME_check37="kms"
|
||||
CHECK_RISK_check37='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check37='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check37='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check37='Logging and Monitoring'
|
||||
|
||||
check37(){
|
||||
check3x '\$\.eventSource\s*=\s*kms.amazonaws.com.+\$\.eventName\s*=\s*DisableKey.+\$\.eventName\s*=\s*ScheduleKeyDeletion'
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name S3BucketConfigChanges \
|
||||
# --filter-pattern '{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) || ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }' \
|
||||
# --metric-transformations metricName=S3BucketEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name S3BucketConfigChangesAlarm \
|
||||
# --alarm-description "Triggered by AWS S3 Bucket config changes." \
|
||||
# --metric-name S3BucketEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check38="3.8"
|
||||
CHECK_TITLE_check38="[check38] Ensure a log metric filter and alarm exist for S3 bucket policy changes"
|
||||
CHECK_SCORED_check38="SCORED"
|
||||
CHECK_CIS_LEVEL_check38="LEVEL1"
|
||||
CHECK_SEVERITY_check38="Medium"
|
||||
CHECK_ASFF_TYPE_check38="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check38="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check308="check38"
|
||||
CHECK_SERVICENAME_check38="s3"
|
||||
CHECK_RISK_check38='Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.'
|
||||
CHECK_REMEDIATION_check38='It is recommended that a metric filter and alarm be established for unauthorized requests.'
|
||||
CHECK_DOC_check38='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check38='Logging and Monitoring'
|
||||
|
||||
check38(){
|
||||
check3x '\$\.eventSource\s*=\s*s3.amazonaws.com.+\$\.eventName\s*=\s*PutBucketAcl.+\$\.eventName\s*=\s*PutBucketPolicy.+\$\.eventName\s*=\s*PutBucketCors.+\$\.eventName\s*=\s*PutBucketLifecycle.+\$\.eventName\s*=\s*PutBucketReplication.+\$\.eventName\s*=\s*DeleteBucketPolicy.+\$\.eventName\s*=\s*DeleteBucketCors.+\$\.eventName\s*=\s*DeleteBucketLifecycle.+\$\.eventName\s*=\s*DeleteBucketReplication'
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
# Remediation:
|
||||
#
|
||||
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||||
#
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/CloudWatchLogGroup \
|
||||
# --filter-name AWSConfigChanges \
|
||||
# --filter-pattern '{ ($.eventSource = config.amazonaws.com) && (($.eventName = StopConfigurationRecorder)||($.eventName = DeleteDeliveryChannel)||($.eventName = PutDeliveryChannel)||($.eventName = PutConfigurationRecorder)) }' \
|
||||
# --metric-transformations metricName=ConfigEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
#
|
||||
# aws cloudwatch put-metric-alarm \
|
||||
# --region us-east-1 \
|
||||
# --alarm-name AWSConfigChangesAlarm \
|
||||
# --alarm-description "Triggered by AWS Config changes." \
|
||||
# --metric-name ConfigEventCount \
|
||||
# --namespace CloudTrailMetrics \
|
||||
# --statistic Sum \
|
||||
# --comparison-operator GreaterThanOrEqualToThreshold \
|
||||
# --evaluation-periods 1 \
|
||||
# --period 300 \
|
||||
# --threshold 1 \
|
||||
# --actions-enabled \
|
||||
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
|
||||
|
||||
CHECK_ID_check39="3.9"
|
||||
CHECK_TITLE_check39="[check39] Ensure a log metric filter and alarm exist for AWS Config configuration changes"
|
||||
CHECK_SCORED_check39="SCORED"
|
||||
CHECK_CIS_LEVEL_check39="LEVEL2"
|
||||
CHECK_SEVERITY_check39="Medium"
|
||||
CHECK_ASFF_TYPE_check39="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check39="AwsCloudTrailTrail"
|
||||
CHECK_ALTERNATE_check309="check39"
|
||||
CHECK_SERVICENAME_check39="config"
|
||||
CHECK_RISK_check39='If not enabled important changes to accounts could go unnoticed or difficult to find.'
|
||||
CHECK_REMEDIATION_check39='Use this service as a complement to implement detective controls that cannot be prevented. (e.g. a Security Group is modified to open to internet without restrictions or route changed to avoid going thru the network firewall). Ensure AWS Config is enabled in all regions in order to detect any not intended action. On the other hand if sufficient preventive controls to make changes in critical services are in place; the rating on this finding can be lowered or discarded depending on residual risk.'
|
||||
CHECK_DOC_check39='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
|
||||
CHECK_CAF_EPIC_check39='Logging and Monitoring'
|
||||
|
||||
check39(){
|
||||
check3x '\$\.eventSource\s*=\s*config.amazonaws.com.+\$\.eventName\s*=\s*StopConfigurationRecorder.+\$\.eventName\s*=\s*DeleteDeliveryChannel.+\$\.eventName\s*=\s*PutDeliveryChannel.+\$\.eventName\s*=\s*PutConfigurationRecorder'
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra7144="7.144"
|
||||
CHECK_TITLE_extra7144="[extra7144] Check if CloudWatch has allowed cross-account sharing"
|
||||
CHECK_SCORED_extra7144="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7144="EXTRA"
|
||||
CHECK_SEVERITY_extra7144="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7144="AwsCloudWatch"
|
||||
CHECK_ALTERNATE_check7144="extra7144"
|
||||
CHECK_SERVICENAME_extra7144="cloudwatch"
|
||||
CHECK_RISK_extra7144='Cross-Account access to CloudWatch could increase the risk of compromising information between accounts'
|
||||
CHECK_REMEDIATION_extra7144='Grant usage permission on a per-resource basis to enforce least privilege and Zero Trust principles'
|
||||
CHECK_DOC_extra7144='https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Cross-Account-Cross-Region.html'
|
||||
CHECK_CAF_EPIC_extra7144='Logging and Monitoring'
|
||||
|
||||
extra7144(){
|
||||
# "Check if aws cloudwatch has allowed sharing with other accounts (Not Scored) (Not part of CIS benchmark)"
|
||||
CLOUDWATCH_CROSS_ACCOUNT_ROLE=$($AWSCLI iam get-role $PROFILE_OPT --role-name=CloudWatch-CrossAccountSharingRole --output json --query Role 2>&1)
|
||||
if [[ $CLOUDWATCH_CROSS_ACCOUNT_ROLE != *NoSuchEntity* ]]; then
|
||||
CLOUDWATCH_POLICY_BAD_STATEMENTS=$(echo $CLOUDWATCH_CROSS_ACCOUNT_ROLE | jq '.AssumeRolePolicyDocument')
|
||||
textInfo "$REGION: CloudWatch has allowed cross-account sharing" "$REGION"
|
||||
else
|
||||
textPass "$REGION: CloudWatch doesn't allows cross-account sharing" "$REGION"
|
||||
fi
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra7162="7.162"
|
||||
CHECK_TITLE_extra7162="[extra7162] Check if CloudWatch Log Groups have a retention policy of 365 days"
|
||||
CHECK_SCORED_extra7162="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7162="EXTRA"
|
||||
CHECK_SEVERITY_extra7162="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7162="AwsLogsLogGroup"
|
||||
CHECK_ALTERNATE_check7162="extra7162"
|
||||
CHECK_SERVICENAME_extra7162="cloudwatch"
|
||||
CHECK_RISK_extra7162='If log groups have a low retention policy of less than 365 days; crucial logs and data can be lost'
|
||||
CHECK_REMEDIATION_extra7162='Add Log Retention policy of 365 days to log groups. This will persist logs and traces for a long time.'
|
||||
CHECK_DOC_extra7162='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_Logs.html'
|
||||
CHECK_CAF_EPIC_extra7162='Data Retention'
|
||||
|
||||
extra7162() {
|
||||
# "Check if CloudWatch Log Groups have a retention policy of 365 days"
|
||||
declare -i LOG_GROUP_RETENTION_PERIOD_DAYS=365
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_365_RETENTION_LOG_GROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --query 'logGroups[?retentionInDays=="${LOG_GROUP_RETENTION_PERIOD_DAYS}"].[logGroupName]' --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_365_RETENTION_LOG_GROUPS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe log groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_365_RETENTION_LOG_GROUPS ]]; then
|
||||
for log in $LIST_OF_365_RETENTION_LOG_GROUPS; do
|
||||
textPass "$regx: $log Log Group has 365 days retention period!" "$regx" "$log"
|
||||
done
|
||||
fi
|
||||
LIST_OF_NON_365_RETENTION_LOG_GROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --query 'logGroups[?retentionInDays!="${LOG_GROUP_RETENTION_PERIOD_DAYS}"].[logGroupName]' --output text)
|
||||
if [[ $LIST_OF_NON_365_RETENTION_LOG_GROUPS ]]; then
|
||||
for log in $LIST_OF_NON_365_RETENTION_LOG_GROUPS; do
|
||||
textFail "$regx: $log Log Group does not have 365 days retention period!" "$regx" "$log"
|
||||
done
|
||||
fi
|
||||
REGION_NO_LOG_GROUP=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --output text)
|
||||
if [[ $REGION_NO_LOG_GROUP ]]; then
|
||||
:
|
||||
else
|
||||
textInfo "$regx does not have a Log Group!" "$regx"
|
||||
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
|
||||
# Remediation:
|
||||
#
|
||||
# https://docs.aws.amazon.com/cli/latest/reference/logs/associate-kms-key.html
|
||||
# associate-kms-key
|
||||
# --log-group-name <value>
|
||||
# --kms-key-id <value>
|
||||
# [--cli-input-json <value>]
|
||||
# [--generate-cli-skeleton <value>]
|
||||
|
||||
CHECK_ID_extra7164="7.164"
|
||||
CHECK_TITLE_extra7164="[extra7164] Check if CloudWatch log groups are protected by AWS KMS "
|
||||
CHECK_SCORED_extra7164="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7164="EXTRA"
|
||||
CHECK_SEVERITY_extra7164="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7164="Logs"
|
||||
CHECK_ALTERNATE_extra7164="extra7164"
|
||||
CHECK_SERVICENAME_extra7164="cloudwatch"
|
||||
CHECK_RISK_extra7164="Using customer managed KMS to encrypt CloudWatch log group provide additional confidentiality and control over the log data"
|
||||
CHECK_REMEDIATION_extra7164="Associate KMS Key with Cloudwatch log group."
|
||||
CHECK_DOC_extra7164="https://docs.aws.amazon.com/cli/latest/reference/logs/associate-kms-key.html"
|
||||
CHECK_CAF_EPIC_extra7164="Data Protection"
|
||||
|
||||
extra7164(){
|
||||
# "Check if Cloudwatch log groups are associated with AWS KMS"
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_LOGGROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --output json 2>&1 )
|
||||
if [[ $(echo "$LIST_OF_LOGGROUPS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe log groups" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_LOGGROUPS ]]; then
|
||||
LIST_OF_LOGGROUPS_WITHOUT_KMS=$(echo "${LIST_OF_LOGGROUPS}" | jq '.logGroups[]' | jq '. | select( has("kmsKeyId") == false )' | jq -r '.logGroupName')
|
||||
LIST_OF_LOGGROUPS_WITH_KMS=$(echo "${LIST_OF_LOGGROUPS}" | jq '.logGroups[]' | jq '. | select( has("kmsKeyId") == true )' | jq -r '.logGroupName')
|
||||
if [[ $LIST_OF_LOGGROUPS_WITHOUT_KMS ]]; then
|
||||
for loggroup in $LIST_OF_LOGGROUPS_WITHOUT_KMS; do
|
||||
textFail "$regx: ${loggroup} does not have AWS KMS keys associated." "$regx" "${loggroup}"
|
||||
done
|
||||
fi
|
||||
if [[ $LIST_OF_LOGGROUPS_WITH_KMS ]]; then
|
||||
for loggroup in $LIST_OF_LOGGROUPS_WITH_KMS; do
|
||||
textPass "$regx: ${loggroup} does have AWS KMS keys associated." "$regx" "${loggroup}"
|
||||
done
|
||||
fi
|
||||
else
|
||||
textPass "$regx: No Cloudwatch log groups found." "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_changes_to_network_acls_alarm_configured",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL).",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL).",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_11#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_11#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_changes_to_network_acls_alarm_configured(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*CreateNetworkAcl.+\$\.eventName\s*=\s*CreateNetworkAclEntry.+\$\.eventName\s*=\s*DeleteNetworkAcl.+\$\.eventName\s*=\s*DeleteNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclAssociation"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured import (
|
||||
cloudwatch_changes_to_network_acls_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_acls_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured import (
|
||||
cloudwatch_changes_to_network_acls_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_acls_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured import (
|
||||
cloudwatch_changes_to_network_acls_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_acls_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured import (
|
||||
cloudwatch_changes_to_network_acls_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_acls_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured import (
|
||||
cloudwatch_changes_to_network_acls_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_acls_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_changes_to_network_gateways_alarm_configured",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for changes to network gateways.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for changes to network gateways.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_12#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_12#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_changes_to_network_gateways_alarm_configured(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*CreateCustomerGateway.+\$\.eventName\s*=\s*DeleteCustomerGateway.+\$\.eventName\s*=\s*AttachInternetGateway.+\$\.eventName\s*=\s*CreateInternetGateway.+\$\.eventName\s*=\s*DeleteInternetGateway.+\$\.eventName\s*=\s*DetachInternetGateway"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured import (
|
||||
cloudwatch_changes_to_network_gateways_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_gateways_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured import (
|
||||
cloudwatch_changes_to_network_gateways_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_gateways_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured import (
|
||||
cloudwatch_changes_to_network_gateways_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_gateways_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = CreateCustomerGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = DetachInternetGateway) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured import (
|
||||
cloudwatch_changes_to_network_gateways_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_gateways_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = CreateCustomerGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = DetachInternetGateway) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured import (
|
||||
cloudwatch_changes_to_network_gateways_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_gateways_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_changes_to_network_route_tables_alarm_configured",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for route table changes.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for route table changes.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_13#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_13#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_changes_to_network_route_tables_alarm_configured(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*CreateRoute.+\$\.eventName\s*=\s*CreateRouteTable.+\$\.eventName\s*=\s*ReplaceRoute.+\$\.eventName\s*=\s*ReplaceRouteTableAssociation.+\$\.eventName\s*=\s*DeleteRouteTable.+\$\.eventName\s*=\s*DeleteRoute.+\$\.eventName\s*=\s*DisassociateRouteTable"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured import (
|
||||
cloudwatch_changes_to_network_route_tables_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_route_tables_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured import (
|
||||
cloudwatch_changes_to_network_route_tables_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_route_tables_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured import (
|
||||
cloudwatch_changes_to_network_route_tables_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_route_tables_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) || ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation)|| ($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DisassociateRouteTable) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured import (
|
||||
cloudwatch_changes_to_network_route_tables_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_route_tables_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) || ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation)|| ($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DisassociateRouteTable) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured import (
|
||||
cloudwatch_changes_to_network_route_tables_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_network_route_tables_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_changes_to_vpcs_alarm_configured",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for VPC changes.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for VPC changes.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_14#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_14#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_changes_to_vpcs_alarm_configured(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*CreateVpc.+\$\.eventName\s*=\s*DeleteVpc.+\$\.eventName\s*=\s*ModifyVpcAttribute.+\$\.eventName\s*=\s*AcceptVpcPeeringConnection.+\$\.eventName\s*=\s*CreateVpcPeeringConnection.+\$\.eventName\s*=\s*DeleteVpcPeeringConnection.+\$\.eventName\s*=\s*RejectVpcPeeringConnection.+\$\.eventName\s*=\s*AttachClassicLinkVpc.+\$\.eventName\s*=\s*DetachClassicLinkVpc.+\$\.eventName\s*=\s*DisableVpcClassicLink.+\$\.eventName\s*=\s*EnableVpcClassicLink"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured import (
|
||||
cloudwatch_changes_to_vpcs_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_vpcs_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured import (
|
||||
cloudwatch_changes_to_vpcs_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_vpcs_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured import (
|
||||
cloudwatch_changes_to_vpcs_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_vpcs_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured import (
|
||||
cloudwatch_changes_to_vpcs_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_vpcs_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured import (
|
||||
cloudwatch_changes_to_vpcs_alarm_configured,
|
||||
)
|
||||
|
||||
check = cloudwatch_changes_to_vpcs_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
4
providers/aws/services/cloudwatch/cloudwatch_client.py
Normal file
4
providers/aws/services/cloudwatch/cloudwatch_client.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import CloudWatch
|
||||
|
||||
cloudwatch_client = CloudWatch(current_audit_info)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_cross_account_sharing_disabled",
|
||||
"CheckTitle": "Check if CloudWatch has allowed cross-account sharing.",
|
||||
"CheckType": ["Logging and Monitoring"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudWatch",
|
||||
"Description": "Check if CloudWatch has allowed cross-account sharing.",
|
||||
"Risk": "Cross-Account access to CloudWatch could increase the risk of compromising information between accounts.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Cross-Account-Cross-Region.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Grant usage permission on a per-resource basis to enforce least privilege and Zero Trust principles.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Cross-Account-Cross-Region.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
class cloudwatch_cross_account_sharing_disabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "PASS"
|
||||
report.status_extended = "CloudWatch doesn't allows cross-account sharing"
|
||||
report.resource_id = "CloudWatch-CrossAccountSharingRole"
|
||||
report.region = iam_client.region
|
||||
for role in iam_client.roles:
|
||||
if role["RoleName"] == "CloudWatch-CrossAccountSharingRole":
|
||||
report.resource_arn = role["Arn"]
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "CloudWatch has allowed cross-account sharing."
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,68 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_iam
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_cross_account_sharing_disabled:
|
||||
@mock_iam
|
||||
def test_cloudwatch_without_cross_account_role(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled import (
|
||||
cloudwatch_cross_account_sharing_disabled,
|
||||
)
|
||||
|
||||
check = cloudwatch_cross_account_sharing_disabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "CloudWatch doesn't allows cross-account sharing"
|
||||
)
|
||||
assert result[0].resource_id == "CloudWatch-CrossAccountSharingRole"
|
||||
|
||||
@mock_iam
|
||||
def test_cloudwatch_log_group_with_cross_account_role(self):
|
||||
# Generate Logs Client
|
||||
iam_client = client("iam", region_name=AWS_REGION)
|
||||
# Request Logs group
|
||||
iam_client.create_role(
|
||||
RoleName="CloudWatch-CrossAccountSharingRole", AssumeRolePolicyDocument="{}"
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled import (
|
||||
cloudwatch_cross_account_sharing_disabled,
|
||||
)
|
||||
|
||||
check = cloudwatch_cross_account_sharing_disabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "CloudWatch has allowed cross-account sharing."
|
||||
)
|
||||
assert result[0].resource_id == "CloudWatch-CrossAccountSharingRole"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_group_kms_encryption_enabled",
|
||||
"CheckTitle": "Check if CloudWatch log groups are protected by AWS KMS.",
|
||||
"CheckType": ["Data Protection"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "logs",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsLogsLogGroup",
|
||||
"Description": "Check if CloudWatch log groups are protected by AWS KMS.",
|
||||
"Risk": "Using customer managed KMS to encrypt CloudWatch log group provide additional confidentiality and control over the log data.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/cli/latest/reference/logs/associate-kms-key.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "associate-kms-key --log-group-name <value> --kms-key-id <value>",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.bridgecrew.io/docs/logging_21#aws-console",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Associate KMS Key with Cloudwatch log group.",
|
||||
"Url": "https://docs.aws.amazon.com/cli/latest/reference/logs/associate-kms-key.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_group_kms_encryption_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for log_group in logs_client.log_groups:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = log_group.region
|
||||
report.resource_id = log_group.name
|
||||
report.resource_arn = log_group.arn
|
||||
if log_group.kms_id:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Log Group {log_group.name} does not have AWS KMS keys associated."
|
||||
)
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,92 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_logs
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_group_kms_encryption_enabled:
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled import (
|
||||
cloudwatch_log_group_kms_encryption_enabled,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_group_kms_encryption_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_logs
|
||||
def test_cloudwatch_log_group_without_kms_key(self):
|
||||
# Generate Logs Client
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
# Request Logs group
|
||||
logs_client.create_log_group(
|
||||
logGroupName="test",
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled import (
|
||||
cloudwatch_log_group_kms_encryption_enabled,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_group_kms_encryption_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Log Group test does not have AWS KMS keys associated."
|
||||
)
|
||||
assert result[0].resource_id == "test"
|
||||
|
||||
@mock_logs
|
||||
def test_cloudwatch_log_group_with_kms_key(self):
|
||||
# Generate Logs Client
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
# Request Logs group
|
||||
logs_client.create_log_group(logGroupName="test", kmsKeyId="test_kms_id")
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled import (
|
||||
cloudwatch_log_group_kms_encryption_enabled,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_group_kms_encryption_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Log Group test does have AWS KMS key test_kms_id associated."
|
||||
)
|
||||
assert result[0].resource_id == "test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_group_retention_policy_specific_days_enabled",
|
||||
"CheckTitle": "Check if CloudWatch Log Groups have a retention policy of specific days.",
|
||||
"CheckType": ["Data Retention"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "logs",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsLogsLogGroup",
|
||||
"Description": "Check if CloudWatch Log Groups have a retention policy of specific days.",
|
||||
"Risk": "If log groups have a low retention policy of less than specific days, crucial logs and data can be lost.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_Logs.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/logging_13#cli-command",
|
||||
"NativeIaC": "https://docs.bridgecrew.io/docs/logging_13#cloudformation",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/logging_13#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Add Log Retention policy of specific days to log groups. This will persist logs and traces for a long time.",
|
||||
"Url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_Logs.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
from config.config import get_config_var
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_group_retention_policy_specific_days_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
specific_retention_days = get_config_var("log_group_retention_days")
|
||||
for log_group in logs_client.log_groups:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = log_group.region
|
||||
report.resource_id = log_group.name
|
||||
report.resource_arn = log_group.arn
|
||||
if log_group.retention_days < specific_retention_days:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Log Group {log_group.name} has less than {specific_retention_days} days retention period ({log_group.retention_days} days)."
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it has {log_group.retention_days} days."
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,129 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_logs
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_group_retention_policy_specific_days_enabled:
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled import (
|
||||
cloudwatch_log_group_retention_policy_specific_days_enabled,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_group_retention_policy_specific_days_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_logs
|
||||
def test_cloudwatch_log_group_without_retention_days(self):
|
||||
# Generate Logs Client
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
# Request Logs group
|
||||
logs_client.create_log_group(
|
||||
logGroupName="test",
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled import (
|
||||
cloudwatch_log_group_retention_policy_specific_days_enabled,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_group_retention_policy_specific_days_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Log Group test has less than 365 days retention period (0 days)."
|
||||
)
|
||||
assert result[0].resource_id == "test"
|
||||
|
||||
@mock_logs
|
||||
def test_cloudwatch_log_group_with_compliant_retention_days(self):
|
||||
# Generate Logs Client
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
# Request Logs group
|
||||
logs_client.create_log_group(
|
||||
logGroupName="test",
|
||||
)
|
||||
logs_client.put_retention_policy(logGroupName="test", retentionInDays=400)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled import (
|
||||
cloudwatch_log_group_retention_policy_specific_days_enabled,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_group_retention_policy_specific_days_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Log Group test comply with 365 days retention period since it has 400 days."
|
||||
)
|
||||
assert result[0].resource_id == "test"
|
||||
|
||||
@mock_logs
|
||||
def test_cloudwatch_log_group_with_no_compliant_retention_days(self):
|
||||
# Generate Logs Client
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
# Request Logs group
|
||||
logs_client.create_log_group(
|
||||
logGroupName="test",
|
||||
)
|
||||
logs_client.put_retention_policy(logGroupName="test", retentionInDays=7)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled import (
|
||||
cloudwatch_log_group_retention_policy_specific_days_enabled,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_group_retention_policy_specific_days_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Log Group test has less than 365 days retention period (7 days)."
|
||||
)
|
||||
assert result[0].resource_id == "test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for AWS Config configuration changes.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for AWS Config configuration changes.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_9#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_9#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled(
|
||||
Check
|
||||
):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventSource\s*=\s*config.amazonaws.com.+\$\.eventName\s*=\s*StopConfigurationRecorder.+\$\.eventName\s*=\s*DeleteDeliveryChannel.+\$\.eventName\s*=\s*PutDeliveryChannel.+\$\.eventName\s*=\s*PutConfigurationRecorder"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,292 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventSource = config.amazonaws.com) && (($.eventName=StopConfigurationRecorder)||($.eventName=DeleteDeliveryChannel)|| ($.eventName=PutDeliveryChannel)||($.eventName=PutConfigurationRecorder))}",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventSource = config.amazonaws.com) && (($.eventName=StopConfigurationRecorder)||($.eventName=DeleteDeliveryChannel)|| ($.eventName=PutDeliveryChannel)||($.eventName=PutConfigurationRecorder))}",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for CloudTrail configuration changes.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for CloudTrail configuration changes.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_5#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_5#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled(
|
||||
Check
|
||||
):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*CreateTrail.+\$\.eventName\s*=\s*UpdateTrail.+\$\.eventName\s*=\s*DeleteTrail.+\$\.eventName\s*=\s*StartLogging.+\$\.eventName\s*=\s*StopLogging"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,292 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging)}",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging)}",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_authentication_failures",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for AWS Management Console authentication failures.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for AWS Management Console authentication failures.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_6#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_6#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_authentication_failures(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*ConsoleLogin.+\$\.errorMessage\s*=\s*Failed authentication"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures import (
|
||||
cloudwatch_log_metric_filter_authentication_failures,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_authentication_failures()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures import (
|
||||
cloudwatch_log_metric_filter_authentication_failures,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_authentication_failures()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures import (
|
||||
cloudwatch_log_metric_filter_authentication_failures,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_authentication_failures()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = ConsoleLogin) && ($.errorMessage = Failed authentication)}",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures import (
|
||||
cloudwatch_log_metric_filter_authentication_failures,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_authentication_failures()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = ConsoleLogin) && ($.errorMessage = Failed authentication)}",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures import (
|
||||
cloudwatch_log_metric_filter_authentication_failures,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_authentication_failures()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created KMS CMKs.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created KMS CMKs.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_7#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_7#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventSource\s*=\s*kms.amazonaws.com.+\$\.eventName\s*=\s*DisableKey.+\$\.eventName\s*=\s*ScheduleKeyDeletion"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,292 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk import (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk import (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk import (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventSource = kms.amazonaws.com) &&(($.eventName=DisableKey)||($.eventName=ScheduleKeyDeletion)) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk import (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventSource = kms.amazonaws.com) &&(($.eventName=DisableKey)||($.eventName=ScheduleKeyDeletion)) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk import (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for S3 bucket policy changes.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for S3 bucket policy changes.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_8#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_8#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_for_s3_bucket_policy_changes(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventSource\s*=\s*s3.amazonaws.com.+\$\.eventName\s*=\s*PutBucketAcl.+\$\.eventName\s*=\s*PutBucketPolicy.+\$\.eventName\s*=\s*PutBucketCors.+\$\.eventName\s*=\s*PutBucketLifecycle.+\$\.eventName\s*=\s*PutBucketReplication.+\$\.eventName\s*=\s*DeleteBucketPolicy.+\$\.eventName\s*=\s*DeleteBucketCors.+\$\.eventName\s*=\s*DeleteBucketLifecycle.+\$\.eventName\s*=\s*DeleteBucketReplication"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes import (
|
||||
cloudwatch_log_metric_filter_for_s3_bucket_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_for_s3_bucket_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes import (
|
||||
cloudwatch_log_metric_filter_for_s3_bucket_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_for_s3_bucket_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes import (
|
||||
cloudwatch_log_metric_filter_for_s3_bucket_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_for_s3_bucket_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) || ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes import (
|
||||
cloudwatch_log_metric_filter_for_s3_bucket_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_for_s3_bucket_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) || ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes import (
|
||||
cloudwatch_log_metric_filter_for_s3_bucket_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_for_s3_bucket_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_policy_changes",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for IAM policy changes.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for IAM policy changes.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_4#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_4#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_policy_changes(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*DeleteGroupPolicy.+\$\.eventName\s*=\s*DeleteRolePolicy.+\$\.eventName\s*=\s*DeleteUserPolicy.+\$\.eventName\s*=\s*PutGroupPolicy.+\$\.eventName\s*=\s*PutRolePolicy.+\$\.eventName\s*=\s*PutUserPolicy.+\$\.eventName\s*=\s*CreatePolicy.+\$\.eventName\s*=\s*DeletePolicy.+\$\.eventName\s*=\s*CreatePolicyVersion.+\$\.eventName\s*=\s*DeletePolicyVersion.+\$\.eventName\s*=\s*AttachRolePolicy.+\$\.eventName\s*=\s*DetachRolePolicy.+\$\.eventName\s*=\s*AttachUserPolicy.+\$\.eventName\s*=\s*DetachUserPolicy.+\$\.eventName\s*=\s*AttachGroupPolicy.+\$\.eventName\s*=\s*DetachGroupPolicy"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes import (
|
||||
cloudwatch_log_metric_filter_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes import (
|
||||
cloudwatch_log_metric_filter_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes import (
|
||||
cloudwatch_log_metric_filter_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName=DeleteGroupPolicy)||($.eventName=DeleteRolePolicy)||($.eventName=DeleteUserPolicy)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=PutUserPolicy)||($.eventName=CreatePolicy)||($.eventName=DeletePolicy)||($.eventName=CreatePolicyVersion)||($.eventName=DeletePolicyVersion)||($.eventName=AttachRolePolicy)||($.eventName=DetachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=DetachUserPolicy)||($.eventName=AttachGroupPolicy)||($.eventName=DetachGroupPolicy)}",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes import (
|
||||
cloudwatch_log_metric_filter_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName=DeleteGroupPolicy)||($.eventName=DeleteRolePolicy)||($.eventName=DeleteUserPolicy)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=PutUserPolicy)||($.eventName=CreatePolicy)||($.eventName=DeletePolicy)||($.eventName=CreatePolicyVersion)||($.eventName=DeletePolicyVersion)||($.eventName=AttachRolePolicy)||($.eventName=DetachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=DetachUserPolicy)||($.eventName=AttachGroupPolicy)||($.eventName=DetachGroupPolicy)}",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes import (
|
||||
cloudwatch_log_metric_filter_policy_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_policy_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_root_usage",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for usage of root account.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for usage of root account.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_3#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_3#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_root_usage(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.userIdentity\.type\s*=\s*Root.+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*AwsServiceEvent"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage import (
|
||||
cloudwatch_log_metric_filter_root_usage,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_root_usage()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage import (
|
||||
cloudwatch_log_metric_filter_root_usage,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_root_usage()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage import (
|
||||
cloudwatch_log_metric_filter_root_usage,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_root_usage()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ $.userIdentity.type = Root && $.userIdentity.invokedBy NOT EXISTS && $.eventType != AwsServiceEvent }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage import (
|
||||
cloudwatch_log_metric_filter_root_usage,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_root_usage()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ $.userIdentity.type = Root && $.userIdentity.invokedBy NOT EXISTS && $.eventType != AwsServiceEvent }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_root_usage.cloudwatch_log_metric_filter_root_usage import (
|
||||
cloudwatch_log_metric_filter_root_usage,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_root_usage()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_security_group_changes",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for security group changes.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for security group changes.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_10#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_10#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_security_group_changes(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*RevokeSecurityGroupIngress.+\$\.eventName\s*=\s*RevokeSecurityGroupEgress.+\$\.eventName\s*=\s*CreateSecurityGroup.+\$\.eventName\s*=\s*DeleteSecurityGroup"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes import (
|
||||
cloudwatch_log_metric_filter_security_group_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_security_group_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes import (
|
||||
cloudwatch_log_metric_filter_security_group_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_security_group_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes import (
|
||||
cloudwatch_log_metric_filter_security_group_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_security_group_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes import (
|
||||
cloudwatch_log_metric_filter_security_group_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_security_group_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes import (
|
||||
cloudwatch_log_metric_filter_security_group_changes,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_security_group_changes()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_sign_in_without_mfa",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for Management Console sign-in without MFA.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for Management Console sign-in without MFA.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_2#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_2#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_sign_in_without_mfa(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*ConsoleLogin.+\$\.additionalEventData\.MFAUsed\s*!=\s*Yes"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa import (
|
||||
cloudwatch_log_metric_filter_sign_in_without_mfa,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_sign_in_without_mfa()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa import (
|
||||
cloudwatch_log_metric_filter_sign_in_without_mfa,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_sign_in_without_mfa()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa import (
|
||||
cloudwatch_log_metric_filter_sign_in_without_mfa,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_sign_in_without_mfa()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ ($.eventName = ConsoleLogin) && ($.additionalEventData.MFAUsed != Yes) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa import (
|
||||
cloudwatch_log_metric_filter_sign_in_without_mfa,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_sign_in_without_mfa()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ ($.eventName = ConsoleLogin) && ($.additionalEventData.MFAUsed != Yes) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa import (
|
||||
cloudwatch_log_metric_filter_sign_in_without_mfa,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_sign_in_without_mfa()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "cloudwatch_log_metric_filter_unauthorized_api_calls",
|
||||
"CheckTitle": "Ensure a log metric filter and alarm exist for unauthorized API calls.",
|
||||
"CheckType": ["Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"],
|
||||
"ServiceName": "cloudwatch",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:cloudwatch:region:account-id:certificate/resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"Description": "Ensure a log metric filter and alarm exist for unauthorized API calls.",
|
||||
"Risk": "Monitoring unauthorized API calls will help reveal application errors and may reduce time to detect malicious activity.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "https://docs.bridgecrew.io/docs/monitoring_1#procedure",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/monitoring_1#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended that a metric filter and alarm be established for unauthorized requests.",
|
||||
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Logging and Monitoring",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
|
||||
from providers.aws.services.cloudwatch.cloudwatch_client import cloudwatch_client
|
||||
from providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
|
||||
|
||||
class cloudwatch_log_metric_filter_unauthorized_api_calls(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.errorCode\s*=\s*\*UnauthorizedOperation.+\$\.errorCode\s*=\s*AccessDenied\*"
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = "us-east-1"
|
||||
report.resource_id = ""
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in cloudtrail_client.trails:
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in logs_client.metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
if re.search(pattern, metric_filter.pattern):
|
||||
report.resource_id = metric_filter.log_group
|
||||
report.region = metric_filter.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
|
||||
# 3. Check if there is an alarm for the metric
|
||||
for alarm in cloudwatch_client.metric_alarms:
|
||||
if alarm.metric == metric_filter.metric:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,282 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_cloudtrail, mock_cloudwatch, mock_logs, mock_s3
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_cloudwatch_log_metric_filter_unauthorized_api_calls:
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
def test_cloudwatch_no_log_groups(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls import (
|
||||
cloudwatch_log_metric_filter_unauthorized_api_calls,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_unauthorized_api_calls()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_no_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
cloudtrail_client.create_trail(Name="test_trail", S3BucketName="test")
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls import (
|
||||
cloudwatch_log_metric_filter_unauthorized_api_calls,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_unauthorized_api_calls()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls import (
|
||||
cloudwatch_log_metric_filter_unauthorized_api_calls,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_unauthorized_api_calls()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == ""
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ ($.errorCode = *UnauthorizedOperation) || ($.errorCode = AccessDenied*) || ($.sourceIPAddress!=delivery.logs.amazonaws.com) || ($.eventName!=HeadBucket) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls import (
|
||||
cloudwatch_log_metric_filter_unauthorized_api_calls,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_unauthorized_api_calls()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter but no alarms associated."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
|
||||
@mock_logs
|
||||
@mock_cloudtrail
|
||||
@mock_cloudwatch
|
||||
@mock_s3
|
||||
def test_cloudwatch_trail_with_log_group_with_metric_and_alarm(self):
|
||||
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION)
|
||||
logs_client = client("logs", region_name=AWS_REGION)
|
||||
s3_client = client("s3", region_name=AWS_REGION)
|
||||
s3_client.create_bucket(Bucket="test")
|
||||
logs_client.create_log_group(logGroupName="/log-group/test")
|
||||
cloudtrail_client.create_trail(
|
||||
Name="test_trail",
|
||||
S3BucketName="test",
|
||||
CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:log-group:/log-group/test:*",
|
||||
)
|
||||
logs_client.put_metric_filter(
|
||||
logGroupName="/log-group/test",
|
||||
filterName="test-filter",
|
||||
filterPattern="{ ($.errorCode = *UnauthorizedOperation) || ($.errorCode = AccessDenied*) || ($.sourceIPAddress!=delivery.logs.amazonaws.com) || ($.eventName!=HeadBucket) }",
|
||||
metricTransformations=[
|
||||
{
|
||||
"metricName": "my-metric",
|
||||
"metricNamespace": "my-namespace",
|
||||
"metricValue": "$.value",
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch_client.put_metric_alarm(
|
||||
AlarmName="test-alarm",
|
||||
MetricName="my-metric",
|
||||
Namespace="my-namespace",
|
||||
Period=10,
|
||||
EvaluationPeriods=5,
|
||||
Statistic="Average",
|
||||
Threshold=2,
|
||||
ComparisonOperator="GreaterThanThreshold",
|
||||
ActionsEnabled=True,
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
from providers.aws.services.cloudtrail.cloudtrail_client import Cloudtrail
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.logs_client",
|
||||
new=Logs(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_client",
|
||||
new=CloudWatch(current_audit_info),
|
||||
), mock.patch(
|
||||
"providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudtrail_client",
|
||||
new=Cloudtrail(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_unauthorized_api_calls.cloudwatch_log_metric_filter_unauthorized_api_calls import (
|
||||
cloudwatch_log_metric_filter_unauthorized_api_calls,
|
||||
)
|
||||
|
||||
check = cloudwatch_log_metric_filter_unauthorized_api_calls()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"CloudWatch log group /log-group/test found with metric filter test-filter and alarms set."
|
||||
)
|
||||
assert result[0].resource_id == "/log-group/test"
|
||||
192
providers/aws/services/cloudwatch/cloudwatch_service.py
Normal file
192
providers/aws/services/cloudwatch/cloudwatch_service.py
Normal file
@@ -0,0 +1,192 @@
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
|
||||
from lib.logger import logger
|
||||
from providers.aws.aws_provider import generate_regional_clients
|
||||
|
||||
|
||||
################## CloudWatch
|
||||
class CloudWatch:
|
||||
def __init__(self, audit_info):
|
||||
self.service = "cloudwatch"
|
||||
self.session = audit_info.audit_session
|
||||
self.audited_account = audit_info.audited_account
|
||||
self.regional_clients = generate_regional_clients(self.service, audit_info)
|
||||
self.metric_alarms = []
|
||||
self.__threading_call__(self.__describe_alarms__)
|
||||
|
||||
def __get_session__(self):
|
||||
return self.session
|
||||
|
||||
def __threading_call__(self, call):
|
||||
threads = []
|
||||
for regional_client in self.regional_clients.values():
|
||||
threads.append(threading.Thread(target=call, args=(regional_client,)))
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
def __describe_alarms__(self, regional_client):
|
||||
logger.info("CloudWatch - Describing alarms...")
|
||||
try:
|
||||
describe_alarms_paginator = regional_client.get_paginator("describe_alarms")
|
||||
for page in describe_alarms_paginator.paginate():
|
||||
for alarm in page["MetricAlarms"]:
|
||||
self.metric_alarms.append(
|
||||
MetricAlarm(
|
||||
alarm["AlarmArn"],
|
||||
alarm["AlarmName"],
|
||||
alarm["MetricName"],
|
||||
alarm["Namespace"],
|
||||
regional_client.region,
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}: {error}"
|
||||
)
|
||||
|
||||
|
||||
################## CloudWatch Logs
|
||||
class Logs:
|
||||
def __init__(self, audit_info):
|
||||
self.service = "logs"
|
||||
self.session = audit_info.audit_session
|
||||
self.audited_account = audit_info.audited_account
|
||||
self.regional_clients = generate_regional_clients(self.service, audit_info)
|
||||
self.metric_filters = []
|
||||
self.log_groups = []
|
||||
self.__threading_call__(self.__describe_metric_filters__)
|
||||
self.__threading_call__(self.__describe_log_groups__)
|
||||
|
||||
def __get_session__(self):
|
||||
return self.session
|
||||
|
||||
def __threading_call__(self, call):
|
||||
threads = []
|
||||
for regional_client in self.regional_clients.values():
|
||||
threads.append(threading.Thread(target=call, args=(regional_client,)))
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
def __describe_metric_filters__(self, regional_client):
|
||||
logger.info("CloudWatch Logs- Describing metric filters...")
|
||||
try:
|
||||
describe_metric_filters_paginator = regional_client.get_paginator(
|
||||
"describe_metric_filters"
|
||||
)
|
||||
for page in describe_metric_filters_paginator.paginate():
|
||||
for filter in page["metricFilters"]:
|
||||
self.metric_filters.append(
|
||||
MetricFilter(
|
||||
filter["filterName"],
|
||||
filter["metricTransformations"][0]["metricName"],
|
||||
filter["filterPattern"],
|
||||
filter["logGroupName"],
|
||||
regional_client.region,
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}: {error}"
|
||||
)
|
||||
|
||||
def __describe_log_groups__(self, regional_client):
|
||||
logger.info("CloudWatch Logs- Describing log groups...")
|
||||
try:
|
||||
describe_log_groups_paginator = regional_client.get_paginator(
|
||||
"describe_log_groups"
|
||||
)
|
||||
for page in describe_log_groups_paginator.paginate():
|
||||
for filter in page["logGroups"]:
|
||||
kms = None
|
||||
retention_days = 0
|
||||
if "kmsKeyId" in filter:
|
||||
kms = filter["kmsKeyId"]
|
||||
if "retentionInDays" in filter:
|
||||
retention_days = filter["retentionInDays"]
|
||||
self.log_groups.append(
|
||||
LogGroup(
|
||||
filter["arn"],
|
||||
filter["logGroupName"],
|
||||
retention_days,
|
||||
kms,
|
||||
regional_client.region,
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}: {error}"
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class MetricAlarm:
|
||||
arn: str
|
||||
name: str
|
||||
metric: str
|
||||
name_space: str
|
||||
region: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
arn,
|
||||
name,
|
||||
metric,
|
||||
name_space,
|
||||
region,
|
||||
):
|
||||
self.arn = arn
|
||||
self.name = name
|
||||
self.metric = metric
|
||||
self.name_space = name_space
|
||||
self.region = region
|
||||
|
||||
|
||||
@dataclass
|
||||
class MetricFilter:
|
||||
name: str
|
||||
metric: str
|
||||
pattern: str
|
||||
log_group: str
|
||||
region: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
metric,
|
||||
pattern,
|
||||
log_group,
|
||||
region,
|
||||
):
|
||||
self.name = name
|
||||
self.metric = metric
|
||||
self.pattern = pattern
|
||||
self.log_group = log_group
|
||||
self.region = region
|
||||
|
||||
|
||||
@dataclass
|
||||
class LogGroup:
|
||||
arn: str
|
||||
name: str
|
||||
retention_days: int
|
||||
kms_id: str
|
||||
region: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
arn,
|
||||
name,
|
||||
retention_days,
|
||||
kms_id,
|
||||
region,
|
||||
):
|
||||
self.arn = arn
|
||||
self.name = name
|
||||
self.retention_days = retention_days
|
||||
self.kms_id = kms_id
|
||||
self.region = region
|
||||
64
providers/aws/services/cloudwatch/cloudwatch_service_test.py
Normal file
64
providers/aws/services/cloudwatch/cloudwatch_service_test.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from boto3 import session
|
||||
from moto import mock_cloudwatch
|
||||
|
||||
from providers.aws.lib.audit_info.models import AWS_Audit_Info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import CloudWatch
|
||||
|
||||
AWS_ACCOUNT_NUMBER = 123456789012
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_CloudWatch_Service:
|
||||
# Mocked Audit Info
|
||||
def set_mocked_audit_info(self):
|
||||
audit_info = AWS_Audit_Info(
|
||||
original_session=None,
|
||||
audit_session=session.Session(
|
||||
profile_name=None,
|
||||
botocore_session=None,
|
||||
),
|
||||
audited_account=AWS_ACCOUNT_NUMBER,
|
||||
audited_user_id=None,
|
||||
audited_partition="aws",
|
||||
audited_identity_arn=None,
|
||||
profile=None,
|
||||
profile_region=None,
|
||||
credentials=None,
|
||||
assumed_role_info=None,
|
||||
audited_regions=None,
|
||||
organizations_metadata=None,
|
||||
)
|
||||
return audit_info
|
||||
|
||||
# Test CloudWatch Service
|
||||
@mock_cloudwatch
|
||||
def test_service(self):
|
||||
# CloudWatch client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
cloudwatch = CloudWatch(audit_info)
|
||||
assert cloudwatch.service == "cloudwatch"
|
||||
|
||||
# Test CloudWatch Client
|
||||
@mock_cloudwatch
|
||||
def test_client(self):
|
||||
# CloudWatch client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
cloudwatch = CloudWatch(audit_info)
|
||||
for client in cloudwatch.regional_clients.values():
|
||||
assert client.__class__.__name__ == "CloudWatch"
|
||||
|
||||
# Test CloudWatch Session
|
||||
@mock_cloudwatch
|
||||
def test__get_session__(self):
|
||||
# CloudWatch client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
cloudwatch = CloudWatch(audit_info)
|
||||
assert cloudwatch.session.__class__.__name__ == "Session"
|
||||
|
||||
# Test CloudWatch Session
|
||||
@mock_cloudwatch
|
||||
def test_audited_account(self):
|
||||
# CloudWatch client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
cloudwatch = CloudWatch(audit_info)
|
||||
assert cloudwatch.audited_account == AWS_ACCOUNT_NUMBER
|
||||
4
providers/aws/services/cloudwatch/logs_client.py
Normal file
4
providers/aws/services/cloudwatch/logs_client.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
logs_client = Logs(current_audit_info)
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
|
||||
CHECK_ID_extra9999="9.9999"
|
||||
CHECK_TITLE_extra9999="[check9999] Custom Defined Check"
|
||||
CHECK_SCORED_extra79999="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra9999="EXTRA"
|
||||
CHECK_SEVERITY_extra9999="Critical"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra9999="Custom"
|
||||
CHECK_ALTERNATE_extra9999="extra9999"
|
||||
CHECK_SERVICENAME_extra9999="custom"
|
||||
CHECK_RISK_cextra9999="Custom Defined Risk"
|
||||
CHECK_REMEDIATION_extra9999="Custom Remediation"
|
||||
CHECK_CAF_EPIC_extra9999="Custom EPIC"
|
||||
|
||||
extra9999(){
|
||||
|
||||
for regx in $REGIONS; do
|
||||
MY_CUSTOM_CMD=$($AWSCLI $CUSTOM_CMD $PROFILE_OPT --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$MY_CUSTOM_CMD" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
|
||||
textInfo "$regx: Access Denied or error trying to execute the custom command" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $MY_CUSTOM_CMD ]]; then
|
||||
for element in $MY_CUSTOM_CMD; do
|
||||
textFail "$regx: Custom output is: $element" "$regx" "$CHECK_SGDEFAULT_ID"
|
||||
done
|
||||
else
|
||||
textPass "$regx: Custom output is empty" "$regx" "$CHECK_SGDEFAULT_ID"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
|
||||
# Remediation:
|
||||
#
|
||||
# here URL to the relevand/official documentation
|
||||
#
|
||||
# here commands or steps to fix it if avalable, like:
|
||||
# aws logs put-metric-filter \
|
||||
# --region us-east-1 \
|
||||
# --log-group-name CloudTrail/MyCloudTrailLG \
|
||||
# --filter-name AWSCloudTrailChanges \
|
||||
# --filter-pattern '{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }' \
|
||||
# --metric-transformations metricName=CloudTrailEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
|
||||
# CHECK_ID_checkN="N.N"
|
||||
# CHECK_TITLE_checkN="[checkN] Description "
|
||||
# CHECK_SCORED_checkN="NOT_SCORED"
|
||||
# CHECK_CIS_LEVEL_checkN="EXTRA"
|
||||
# CHECK_SEVERITY_checkNN="Medium"
|
||||
# CHECK_ASFF_RESOURCE_TYPE_checkN="AwsAccount" # Choose appropriate value from https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format.html#asff-resources
|
||||
# CHECK_ALTERNATE_checkN="extraN"
|
||||
# CHECK_SERVICENAME_checkN="service" # get service short name from `curl -s https://api.regional-table.region-services.aws.a2z.com/index.json | jq -r '.prices[] | .id' | awk -F: '{ print $1 }' | sort -u`
|
||||
# CHECK_RISK_checkN=""
|
||||
# CHECK_REMEDIATION_checkN=""
|
||||
# CHECK_DOC_checkN=""
|
||||
# CHECK_CAF_EPIC_checkN=""
|
||||
|
||||
# General comments
|
||||
# ----------------
|
||||
# Do not add double quotes (") arround variable ${PROFILE_OPT} because this variable holds "--profile <profile-name>" and we need to read it as it is
|
||||
# Always check for AccessDenied|UnauthorizedOperation|AuthorizationError after AWS CLI command, using "2>&1" at the end
|
||||
# Avoid execute the same AWS CLI command again to check different attribute:
|
||||
# - Return all attributes on "--query"
|
||||
# - Use "read -r" to get all individual attributes
|
||||
# - Use "here-string" (<<<) when is necessary to interate through AWS CLI output with multiple attributes on the same line
|
||||
# - Here-string variable must be enclosed with double quotes, like "${LIST_OF_PUBLIC_INSTANCES}"
|
||||
# - See "Example of regional resource" below about how to do it
|
||||
# When an attribute doesn't exist, AWS CLI "--query" always return "none" if output is json or "None" if output is text
|
||||
# Use bash features to handle variable:
|
||||
# - ${var:N} : Return string from position 'N'
|
||||
# - ${var:N:len} : Return 'len' characters from position 'N'
|
||||
# - ${var^^} : Convert to upper-case all characters
|
||||
# - ${var,,} : Convert to lower-case all characters
|
||||
# - ATTENTION: macOS original bash version "GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin19)" doesn't support some variable expansion above.
|
||||
# Please make sure to test it.
|
||||
# - For more examples and how to use it please refer to https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
|
||||
# Check code with ShellCheck for best practices:
|
||||
# - https://www.shellcheck.net/
|
||||
# - https://github.com/koalaman/shellcheck#user-content-in-your-editor
|
||||
|
||||
# Example of regional resource
|
||||
# extraN(){
|
||||
# # "Description "
|
||||
# textInfo "Looking for instances in all regions... "
|
||||
# for regx in ${REGIONS}; do
|
||||
# LIST_OF_PUBLIC_INSTANCES=$("${AWSCLI}" ec2 describe-instances ${PROFILE_OPT} --region "${regx}" --query 'Reservations[*].Instances[?PublicIpAddress].[InstanceId,PublicIpAddress]' --output text 2>&1)
|
||||
# if [[ $(echo "${LIST_OF_PUBLIC_INSTANCES}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
# textInfo "${regx}: Access Denied trying to list EC2 Instances" "${regx}"
|
||||
# continue
|
||||
# fi
|
||||
# if [[ "${LIST_OF_PUBLIC_INSTANCES}" != "" && "${LIST_OF_PUBLIC_INSTANCES,,}" != "none" ]]; then
|
||||
# while read -r INSTANCE_ID PUBLIC_IP; do
|
||||
# textFail "${regx}: Instance: ${INSTANCE_ID} at IP: ${PUBLIC_IP} is internet-facing!" "${regx}" "${INSTANCE_ID}"
|
||||
# done <<< "${LIST_OF_PUBLIC_INSTANCES}"
|
||||
# else
|
||||
# textPass "${regx}: no Internet Facing EC2 Instances found" "${regx}"
|
||||
# fi
|
||||
# done
|
||||
# }
|
||||
|
||||
# Example of global resource
|
||||
# extraN(){
|
||||
# # "Description "
|
||||
# LIST_DISTRIBUTIONS=$("${AWSCLI}" cloudfront list-distributions ${PROFILE_OPT} --query 'DistributionList.Items[*].Id' --output text 2>&1)
|
||||
# if [[ $(echo "${LIST_DISTRIBUTIONS}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
# textInfo "${REGION}: Access Denied trying to list distributions" "${REGION}"
|
||||
# return
|
||||
# fi
|
||||
# if [[ "${LIST_DISTRIBUTIONS}" != "" && "${LIST_DISTRIBUTIONS,,}" != "none" ]]; then
|
||||
# for dist in ${LIST_DISTRIBUTIONS}; do
|
||||
# GEO_ENABLED=$("${AWSCLI}" cloudfront get-distribution-config $PROFILE_OPT --id "${dist}" --query 'DistributionConfig.Restrictions.GeoRestriction.RestrictionType' --output text 2>&1)
|
||||
# if [[ $(echo "${GEO_ENABLED}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
# textInfo "${REGION}: Access Denied trying to get distribution config for ${dist}" "${REGION}"
|
||||
# continue
|
||||
# fi
|
||||
# if [[ "${GEO_ENABLED,,}" == "none" ]]; then
|
||||
# textFail "${REGION}: CloudFront distribution ${dist} has not Geo restrictions" "${REGION}" "${dist}"
|
||||
# else
|
||||
# textPass "${REGION}: CloudFront distribution ${dist} has Geo restrictions enabled" "${REGION}" "${dist}"
|
||||
# fi
|
||||
# done
|
||||
# else
|
||||
# textInfo "${REGION}: No CloudFront distributions found"
|
||||
# fi
|
||||
# }
|
||||
Reference in New Issue
Block a user