cfn-guardian 0.1.0 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|