Created
June 7, 2016 12:57
-
-
Save chalfant/d4fe1b2bffbc4c1cc55e552133db961a to your computer and use it in GitHub Desktop.
Create monitoring metric filters and alarms for CIS Benchmarks for AWS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# Implement CIS Benchmarks for AWS Section 3.x | |
# Details on each benchmark from https://benchmarks.cisecurity.org/downloads/show-single/?file=awsfoundations.100 | |
# name should be in camelcase since we'll use it for filter and alarm names | |
filters = [ | |
{ | |
benchmark: '3.1', | |
description: 'Ensure a log metric filter and alarm exist for unauthorized API calls', | |
level: 1, | |
name: 'UnauthorizedApiCalls', | |
pattern: '{ ($.errorCode = "*UnauthorizedOperation") || ($.errorCode = "AccessDenied*") }' | |
}, | |
{ | |
benchmark: '3.2', | |
description: 'Ensure a log metric filter and alarm exist for Management Console sign-in without MFA', | |
level: 1, | |
name: 'NoMfaConsoleLogins', | |
pattern: '{ $.userIdentity.sessionContext.attributes.mfaAuthenticated != "true" && $.userIdentity.invokedBy = "signin.amazonaws.com" }' | |
}, | |
{ | |
benchmark: '3.3', | |
description: 'Ensure a log metric filter and alarm exist for usage of "root" account', | |
level: 1, | |
name: 'RootAccountLogins', | |
pattern: '{ $.userIdentity.type = "Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType != "AwsServiceEvent" }' | |
}, | |
{ | |
benchmark: '3.4', | |
description: 'Ensure a log metric filter and alarm exist for IAM policy changes', | |
level: 1, | |
name: 'IamPolicyChanges', | |
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)}' | |
}, | |
{ | |
benchmark: '3.5', | |
description: 'Ensure a log metric filter and alarm exist for CloudTrail configuration changes', | |
level: 1, | |
name: 'CloudTrailConfigurationChanges', | |
pattern: '{ ($.eventName = CreateTrail) ||($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }' | |
}, | |
{ | |
benchmark: '3.6', | |
description: 'Ensure a log metric filter and alarm exist for AWS Management Console authentication failures', | |
level: 2, | |
name: 'FailedConsoleLogins', | |
pattern: '{ ($.eventName = ConsoleLogin) && ($.errorMessage = "Failed authentication") }' | |
}, | |
{ | |
benchmark: '3.7', | |
description: 'Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs', | |
level: 2, | |
name: 'DisabledOrDeletedCmks', | |
pattern: '{($.eventSource = kms.amazonaws.com) && (($.eventName=DisableKey)||($.eventName=ScheduleKeyDeletion))}' | |
}, | |
{ | |
benchmark: '3.8', | |
description: 'Ensure a log metric filter and alarm exist for S3 bucket policy changes', | |
level: 1, | |
name: 'S3BucketPolicyChanges', | |
pattern: '{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) || ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }' | |
}, | |
{ | |
benchmark: '3.9', | |
description: 'Ensure a log metric filter and alarm exist for AWS Config configuration changes', | |
level: 2, | |
name: 'AwsConfigChanges', | |
pattern: '{($.eventSource = config.amazonaws.com) && (($.eventName=StopConfigurationRecorder)||($.eventName=DeleteDeliveryChannel)||($.eventName=PutDeliveryChannel)||($.eventName=PutConfigurationRecorder))}' | |
}, | |
{ | |
benchmark: '3.10', | |
description: 'Ensure a log metric filter and alarm exist for security group changes', | |
level: 2, | |
name: 'SecurityGroupChanges', | |
pattern: '{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup)}' | |
}, | |
{ | |
benchmark: '3.11', | |
description: 'Ensure a log metric filter and alarm exist for changes to Network Access Control Lists', | |
level: 2, | |
name: 'NetworkAccessControlListChanges', | |
pattern: '{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }' | |
}, | |
{ | |
benchmark: '3.12', | |
description: 'Ensure a log metric filter and alarm exist for changes to network gateways', | |
level: 1, | |
name: 'NetworkGatewayChanges', | |
pattern: '{ ($.eventName = CreateCustomerGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = DetachInternetGateway) }' | |
} | |
] | |
require 'aws-sdk' | |
# The name (not ARN) of your CloudTrail Log Group | |
CLOUDTRAIL_LOG_GROUP = ENV['CLOUDTRAIL_LOG_GROUP'] | |
# The ARN of an SNS topic the alarm will post to | |
ALARM_SNS_TOPIC = ENV['ALARM_SNS_TOPIC'] | |
# Namespace used for metrics created by metric filters | |
METRIC_NAMESPACE = 'CisBenchmarks' | |
def filter_name(filter) | |
"#{filter[:name]}Filter" | |
end | |
def alarm_name(filter) | |
"#{filter[:name]}Alarm" | |
end | |
def metric_filter_exists?(filter) | |
cwl = Aws::CloudWatchLogs::Client.new(region: 'us-east-1') | |
resp = cwl.describe_metric_filters({ | |
log_group_name: CLOUDTRAIL_LOG_GROUP, | |
filter_name_prefix: filter_name(filter) | |
}) | |
resp.metric_filters.each do |metric_filter| | |
if metric_filter.filter_name == filter_name(filter) | |
return true | |
end | |
end | |
return false | |
end | |
# filter: a record from the hash above | |
def create_metric_filter(filter) | |
filter_name = filter_name(filter) | |
cwl = Aws::CloudWatchLogs::Client.new(region: 'us-east-1') | |
resp = cwl.put_metric_filter({ | |
log_group_name: CLOUDTRAIL_LOG_GROUP, | |
filter_name: filter_name, | |
filter_pattern: filter[:pattern], | |
metric_transformations: [ | |
{ | |
metric_name: filter[:name], | |
metric_namespace: METRIC_NAMESPACE, | |
metric_value: '1' | |
} | |
] | |
}) | |
end | |
def metric_alarm_exists?(filter) | |
cw = Aws::CloudWatch::Resource.new(region: 'us-east-1') | |
alarms = cw.alarms({ | |
alarm_names: [ alarm_name(filter) ] | |
}) | |
alarms.each do |alarm| | |
if alarm.alarm_name == alarm_name(filter) | |
return true | |
end | |
end | |
return false | |
end | |
# filter: a record from the hash above | |
def create_metric_alarm(filter) | |
alarm_name = alarm_name(filter) | |
alarm_description = "CIS Benchmark Alarm for #{filter[:benchmark]} #{filter[:name]}" | |
cw = Aws::CloudWatch::Client.new(region: 'us-east-1') | |
resp = cw.put_metric_alarm({ | |
alarm_name: alarm_name, | |
actions_enabled: true, | |
alarm_actions: [ ALARM_SNS_TOPIC ], | |
metric_name: filter[:name], | |
namespace: METRIC_NAMESPACE, | |
statistic: 'Sum', | |
period: 300, | |
evaluation_periods: 1, | |
threshold: 1.0, | |
comparison_operator: 'GreaterThanOrEqualToThreshold' | |
}) | |
end | |
## MAIN | |
filters.each do |filter| | |
puts "Checking #{filter[:benchmark]}: #{filter[:description]}..." | |
if !metric_filter_exists?(filter) | |
puts "\tCreating #{filter_name(filter)}." | |
create_metric_filter(filter) | |
else | |
puts "\t#{filter_name(filter)} exists." | |
end | |
if !metric_alarm_exists?(filter) | |
puts "\tCreating #{alarm_name(filter)}." | |
create_metric_alarm(filter) | |
else | |
puts "\t#{alarm_name(filter)} exists." | |
end | |
puts "" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi chalfant, how can I load this to my AWS account. do I need to load this in Cloudformation?