cfn-guardian 0.1.0 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +1 -0
- data/.github/workflows/build-gem.yml +25 -0
- data/.github/workflows/release-gem.yml +25 -0
- data/.github/workflows/release-image.yml +33 -0
- data/.rspec +1 -0
- data/Dockerfile +19 -0
- data/Gemfile.lock +39 -21
- data/README.md +9 -378
- data/cfn-guardian.gemspec +7 -5
- data/docs/alarm_templates.md +130 -0
- data/docs/cli.md +182 -0
- data/docs/composite_alarms.md +24 -0
- data/docs/custom_checks/azure_file_check.md +28 -0
- data/docs/custom_checks/domain_expiry.md +10 -0
- data/docs/custom_checks/http.md +59 -0
- data/docs/custom_checks/log_group_metric_filters.md +27 -0
- data/docs/custom_checks/nrpe.md +29 -0
- data/docs/custom_checks/port.md +40 -0
- data/docs/custom_checks/sftp.md +73 -0
- data/docs/custom_checks/sql.md +44 -0
- data/docs/custom_checks/tls.md +25 -0
- data/docs/custom_metrics.md +71 -0
- data/docs/event_subscriptions.md +67 -0
- data/docs/maintenance_mode.md +85 -0
- data/docs/notifiers.md +33 -0
- data/docs/overview.md +22 -0
- data/docs/resources.md +93 -0
- data/docs/variables.md +58 -0
- data/lib/cfnguardian.rb +325 -37
- data/lib/cfnguardian/cloudwatch.rb +132 -0
- data/lib/cfnguardian/codecommit.rb +54 -0
- data/lib/cfnguardian/codepipeline.rb +138 -0
- data/lib/cfnguardian/compile.rb +142 -18
- data/lib/cfnguardian/config/defaults.yaml +103 -0
- data/lib/cfnguardian/deploy.rb +2 -16
- data/lib/cfnguardian/display_formatter.rb +163 -0
- data/lib/cfnguardian/drift.rb +79 -0
- data/lib/cfnguardian/error.rb +4 -0
- data/lib/cfnguardian/log.rb +0 -1
- data/lib/cfnguardian/models/alarm.rb +193 -59
- data/lib/cfnguardian/models/check.rb +128 -33
- data/lib/cfnguardian/models/composite.rb +21 -0
- data/lib/cfnguardian/models/event.rb +201 -49
- data/lib/cfnguardian/models/event_subscription.rb +96 -0
- data/lib/cfnguardian/models/metric_filter.rb +28 -0
- data/lib/cfnguardian/resources/amazonmq_rabbitmq.rb +136 -0
- data/lib/cfnguardian/resources/application_targetgroup.rb +2 -0
- data/lib/cfnguardian/resources/azure_file.rb +20 -0
- data/lib/cfnguardian/resources/base.rb +155 -33
- data/lib/cfnguardian/resources/ec2_instance.rb +11 -0
- data/lib/cfnguardian/resources/ecs_service.rb +2 -2
- data/lib/cfnguardian/resources/http.rb +17 -1
- data/lib/cfnguardian/resources/internal_http.rb +74 -0
- data/lib/cfnguardian/resources/internal_port.rb +33 -0
- data/lib/cfnguardian/resources/internal_sftp.rb +58 -0
- data/lib/cfnguardian/resources/log_group.rb +26 -0
- data/lib/cfnguardian/resources/network_targetgroup.rb +1 -0
- data/lib/cfnguardian/resources/port.rb +25 -0
- data/lib/cfnguardian/resources/rds_cluster.rb +14 -0
- data/lib/cfnguardian/resources/rds_instance.rb +73 -0
- data/lib/cfnguardian/resources/redshift_cluster.rb +2 -2
- data/lib/cfnguardian/resources/sftp.rb +50 -0
- data/lib/cfnguardian/resources/sql.rb +3 -3
- data/lib/cfnguardian/resources/tls.rb +66 -0
- data/lib/cfnguardian/s3.rb +3 -2
- data/lib/cfnguardian/stacks/main.rb +94 -72
- data/lib/cfnguardian/stacks/resources.rb +111 -43
- data/lib/cfnguardian/string.rb +12 -0
- data/lib/cfnguardian/version.rb +1 -1
- metadata +133 -10
@@ -0,0 +1,103 @@
|
|
1
|
+
Resources:
|
2
|
+
AmazonMQBroker:
|
3
|
+
- Id: Default
|
4
|
+
AmazonMQRabbitMQBroker:
|
5
|
+
- Id: Default
|
6
|
+
AmazonMQRabbitMQNode:
|
7
|
+
- Id: Default
|
8
|
+
Node: Default
|
9
|
+
AmazonMQRabbitMQQueue:
|
10
|
+
- Id: Default
|
11
|
+
Queue: Default
|
12
|
+
Vhost: Default
|
13
|
+
ApiGateway:
|
14
|
+
- Id: Default
|
15
|
+
ApplicationTargetGroup:
|
16
|
+
- Id: Default
|
17
|
+
LoadBalancer: Default
|
18
|
+
AutoScalingGroup:
|
19
|
+
- Id: Default
|
20
|
+
CloudFrontDistribution:
|
21
|
+
- Id: Default
|
22
|
+
DomainExpiry:
|
23
|
+
- Id: Default
|
24
|
+
DynamoDBTable:
|
25
|
+
- Id: Default
|
26
|
+
Ec2Instance:
|
27
|
+
- Id: Default
|
28
|
+
ECSCluster:
|
29
|
+
- Id: Default
|
30
|
+
ECSService:
|
31
|
+
- Id: Default
|
32
|
+
Cluster: Default
|
33
|
+
ElasticFileSystem:
|
34
|
+
- Id: Default
|
35
|
+
ElasticLoadBalancer:
|
36
|
+
- Id: Default
|
37
|
+
ElastiCacheReplicationGroup:
|
38
|
+
- Id: Default
|
39
|
+
Http:
|
40
|
+
- Id: Default
|
41
|
+
StatusCode: 200
|
42
|
+
Ssl: true
|
43
|
+
BodyRegex: Default
|
44
|
+
InternalHttp:
|
45
|
+
- Environment: Default
|
46
|
+
VpcId: vpc-default
|
47
|
+
Subnets:
|
48
|
+
- subnet-default
|
49
|
+
Hosts:
|
50
|
+
- Id: Default
|
51
|
+
StatusCode: 200
|
52
|
+
BodyRegex: Default
|
53
|
+
Port:
|
54
|
+
- Id: Default
|
55
|
+
Port: 0
|
56
|
+
InternalPort:
|
57
|
+
- Environment: Default
|
58
|
+
VpcId: vpc-default
|
59
|
+
Subnets:
|
60
|
+
- subnet-default
|
61
|
+
Hosts:
|
62
|
+
- Id: Default
|
63
|
+
Port: 0
|
64
|
+
Lambda:
|
65
|
+
- Id: Default
|
66
|
+
LogGroup:
|
67
|
+
- Id: Default
|
68
|
+
MetricFilters:
|
69
|
+
- MetricName: Default
|
70
|
+
Pattern: Default
|
71
|
+
NetworkTargetGroup:
|
72
|
+
- Id: Default
|
73
|
+
LoadBalancer: Default
|
74
|
+
Nrpe:
|
75
|
+
- Environment: Default
|
76
|
+
VpcId: vpc-default
|
77
|
+
Subnets:
|
78
|
+
- subnet-default
|
79
|
+
Hosts:
|
80
|
+
- Id: Default
|
81
|
+
Commands:
|
82
|
+
- default
|
83
|
+
RDSClusterInstance:
|
84
|
+
- Id: Default
|
85
|
+
RDSInstance:
|
86
|
+
- Id: Default
|
87
|
+
RedshiftCluster:
|
88
|
+
- Id: Default
|
89
|
+
Sql:
|
90
|
+
- Environment: Default
|
91
|
+
VpcId: vpc-default
|
92
|
+
Subnets:
|
93
|
+
- subnet-default
|
94
|
+
Hosts:
|
95
|
+
- Id: Default
|
96
|
+
SecretId: Default
|
97
|
+
Engine: default
|
98
|
+
Queries:
|
99
|
+
- MetricName: Default
|
100
|
+
Query: Default
|
101
|
+
SQSQueue:
|
102
|
+
- Id: Default
|
103
|
+
|
data/lib/cfnguardian/deploy.rb
CHANGED
@@ -7,27 +7,13 @@ module CfnGuardian
|
|
7
7
|
class Deploy
|
8
8
|
include Logging
|
9
9
|
|
10
|
-
def initialize(opts,bucket)
|
10
|
+
def initialize(opts,bucket,parameters)
|
11
11
|
@stack_name = opts.fetch(:stack_name,'guardian')
|
12
12
|
@bucket = bucket
|
13
13
|
@prefix = @stack_name
|
14
14
|
@template_path = "out/guardian.compiled.yaml"
|
15
15
|
@template_url = "https://#{@bucket}.s3.amazonaws.com/#{@prefix}/guardian.compiled.yaml"
|
16
|
-
@parameters =
|
17
|
-
|
18
|
-
config = YAML.load_file(opts[:config])
|
19
|
-
if config.has_key?('Topics')
|
20
|
-
@parameters['Critical'] = config['Topics'].fetch('Critical','')
|
21
|
-
@parameters['Warning'] = config['Topics'].fetch('Warning','')
|
22
|
-
@parameters['Task'] = config['Topics'].fetch('Task','')
|
23
|
-
@parameters['Informational'] = config['Topics'].fetch('Informational','')
|
24
|
-
end
|
25
|
-
|
26
|
-
@parameters['Critical'] = opts.fetch(:sns_critical,@parameters['Critical'])
|
27
|
-
@parameters['Warning'] = opts.fetch(:sns_warning,@parameters['Warning'])
|
28
|
-
@parameters['Task'] = opts.fetch(:sns_task,@parameters['Task'])
|
29
|
-
@parameters['Informational'] = opts.fetch(:sns_informational,@parameters['Informational'])
|
30
|
-
|
16
|
+
@parameters = parameters
|
31
17
|
@client = Aws::CloudFormation::Client.new()
|
32
18
|
end
|
33
19
|
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'cfnguardian/cloudwatch'
|
2
|
+
require 'cfnguardian/string'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module CfnGuardian
|
6
|
+
class DisplayFormatter
|
7
|
+
|
8
|
+
def initialize(alarms=[])
|
9
|
+
@alarms = alarms
|
10
|
+
end
|
11
|
+
|
12
|
+
def alarms()
|
13
|
+
resp = []
|
14
|
+
|
15
|
+
@alarms.each do |alarm|
|
16
|
+
alarm_name = CfnGuardian::CloudWatch.get_alarm_name(alarm)
|
17
|
+
rows = [
|
18
|
+
['ResourceId', alarm.resource_id],
|
19
|
+
['ResourceHash', alarm.resource_hash],
|
20
|
+
['ResourceName', alarm.resource_name],
|
21
|
+
['Enabled', alarm.enabled],
|
22
|
+
['MetricName', alarm.metric_name],
|
23
|
+
['Dimensions', alarm.dimensions],
|
24
|
+
['Threshold', alarm.threshold],
|
25
|
+
['Period', alarm.period],
|
26
|
+
['EvaluationPeriods', alarm.evaluation_periods],
|
27
|
+
['ComparisonOperator', alarm.comparison_operator],
|
28
|
+
['Statistic', alarm.statistic],
|
29
|
+
['ActionsEnabled', alarm.actions_enabled],
|
30
|
+
['DatapointsToAlarm', alarm.datapoints_to_alarm],
|
31
|
+
['ExtendedStatistic', alarm.extended_statistic],
|
32
|
+
['EvaluateLowSampleCountPercentile', alarm.evaluate_low_sample_count_percentile],
|
33
|
+
['Unit', alarm.unit],
|
34
|
+
['AlarmAction', alarm.alarm_action],
|
35
|
+
['TreatMissingData', alarm.treat_missing_data]
|
36
|
+
]
|
37
|
+
|
38
|
+
rows.select! {|row| !row[1].nil?}
|
39
|
+
|
40
|
+
resp << {
|
41
|
+
title: "#{alarm.group}::#{alarm.name}".green + "\n" + alarm_name.green,
|
42
|
+
rows: rows
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
return resp
|
47
|
+
end
|
48
|
+
|
49
|
+
def compare_alarms(metric_alarms)
|
50
|
+
resp = []
|
51
|
+
|
52
|
+
@alarms.each do |alarm|
|
53
|
+
alarm_name = CfnGuardian::CloudWatch.get_alarm_name(alarm)
|
54
|
+
metric_alarm = metric_alarms.find {|ma| ma.alarm_name.include? alarm_name}
|
55
|
+
dimensions = metric_alarm.dimensions.map {|dim| {dim.name.to_sym => dim.value}}.inject(:merge)
|
56
|
+
|
57
|
+
rows = [
|
58
|
+
['ResourceId', alarm.resource_id, alarm.resource_id],
|
59
|
+
['ResourceHash', alarm.resource_hash, alarm.resource_hash],
|
60
|
+
['ResourceName', alarm.resource_name, alarm.resource_name],
|
61
|
+
['Enabled', alarm.enabled, true],
|
62
|
+
['MetricName', alarm.metric_name, metric_alarm.metric_name],
|
63
|
+
['Dimensions', alarm.dimensions, dimensions],
|
64
|
+
['Threshold', alarm.threshold.to_f, metric_alarm.threshold],
|
65
|
+
['Period', alarm.period, metric_alarm.period],
|
66
|
+
['EvaluationPeriods', alarm.evaluation_periods, metric_alarm.evaluation_periods],
|
67
|
+
['ComparisonOperator', alarm.comparison_operator, metric_alarm.comparison_operator],
|
68
|
+
['Statistic', alarm.statistic, metric_alarm.statistic],
|
69
|
+
['ActionsEnabled', alarm.actions_enabled, metric_alarm.actions_enabled],
|
70
|
+
['DatapointsToAlarm', alarm.datapoints_to_alarm, metric_alarm.datapoints_to_alarm],
|
71
|
+
['ExtendedStatistic', alarm.extended_statistic, metric_alarm.extended_statistic],
|
72
|
+
['EvaluateLowSampleCountPercentile', alarm.evaluate_low_sample_count_percentile, metric_alarm.evaluate_low_sample_count_percentile],
|
73
|
+
['Unit', alarm.unit, metric_alarm.unit],
|
74
|
+
['TreatMissingData', alarm.treat_missing_data, metric_alarm.treat_missing_data],
|
75
|
+
['AlarmAction', alarm.alarm_action, alarm.alarm_action]
|
76
|
+
]
|
77
|
+
|
78
|
+
rows.select! {|row| !row[1].nil?}.each {|row| colour_compare_row(row)}
|
79
|
+
|
80
|
+
if has_config_difference?(rows)
|
81
|
+
resp << {
|
82
|
+
title: "#{alarm.group}::#{alarm.name}".green + "\n" + alarm_name.green,
|
83
|
+
rows: rows
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
return resp
|
89
|
+
end
|
90
|
+
|
91
|
+
def alarm_state(metric_alarms)
|
92
|
+
rows = []
|
93
|
+
|
94
|
+
metric_alarms.each do |ma|
|
95
|
+
if ma.state_value == 'ALARM'
|
96
|
+
state_value = ma.state_value.to_s.red
|
97
|
+
elsif ma.state_value == 'INSUFFICIENT_DATA'
|
98
|
+
state_value = ma.state_value.to_s.yellow
|
99
|
+
else
|
100
|
+
state_value = ma.state_value.to_s.green
|
101
|
+
end
|
102
|
+
|
103
|
+
rows << [
|
104
|
+
ma.alarm_name,
|
105
|
+
state_value,
|
106
|
+
ma.state_updated_timestamp.localtime,
|
107
|
+
ma.actions_enabled ? 'ENABLED'.green : 'DISABLED'.red
|
108
|
+
]
|
109
|
+
end
|
110
|
+
# sort by state_value
|
111
|
+
return rows.sort_by {|r| r[3]}
|
112
|
+
end
|
113
|
+
|
114
|
+
def alarm_history(history,type)
|
115
|
+
rows = []
|
116
|
+
line_width = 100
|
117
|
+
|
118
|
+
history.each do |item|
|
119
|
+
data = JSON.load(item.history_data)
|
120
|
+
|
121
|
+
case type
|
122
|
+
when "StateUpdate"
|
123
|
+
rows << [
|
124
|
+
item.timestamp.localtime,
|
125
|
+
item.history_summary,
|
126
|
+
data['newState']['stateReason'].word_wrap
|
127
|
+
]
|
128
|
+
when "ConfigurationUpdate"
|
129
|
+
updated = []
|
130
|
+
if data['type'] == 'Update'
|
131
|
+
data['originalUpdatedFields'].each do |k,v|
|
132
|
+
unless k == 'alarmConfigurationUpdatedTimestamp'
|
133
|
+
updated << "#{k}: #{v} -> #{data['updatedAlarm'][k]}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
rows << [
|
138
|
+
item.timestamp.localtime,
|
139
|
+
data['type'],
|
140
|
+
updated.join("\n").word_wrap
|
141
|
+
]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
return rows
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def has_config_difference?(rows)
|
151
|
+
rows.each do |row|
|
152
|
+
unless row[1].eql?(row[2])
|
153
|
+
return true
|
154
|
+
end
|
155
|
+
end
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
|
159
|
+
def colour_compare_row(row)
|
160
|
+
return row[1].eql?(row[2]) ? row.map! {|r| r.to_s.green} : row.map! {|r| r.to_s.red}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'aws-sdk-cloudformation'
|
2
|
+
|
3
|
+
module CfnGuardian
|
4
|
+
class Drift
|
5
|
+
|
6
|
+
def initialize(stack)
|
7
|
+
@stack = stack
|
8
|
+
@client = Aws::CloudFormation::Client.new()
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_nested_stacks
|
12
|
+
stacks = []
|
13
|
+
resp = @client.describe_stack_resources({
|
14
|
+
stack_name: @stack
|
15
|
+
})
|
16
|
+
resp.stack_resources.each do |r|
|
17
|
+
if r.resource_type == 'AWS::CloudFormation::Stack'
|
18
|
+
stacks << r.physical_resource_id
|
19
|
+
end
|
20
|
+
end
|
21
|
+
return stacks
|
22
|
+
end
|
23
|
+
|
24
|
+
def detect_drift(stack)
|
25
|
+
resp = @client.detect_stack_drift({
|
26
|
+
stack_name: stack
|
27
|
+
})
|
28
|
+
wait_for_dirft_detection(resp.stack_drift_detection_id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def wait_for_dirft_detection(id,count=0)
|
32
|
+
resp = @client.describe_stack_drift_detection_status({
|
33
|
+
stack_drift_detection_id: id
|
34
|
+
})
|
35
|
+
if resp.detection_status == 'DETECTION_IN_PROGRESS' && count < 10
|
36
|
+
sleep(2)
|
37
|
+
count += 1
|
38
|
+
wait_for_dirft_detection(id,count)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_drift(stack)
|
43
|
+
rows = []
|
44
|
+
resp = @client.describe_stack_resource_drifts({
|
45
|
+
stack_name: stack,
|
46
|
+
stack_resource_drift_status_filters: ["MODIFIED", "DELETED"]
|
47
|
+
})
|
48
|
+
|
49
|
+
if resp.stack_resource_drifts.any?
|
50
|
+
resp.stack_resource_drifts.each do |drift|
|
51
|
+
next if drift.resource_type != 'AWS::CloudWatch::Alarm'
|
52
|
+
|
53
|
+
if drift.stack_resource_drift_status == 'MODIFIED'
|
54
|
+
drift.property_differences.each do |diff|
|
55
|
+
rows << [
|
56
|
+
drift.physical_resource_id,
|
57
|
+
diff.property_path,
|
58
|
+
diff.expected_value,
|
59
|
+
diff.actual_value,
|
60
|
+
diff.difference_type
|
61
|
+
]
|
62
|
+
end
|
63
|
+
elsif drift.stack_resource_drift_status == 'DELETED'
|
64
|
+
rows << [
|
65
|
+
drift.physical_resource_id.red,
|
66
|
+
"",
|
67
|
+
"",
|
68
|
+
"",
|
69
|
+
drift.stack_resource_drift_status.red
|
70
|
+
]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
return rows
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/lib/cfnguardian/log.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
require 'cfnguardian/string'
|
2
|
+
require 'digest/md5'
|
2
3
|
|
3
4
|
module CfnGuardian
|
4
5
|
module Models
|
5
|
-
class
|
6
|
+
class BaseAlarm
|
6
7
|
|
7
|
-
attr_reader :type
|
8
|
-
|
8
|
+
attr_reader :type,
|
9
|
+
:resource_hash
|
10
|
+
|
11
|
+
attr_accessor :group,
|
9
12
|
:name,
|
10
13
|
:metric_name,
|
11
14
|
:namespace,
|
@@ -17,21 +20,24 @@ module CfnGuardian
|
|
17
20
|
:statistic,
|
18
21
|
:actions_enabled,
|
19
22
|
:enabled,
|
20
|
-
:
|
23
|
+
:resource_id,
|
24
|
+
:resource_name,
|
21
25
|
:alarm_action,
|
22
26
|
:treat_missing_data,
|
23
27
|
:datapoints_to_alarm,
|
24
28
|
:extended_statistic,
|
25
29
|
:evaluate_low_sample_count_percentile,
|
26
|
-
:unit
|
30
|
+
:unit,
|
31
|
+
:maintenance_groups,
|
32
|
+
:additional_notifiers
|
27
33
|
|
28
34
|
def initialize(resource)
|
29
35
|
@type = 'Alarm'
|
30
|
-
@
|
36
|
+
@group = nil
|
31
37
|
@name = ''
|
32
38
|
@metric_name = nil
|
33
39
|
@namespace = nil
|
34
|
-
@dimensions =
|
40
|
+
@dimensions = nil
|
35
41
|
@threshold = 0
|
36
42
|
@period = 60
|
37
43
|
@evaluation_periods = 1
|
@@ -43,37 +49,35 @@ module CfnGuardian
|
|
43
49
|
@evaluate_low_sample_count_percentile = nil
|
44
50
|
@unit = nil
|
45
51
|
@enabled = true
|
46
|
-
@
|
47
|
-
@
|
52
|
+
@resource_hash = Digest::MD5.hexdigest resource['Id']
|
53
|
+
@resource_id = resource['Id']
|
54
|
+
@resource_name = resource.fetch('Name', nil)
|
48
55
|
@alarm_action = 'Critical'
|
49
56
|
@treat_missing_data = nil
|
57
|
+
@maintenance_groups = []
|
58
|
+
@additional_notifiers = []
|
50
59
|
end
|
51
60
|
|
52
61
|
def metric_name=(metric_name)
|
53
62
|
raise ArgumentError.new("metric_name '#{metric_name}' must be of type String, provided type '#{metric_name.class}'") unless metric_name.is_a?(String)
|
54
63
|
@metric_name=metric_name
|
55
|
-
end
|
56
|
-
|
57
|
-
def to_h
|
58
|
-
Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] } ]
|
59
|
-
end
|
60
|
-
|
64
|
+
end
|
61
65
|
end
|
62
66
|
|
63
67
|
|
64
|
-
class ApiGatewayAlarm <
|
68
|
+
class ApiGatewayAlarm < BaseAlarm
|
65
69
|
def initialize(resource)
|
66
70
|
super(resource)
|
67
|
-
@
|
71
|
+
@group = 'ApiGateway'
|
68
72
|
@namespace = 'AWS/ApiGateway'
|
69
73
|
@dimensions = { ApiName: resource['Id'] }
|
70
74
|
end
|
71
75
|
end
|
72
76
|
|
73
|
-
class ApplicationTargetGroupAlarm <
|
77
|
+
class ApplicationTargetGroupAlarm < BaseAlarm
|
74
78
|
def initialize(resource)
|
75
79
|
super(resource)
|
76
|
-
@
|
80
|
+
@group = 'ApplicationTargetGroup'
|
77
81
|
@namespace = 'AWS/ApplicationELB'
|
78
82
|
@dimensions = {
|
79
83
|
TargetGroup: resource['Id'],
|
@@ -82,19 +86,53 @@ module CfnGuardian
|
|
82
86
|
end
|
83
87
|
end
|
84
88
|
|
85
|
-
class AmazonMQBrokerAlarm <
|
89
|
+
class AmazonMQBrokerAlarm < BaseAlarm
|
90
|
+
def initialize(resource)
|
91
|
+
super(resource)
|
92
|
+
@group = 'AmazonMQBroker'
|
93
|
+
@namespace = 'AWS/AmazonMQ'
|
94
|
+
@dimensions = { Broker: resource['Id'] }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class AmazonMQRabbitMQBrokerAlarm < BaseAlarm
|
86
99
|
def initialize(resource)
|
87
100
|
super(resource)
|
88
|
-
@
|
101
|
+
@group = 'AmazonMQRabbitMQBroker'
|
89
102
|
@namespace = 'AWS/AmazonMQ'
|
90
103
|
@dimensions = { Broker: resource['Id'] }
|
91
104
|
end
|
92
105
|
end
|
106
|
+
|
107
|
+
class AmazonMQRabbitMQNodeAlarm < BaseAlarm
|
108
|
+
def initialize(resource)
|
109
|
+
super(resource)
|
110
|
+
@group = 'AmazonMQRabbitMQNode'
|
111
|
+
@namespace = 'AWS/AmazonMQ'
|
112
|
+
@dimensions = {
|
113
|
+
Broker: resource['Id'],
|
114
|
+
Node: resource['Node']
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class AmazonMQRabbitMQQueueAlarm < BaseAlarm
|
120
|
+
def initialize(resource)
|
121
|
+
super(resource)
|
122
|
+
@group = 'AmazonMQRabbitMQQueue'
|
123
|
+
@namespace = 'AWS/AmazonMQ'
|
124
|
+
@dimensions = {
|
125
|
+
Broker: resource['Id'],
|
126
|
+
Queue: resource['Queue'],
|
127
|
+
VirtualHost: resource['Vhost']
|
128
|
+
}
|
129
|
+
end
|
130
|
+
end
|
93
131
|
|
94
|
-
class CloudFrontDistributionAlarm <
|
132
|
+
class CloudFrontDistributionAlarm < BaseAlarm
|
95
133
|
def initialize(resource)
|
96
134
|
super(resource)
|
97
|
-
@
|
135
|
+
@group = 'CloudFrontDistribution'
|
98
136
|
@namespace = 'AWS/CloudFront'
|
99
137
|
@dimensions = {
|
100
138
|
DistributionId: resource['Id'],
|
@@ -105,47 +143,47 @@ module CfnGuardian
|
|
105
143
|
end
|
106
144
|
end
|
107
145
|
|
108
|
-
class AutoScalingGroupAlarm <
|
146
|
+
class AutoScalingGroupAlarm < BaseAlarm
|
109
147
|
def initialize(resource)
|
110
148
|
super(resource)
|
111
|
-
@
|
149
|
+
@group = 'AutoScalingGroup'
|
112
150
|
@namespace = 'AWS/EC2'
|
113
151
|
@dimensions = { AutoScalingGroupName: resource['Id'] }
|
114
152
|
end
|
115
153
|
end
|
116
154
|
|
117
|
-
class DomainExpiryAlarm <
|
155
|
+
class DomainExpiryAlarm < BaseAlarm
|
118
156
|
def initialize(resource)
|
119
157
|
super(resource)
|
120
|
-
@
|
158
|
+
@group = 'DomainExpiry'
|
121
159
|
@namespace = 'DNS'
|
122
160
|
@dimensions = { Domain: resource['Id'] }
|
123
161
|
@comparison_operator = 'LessThanThreshold'
|
124
162
|
end
|
125
163
|
end
|
126
164
|
|
127
|
-
class DynamoDBTableAlarm <
|
165
|
+
class DynamoDBTableAlarm < BaseAlarm
|
128
166
|
def initialize(resource)
|
129
167
|
super(resource)
|
130
|
-
@
|
168
|
+
@group = 'DynamoDBTable'
|
131
169
|
@namespace = 'AWS/DynamoDB'
|
132
170
|
@dimensions = { TableName: resource['Id'] }
|
133
171
|
end
|
134
172
|
end
|
135
173
|
|
136
|
-
class Ec2InstanceAlarm <
|
174
|
+
class Ec2InstanceAlarm < BaseAlarm
|
137
175
|
def initialize(resource)
|
138
176
|
super(resource)
|
139
|
-
@
|
177
|
+
@group = 'Ec2Instance'
|
140
178
|
@namespace = 'AWS/EC2'
|
141
179
|
@dimensions = { InstanceId: resource['Id'] }
|
142
180
|
end
|
143
181
|
end
|
144
182
|
|
145
|
-
class ECSClusterAlarm <
|
183
|
+
class ECSClusterAlarm < BaseAlarm
|
146
184
|
def initialize(resource)
|
147
185
|
super(resource)
|
148
|
-
@
|
186
|
+
@group = 'ECSCluster'
|
149
187
|
@namespace = 'AWS/ECS'
|
150
188
|
@dimensions = { ClusterName: resource['Id'] }
|
151
189
|
@threshold = 75
|
@@ -154,10 +192,10 @@ module CfnGuardian
|
|
154
192
|
end
|
155
193
|
end
|
156
194
|
|
157
|
-
class ECSServiceAlarm <
|
195
|
+
class ECSServiceAlarm < BaseAlarm
|
158
196
|
def initialize(resource)
|
159
197
|
super(resource)
|
160
|
-
@
|
198
|
+
@group = 'ECSService'
|
161
199
|
@namespace = 'AWS/ECS'
|
162
200
|
@dimensions = {
|
163
201
|
ServiceName: resource['Id'],
|
@@ -166,37 +204,37 @@ module CfnGuardian
|
|
166
204
|
end
|
167
205
|
end
|
168
206
|
|
169
|
-
class ElastiCacheReplicationGroupAlarm <
|
207
|
+
class ElastiCacheReplicationGroupAlarm < BaseAlarm
|
170
208
|
def initialize(resource)
|
171
209
|
super(resource)
|
172
|
-
@
|
210
|
+
@group = 'ElastiCacheReplicationGroup'
|
173
211
|
@namespace = 'AWS/ElastiCache'
|
174
212
|
@dimensions = { CacheClusterId: resource['Id'] }
|
175
213
|
end
|
176
214
|
end
|
177
215
|
|
178
|
-
class ElasticLoadBalancerAlarm <
|
216
|
+
class ElasticLoadBalancerAlarm < BaseAlarm
|
179
217
|
def initialize(resource)
|
180
218
|
super(resource)
|
181
|
-
@
|
219
|
+
@group = 'ElasticLoadBalancer'
|
182
220
|
@namespace = 'AWS/ELB'
|
183
221
|
@dimensions = { LoadBalancerName: resource['Id'] }
|
184
222
|
end
|
185
223
|
end
|
186
224
|
|
187
|
-
class ElasticFileSystemAlarm <
|
225
|
+
class ElasticFileSystemAlarm < BaseAlarm
|
188
226
|
def initialize(resource)
|
189
227
|
super(resource)
|
190
|
-
@
|
228
|
+
@group = 'ElasticFileSystem'
|
191
229
|
@namespace = 'AWS/EFS'
|
192
230
|
@dimensions = { FileSystemId: resource['Id'] }
|
193
231
|
end
|
194
232
|
end
|
195
233
|
|
196
|
-
class HttpAlarm <
|
234
|
+
class HttpAlarm < BaseAlarm
|
197
235
|
def initialize(resource)
|
198
236
|
super(resource)
|
199
|
-
@
|
237
|
+
@group = 'Http'
|
200
238
|
@namespace = 'HttpCheck'
|
201
239
|
@dimensions = { Endpoint: resource['Id'] }
|
202
240
|
@comparison_operator = 'LessThanThreshold'
|
@@ -204,11 +242,51 @@ module CfnGuardian
|
|
204
242
|
@evaluation_periods = 2
|
205
243
|
end
|
206
244
|
end
|
245
|
+
|
246
|
+
class InternalHttpAlarm < HttpAlarm
|
247
|
+
def initialize(resource)
|
248
|
+
super(resource)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
class PortAlarm < BaseAlarm
|
253
|
+
def initialize(resource)
|
254
|
+
super(resource)
|
255
|
+
@group = 'Port'
|
256
|
+
@namespace = 'TcpPortCheck'
|
257
|
+
@dimensions = { Endpoint: "#{resource['Id']}:#{resource['Port']}" }
|
258
|
+
@comparison_operator = 'LessThanThreshold'
|
259
|
+
@threshold = 1
|
260
|
+
@evaluation_periods = 2
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class InternalPortAlarm < PortAlarm
|
265
|
+
def initialize(resource)
|
266
|
+
super(resource)
|
267
|
+
end
|
268
|
+
end
|
207
269
|
|
208
|
-
class
|
270
|
+
class SslAlarm < BaseAlarm
|
271
|
+
def initialize(resource)
|
272
|
+
super(resource)
|
273
|
+
@group = 'Ssl'
|
274
|
+
@namespace = 'SSL'
|
275
|
+
@dimensions = { URL: resource['Id'] }
|
276
|
+
@comparison_operator = 'LessThanThreshold'
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
class InternalSslAlarm < SslAlarm
|
281
|
+
def initialize(resource)
|
282
|
+
super(resource)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
class NrpeAlarm < BaseAlarm
|
209
287
|
def initialize(resource,environment)
|
210
288
|
super(resource)
|
211
|
-
@
|
289
|
+
@group = 'Nrpe'
|
212
290
|
@namespace = 'NRPE'
|
213
291
|
@dimensions = { Host: "#{environment}-#{resource['Id']}" }
|
214
292
|
@treat_missing_data = 'breaching'
|
@@ -216,10 +294,10 @@ module CfnGuardian
|
|
216
294
|
end
|
217
295
|
end
|
218
296
|
|
219
|
-
class LambdaAlarm <
|
297
|
+
class LambdaAlarm < BaseAlarm
|
220
298
|
def initialize(resource)
|
221
299
|
super(resource)
|
222
|
-
@
|
300
|
+
@group = 'Lambda'
|
223
301
|
@namespace = 'AWS/Lambda'
|
224
302
|
@dimensions = { FunctionName: resource['Id'] }
|
225
303
|
@statistic = 'Average'
|
@@ -227,10 +305,10 @@ module CfnGuardian
|
|
227
305
|
end
|
228
306
|
end
|
229
307
|
|
230
|
-
class NetworkTargetGroupAlarm <
|
308
|
+
class NetworkTargetGroupAlarm < BaseAlarm
|
231
309
|
def initialize(resource)
|
232
310
|
super(resource)
|
233
|
-
@
|
311
|
+
@group = 'NetworkTargetGroup'
|
234
312
|
@namespace = 'AWS/NetworkELB'
|
235
313
|
@dimensions = {
|
236
314
|
TargetGroup: resource['Id'],
|
@@ -239,37 +317,37 @@ module CfnGuardian
|
|
239
317
|
end
|
240
318
|
end
|
241
319
|
|
242
|
-
class RedshiftClusterAlarm <
|
320
|
+
class RedshiftClusterAlarm < BaseAlarm
|
243
321
|
def initialize(resource)
|
244
322
|
super(resource)
|
245
|
-
@
|
323
|
+
@group = 'RedshiftCluster'
|
246
324
|
@namespace = 'AWS/Redshift'
|
247
325
|
@dimensions = { ClusterIdentifier: resource['Id'] }
|
248
326
|
end
|
249
327
|
end
|
250
328
|
|
251
|
-
class RDSClusterInstanceAlarm <
|
329
|
+
class RDSClusterInstanceAlarm < BaseAlarm
|
252
330
|
def initialize(resource)
|
253
331
|
super(resource)
|
254
|
-
@
|
332
|
+
@group = 'RDSClusterInstance'
|
255
333
|
@namespace = 'AWS/RDS'
|
256
334
|
@dimensions = { DBInstanceIdentifier: resource['Id'] }
|
257
335
|
end
|
258
336
|
end
|
259
337
|
|
260
|
-
class RDSInstanceAlarm <
|
338
|
+
class RDSInstanceAlarm < BaseAlarm
|
261
339
|
def initialize(resource)
|
262
340
|
super(resource)
|
263
|
-
@
|
341
|
+
@group = 'RDSInstance'
|
264
342
|
@namespace = 'AWS/RDS'
|
265
343
|
@dimensions = { DBInstanceIdentifier: resource['Id'] }
|
266
344
|
end
|
267
345
|
end
|
268
346
|
|
269
|
-
class SqlAlarm <
|
347
|
+
class SqlAlarm < BaseAlarm
|
270
348
|
def initialize(resource)
|
271
349
|
super(resource)
|
272
|
-
@
|
350
|
+
@group = 'Sql'
|
273
351
|
@namespace = 'SQL'
|
274
352
|
@dimensions = { Host: resource['Id'] }
|
275
353
|
@treat_missing_data = 'breaching'
|
@@ -277,10 +355,10 @@ module CfnGuardian
|
|
277
355
|
end
|
278
356
|
end
|
279
357
|
|
280
|
-
class SQSQueueAlarm <
|
358
|
+
class SQSQueueAlarm < BaseAlarm
|
281
359
|
def initialize(resource)
|
282
360
|
super(resource)
|
283
|
-
@
|
361
|
+
@group = 'SQSQueue'
|
284
362
|
@namespace = 'AWS/SQS'
|
285
363
|
@dimensions = { QueueName: resource['Id'] }
|
286
364
|
@statistic = 'Average'
|
@@ -288,5 +366,61 @@ module CfnGuardian
|
|
288
366
|
end
|
289
367
|
end
|
290
368
|
|
369
|
+
class LogGroupAlarm < BaseAlarm
|
370
|
+
def initialize(resource)
|
371
|
+
super(resource)
|
372
|
+
@group = 'LogGroup'
|
373
|
+
@namespace = "MetricFilters"
|
374
|
+
@statistic = 'Sum'
|
375
|
+
@threshold = 1
|
376
|
+
@period = 300
|
377
|
+
@alarm_action = 'Informational'
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
class SFTPAlarm < BaseAlarm
|
382
|
+
def initialize(resource)
|
383
|
+
super(resource)
|
384
|
+
@group = 'SFTP'
|
385
|
+
@namespace = 'SftpCheck'
|
386
|
+
@period = 300
|
387
|
+
@comparison_operator = 'LessThanThreshold'
|
388
|
+
@threshold = 1
|
389
|
+
@dimensions = { Host: resource['Id'], User: resource['User'] }
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
class InternalSFTPAlarm < SFTPAlarm
|
394
|
+
def initialize(resource)
|
395
|
+
super(resource)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
class TLSAlarm < BaseAlarm
|
400
|
+
def initialize(resource)
|
401
|
+
super(resource)
|
402
|
+
@group = 'TLS'
|
403
|
+
@namespace = 'TLSVersionCheck'
|
404
|
+
@period = 300
|
405
|
+
@port = resource.fetch('Port', 443)
|
406
|
+
@dimensions = { Endpoint: "#{resource['Id']}:#{@port}" }
|
407
|
+
@comparison_operator = 'LessThanThreshold'
|
408
|
+
@threshold = 1
|
409
|
+
@evaluation_periods = 1
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
class AzureFileAlarm < BaseAlarm
|
414
|
+
def initialize(resource)
|
415
|
+
super(resource)
|
416
|
+
@group = 'AzureFile'
|
417
|
+
@namespace = 'FileAgeCheck'
|
418
|
+
@period = 300
|
419
|
+
@comparison_operator = 'GreaterThanThreshold'
|
420
|
+
@threshold = 0
|
421
|
+
@dimensions = { StorageAccount: resource['Id'], StorageContainer: resource['Container'] }
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
291
425
|
end
|
292
426
|
end
|