cfn-guardian 0.1.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +1 -0
  3. data/Dockerfile +19 -0
  4. data/Gemfile.lock +31 -13
  5. data/README.md +441 -42
  6. data/cfn-guardian.gemspec +6 -2
  7. data/lib/cfnguardian.rb +301 -27
  8. data/lib/cfnguardian/cloudwatch.rb +121 -0
  9. data/lib/cfnguardian/codecommit.rb +54 -0
  10. data/lib/cfnguardian/codepipeline.rb +138 -0
  11. data/lib/cfnguardian/compile.rb +58 -17
  12. data/lib/cfnguardian/config/defaults.yaml +94 -0
  13. data/lib/cfnguardian/display_formatter.rb +164 -0
  14. data/lib/cfnguardian/drift.rb +79 -0
  15. data/lib/cfnguardian/log.rb +0 -1
  16. data/lib/cfnguardian/models/alarm.rb +98 -36
  17. data/lib/cfnguardian/models/check.rb +103 -26
  18. data/lib/cfnguardian/models/composite.rb +21 -0
  19. data/lib/cfnguardian/models/event.rb +164 -40
  20. data/lib/cfnguardian/models/metric_filter.rb +28 -0
  21. data/lib/cfnguardian/resources/application_targetgroup.rb +2 -0
  22. data/lib/cfnguardian/resources/base.rb +38 -16
  23. data/lib/cfnguardian/resources/ecs_service.rb +2 -2
  24. data/lib/cfnguardian/resources/http.rb +16 -1
  25. data/lib/cfnguardian/resources/internal_http.rb +74 -0
  26. data/lib/cfnguardian/resources/internal_port.rb +33 -0
  27. data/lib/cfnguardian/resources/internal_sftp.rb +58 -0
  28. data/lib/cfnguardian/resources/log_group.rb +26 -0
  29. data/lib/cfnguardian/resources/network_targetgroup.rb +1 -0
  30. data/lib/cfnguardian/resources/port.rb +25 -0
  31. data/lib/cfnguardian/resources/rds_instance.rb +2 -0
  32. data/lib/cfnguardian/resources/sftp.rb +50 -0
  33. data/lib/cfnguardian/resources/sql.rb +1 -1
  34. data/lib/cfnguardian/resources/tls.rb +66 -0
  35. data/lib/cfnguardian/s3.rb +3 -2
  36. data/lib/cfnguardian/stacks/main.rb +86 -65
  37. data/lib/cfnguardian/stacks/resources.rb +81 -42
  38. data/lib/cfnguardian/string.rb +12 -0
  39. data/lib/cfnguardian/version.rb +1 -1
  40. metadata +102 -5
@@ -0,0 +1,21 @@
1
+ module CfnGuardian
2
+ module Models
3
+ class Composite
4
+
5
+ attr_reader :type
6
+ attr_accessor :name,
7
+ :description,
8
+ :rule,
9
+ :alarm_action
10
+
11
+ def initialize(name,params = {})
12
+ @type = 'Composite'
13
+ @name = name
14
+ @description = params.fetch('Description', '')
15
+ @rule = params.fetch('Rule', 'FALSE')
16
+ @alarm_action = params.fetch('Action', nil)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -5,43 +5,30 @@ module CfnGuardian
5
5
  class Event
6
6
 
7
7
  attr_reader :type
8
- attr_accessor :class,
8
+ attr_accessor :group,
9
9
  :target,
10
10
  :hash,
11
11
  :name,
12
12
  :cron,
13
13
  :enabled,
14
- :resource
14
+ :resource,
15
+ :environment,
16
+ :payload,
17
+ :ssm_parameters
15
18
 
16
19
  def initialize(resource)
17
20
  @type = 'Event'
18
- @class = nil
21
+ @group = nil
19
22
  @target = nil
20
23
  @hash = Digest::MD5.hexdigest resource['Id']
21
24
  @name = @hash
22
25
  @cron = "* * * * ? *"
23
26
  @enabled = true
24
27
  @resource = resource['Id'].to_resource_name
25
- end
26
-
27
- def to_h
28
- return {
29
- type: @type,
30
- class: @class,
31
- target: @target,
32
- hash: @hash,
33
- name: @name,
34
- cron: @cron,
35
- enabled: @enabled,
36
- resource: @resource,
37
- payload: event_payload()
38
- }
39
- end
40
-
41
- def event_payload
42
- {}.to_json
43
- end
44
-
28
+ @environment = ""
29
+ @payload = {}.to_json
30
+ @ssm_parameters = []
31
+ end
45
32
  end
46
33
 
47
34
  class HttpEvent < Event
@@ -56,7 +43,7 @@ module CfnGuardian
56
43
 
57
44
  def initialize(resource)
58
45
  super(resource)
59
- @class = 'Http'
46
+ @group = 'Http'
60
47
  @name = 'HttpEvent'
61
48
  @target = 'HttpCheckFunction'
62
49
  @endpoint = resource['Id']
@@ -66,9 +53,10 @@ module CfnGuardian
66
53
  @body_regex = resource.fetch('BodyRegex',nil)
67
54
  @headers = resource.fetch('Headers',nil)
68
55
  @payload = resource.fetch('Payload',nil)
56
+ @compressed = resource.fetch('Compressed',false)
69
57
  end
70
58
 
71
- def event_payload
59
+ def payload
72
60
  payload = {
73
61
  'ENDPOINT' => @endpoint,
74
62
  'METHOD' => @method,
@@ -78,14 +66,56 @@ module CfnGuardian
78
66
  payload['BODY_REGEX_MATCH'] = @body_regex unless @body_regex.nil?
79
67
  payload['HEADERS'] = @headers unless @headers.nil?
80
68
  payload['PAYLOAD'] = @payload unless @payload.nil?
69
+ payload['COMPRESSED'] = '1' if @compressed
81
70
  return payload.to_json
82
71
  end
83
72
  end
84
73
 
85
- class NrpeEvent < Event
74
+ class InternalHttpEvent < HttpEvent
75
+ def initialize(resource,environment)
76
+ super(resource)
77
+ @group = 'InternalHttp'
78
+ @name = 'InternalHttpEvent'
79
+ @target = "InternalHttpCheckFunction#{environment}"
80
+ @environment = environment
81
+ end
82
+ end
83
+
84
+ class PortEvent < Event
85
+ def initialize(resource)
86
+ super(resource)
87
+ @group = 'Port'
88
+ @name = 'PortEvent'
89
+ @target = 'PortCheckFunction'
90
+ @hostname = resource['Id']
91
+ @port = resource['Port']
92
+ @timeout = resource.fetch('Timeout',120)
93
+ end
94
+
95
+ def payload
96
+ return {
97
+ 'HOSTNAME' => @hostname,
98
+ 'PORT' => @port,
99
+ 'TIMEOUT' => @timeout,
100
+ 'STATUS_CODE_MATCH' => @status_code
101
+ }.to_json
102
+ end
103
+ end
104
+
105
+ class InternalPortEvent < PortEvent
106
+ def initialize(resource,environment)
107
+ super(resource)
108
+ @group = 'InternalPort'
109
+ @name = 'InternalPortEvent'
110
+ @target = "InternalPortCheckFunction#{environment}"
111
+ @environment = environment
112
+ end
113
+ end
114
+
115
+ class NrpeEvent < Event
86
116
  def initialize(resource,environment,command)
87
117
  super(resource)
88
- @class = 'Nrpe'
118
+ @group = 'Nrpe'
89
119
  @name = 'NrpeEvent'
90
120
  @target = "NrpeCheckFunction#{environment}"
91
121
  @host = resource['Id']
@@ -94,7 +124,7 @@ module CfnGuardian
94
124
  @command = command
95
125
  end
96
126
 
97
- def event_payload
127
+ def payload
98
128
  return {
99
129
  'host' => @host,
100
130
  'environment' => @environment,
@@ -107,7 +137,7 @@ module CfnGuardian
107
137
  class SslEvent < Event
108
138
  def initialize(resource)
109
139
  super(resource)
110
- @class = 'Ssl'
140
+ @group = 'Ssl'
111
141
  @name = 'SslEvent'
112
142
  @target = 'SslCheckFunction'
113
143
  @cron = "0 12 * * ? *"
@@ -115,7 +145,7 @@ module CfnGuardian
115
145
  @region = resource.fetch('Region',"${AWS::Region}")
116
146
  end
117
147
 
118
- def event_payload
148
+ def payload
119
149
  return {
120
150
  'Url' => @url,
121
151
  'Region' => @region
@@ -123,6 +153,16 @@ module CfnGuardian
123
153
  end
124
154
  end
125
155
 
156
+ class InternalSslEvent < SslEvent
157
+ def initialize(resource,environment)
158
+ super(resource)
159
+ @group = 'InternalSsl'
160
+ @name = 'InternalSslEvent'
161
+ @target = "InternalSslCheckFunction#{environment}"
162
+ @environment = environment
163
+ end
164
+ end
165
+
126
166
  class DomainExpiryEvent < Event
127
167
 
128
168
  attr_accessor :domain,
@@ -130,7 +170,7 @@ module CfnGuardian
130
170
 
131
171
  def initialize(resource)
132
172
  super(resource)
133
- @class = 'DomainExpiry'
173
+ @group = 'DomainExpiry'
134
174
  @name = 'DomainExpiryEvent'
135
175
  @target = 'DomainExpiryCheckFunction'
136
176
  @cron = "0 12 * * ? *"
@@ -138,17 +178,17 @@ module CfnGuardian
138
178
  @region = resource.fetch('Region',"${AWS::Region}")
139
179
  end
140
180
 
141
- def event_payload
142
- {'Domain' => @domain}.to_json
181
+ def payload
182
+ return {'Domain' => @domain}.to_json
143
183
  end
144
184
  end
145
185
 
146
186
  class SqlEvent < Event
147
- def initialize(resource,query)
187
+ def initialize(resource,query,environment)
148
188
  super(resource)
149
- @class = 'Sql'
189
+ @group = 'Sql'
150
190
  @name = 'SqlEvent'
151
- @target = 'SqlCheckFunction'
191
+ @target = "SqlCheckFunction#{environment}"
152
192
  @host = resource['Id']
153
193
  @engine = resource['Engine']
154
194
  @port = resource['Port']
@@ -157,9 +197,10 @@ module CfnGuardian
157
197
  @query = query
158
198
  @region = resource.fetch('Region',"${AWS::Region}")
159
199
  @test_type = '1-row-1-value-zero-is-good'
200
+ @environment = environment
160
201
  end
161
202
 
162
- def event_payload
203
+ def payload
163
204
  return {
164
205
  'Host' => @host,
165
206
  'Engine' => @engine,
@@ -171,20 +212,103 @@ module CfnGuardian
171
212
  'TestType' => @test_type
172
213
  }.to_json
173
214
  end
215
+
216
+ def ssm_parameters
217
+ params = []
218
+ params << @ssm_username
219
+ params << @ssm_password
220
+ return params
221
+ end
174
222
  end
175
223
 
176
224
  class ContainerInstanceEvent < Event
177
225
  def initialize(resource)
178
226
  super(resource)
179
- @class = 'ContainerInstance'
227
+ @group = 'ContainerInstance'
180
228
  @name = 'ContainerInstanceEvent'
181
229
  @target = 'ContainerInstanceCheckFunction'
182
230
  @cron = "0/5 * * * ? *"
183
231
  @cluster = resource['Id']
184
232
  end
185
233
 
186
- def event_payload
187
- {'CLUSTER' => @cluster}.to_json
234
+ def payload
235
+ return {'CLUSTER' => @cluster}.to_json
236
+ end
237
+ end
238
+
239
+ class SFTPEvent < Event
240
+ def initialize(resource)
241
+ super(resource)
242
+ @group = 'SFTP'
243
+ @name = 'SFTPEvent'
244
+ @target = 'SFTPCheckFunction'
245
+ @cron = "0/5 * * * ? *"
246
+ @host = resource['Id']
247
+ @user = resource['User']
248
+ @port = resource.fetch('Port', nil)
249
+ @server_key = resource.fetch('ServerKey', nil)
250
+ @password = resource.fetch('Password', nil)
251
+ @private_key = resource.fetch('PrivateKey', nil)
252
+ @private_key_pass = resource.fetch('PrivateKeyPass', nil)
253
+ @file = resource.fetch('File', nil)
254
+ @file_regex_match = resource.fetch('FileRegexMatch', nil)
255
+ end
256
+
257
+ def payload
258
+ payload = {
259
+ 'HOSTNAME' => @host,
260
+ 'USERNAME' => @user
261
+ }
262
+ payload['PORT'] = @port unless @port.nil?
263
+ payload['SERVER_KEY'] = @server_key unless @server_key.nil?
264
+ payload['PASSWORD'] = @password unless @password.nil?
265
+ payload['PRIVATEKEY'] = @private_key unless @private_key.nil?
266
+ payload['PRIVATEKEY_PASSWORD'] = @private_key_pass unless @private_key_pass.nil?
267
+ payload['FILE'] = @file unless @file.nil?
268
+ payload['FILE_REGEX_MATCH'] = @file_regex_match unless @file_regex_match.nil?
269
+ return payload.to_json
270
+ end
271
+
272
+ def ssm_parameters
273
+ params = []
274
+ params << @password unless @password.nil?
275
+ params << @private_key unless @private_key.nil?
276
+ params << @private_key_pass unless @private_key_pass.nil?
277
+ return params
278
+ end
279
+ end
280
+
281
+ class InternalSFTPEvent < SFTPEvent
282
+ def initialize(resource,environment)
283
+ super(resource)
284
+ @group = 'InternalSFTP'
285
+ @name = 'InternalSFTPEvent'
286
+ @target = "InternalSFTPCheckFunction#{environment}"
287
+ @environment = environment
288
+ end
289
+ end
290
+
291
+ class TLSEvent < Event
292
+ def initialize(resource)
293
+ super(resource)
294
+ @group = 'TLS'
295
+ @name = 'TLSEvent'
296
+ @target = 'TLSCheckFunction'
297
+ @cron = "0/5 * * * ? *"
298
+ @host = resource['Id']
299
+ @port = resource.fetch('Port', 443)
300
+ @check_max = resource.fetch('MaxSupported', nil)
301
+ @versions = resource.fetch('Versions', ['SSLv2','SSLv3','TLSv1','TLSv1.1','TLSv1.2'])
302
+ end
303
+
304
+ def payload
305
+ payload = {
306
+ 'HOSTNAME' => @host,
307
+ 'PORT' => @port
308
+ }
309
+ payload['CHECK_MAX_SUPPORTED'] = @check_max.nil?
310
+ payload['PROTOCOLS'] = @versions unless @versions.nil?
311
+ return payload.to_json
188
312
  end
189
313
  end
190
314
 
@@ -0,0 +1,28 @@
1
+ require 'cfnguardian/string'
2
+ require 'digest/md5'
3
+
4
+ module CfnGuardian
5
+ module Models
6
+ class MetricFilter
7
+
8
+ attr_reader :type,
9
+ :metric_namespace,
10
+ :name
11
+ attr_accessor :log_group,
12
+ :pattern,
13
+ :metric_value,
14
+ :metric_name
15
+
16
+ def initialize(log_group,filter)
17
+ @type = 'MetricFilter'
18
+ @name = Digest::MD5.hexdigest(log_group + filter['MetricName'])
19
+ @log_group = log_group
20
+ @pattern = filter['Pattern']
21
+ @metric_value = filter.fetch('MetricValue', '1').to_s
22
+ @metric_name = filter['MetricName']
23
+ @metric_namespace = "MetricFilters"
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -5,9 +5,11 @@ module CfnGuardian::Resource
5
5
  alarm = CfnGuardian::Models::ApplicationTargetGroupAlarm.new(@resource)
6
6
  alarm.name = 'HealthyHosts'
7
7
  alarm.metric_name = 'HealthyHostCount'
8
+ alarm.comparison_operator = 'LessThanThreshold'
8
9
  alarm.statistic = 'Minimum'
9
10
  alarm.threshold = 2
10
11
  alarm.evaluation_periods = 1
12
+ alarm.comparison_operator = 'LessThanThreshold'
11
13
  @alarms.push(alarm)
12
14
 
13
15
  alarm = CfnGuardian::Models::ApplicationTargetGroupAlarm.new(@resource)
@@ -1,24 +1,29 @@
1
1
  require 'cfnguardian/string'
2
+ require 'cfnguardian/cloudwatch'
2
3
  require 'cfnguardian/models/alarm'
3
4
  require 'cfnguardian/models/event'
4
5
  require 'cfnguardian/models/check'
6
+ require 'cfnguardian/models/metric_filter'
5
7
 
6
8
  module CfnGuardian::Resource
7
9
  class Base
8
10
  include Logging
9
11
 
10
- def initialize(resource)
12
+ def initialize(resource, override_group = nil)
11
13
  @resource = resource
14
+ @override_group = override_group
12
15
  @alarms = []
13
16
  @events = []
14
17
  @checks = []
18
+ @metric_filters = []
15
19
  end
16
20
 
21
+ # Overidden by inheritted classes to define default alarms
17
22
  def default_alarms()
18
23
  return @alarms
19
24
  end
20
25
 
21
- def get_alarms(overides={})
26
+ def get_alarms(overides={},resource={})
22
27
  # generate default alarms
23
28
  default_alarms()
24
29
 
@@ -31,18 +36,20 @@ module CfnGuardian::Resource
31
36
 
32
37
  if !alarm.nil?
33
38
  alarm.enabled = false
34
- logger.debug "Disabling alarm '#{name}' for resource #{alarm.resource}"
39
+ logger.debug "Disabling alarm '#{name}' for resource #{alarm.resource_id}"
35
40
  next
36
41
  end
37
42
  end
38
43
 
44
+ # continue if the override is in the incorrect format
39
45
  unless properties.is_a?(Hash)
40
46
  if name != 'Inherit'
41
- logger.warn "Incorrect format for alarm '#{name}'. Should be of type 'Hash', instead got type '#{properties.class}'"
47
+ logger.warn "Incorrect format for alarm '#{name}'. Should be of type 'Hash', instead got type '#{properties.group}'"
42
48
  end
43
49
  next
44
50
  end
45
51
 
52
+ # Create a new alarm inheriting the defaults of an existing alarm
46
53
  if properties.has_key?('Inherit')
47
54
  alarm = find_alarm(properties['Inherit'])
48
55
  if !alarm.nil?
@@ -59,37 +66,52 @@ module CfnGuardian::Resource
59
66
  alarm = find_alarm(name)
60
67
 
61
68
  if alarm.nil?
62
- alarm = Kernel.const_get("CfnGuardian::Models::#{self.class.to_s.split('::').last}Alarm").new(properties)
63
- end
64
-
65
- if alarm.name.nil?
69
+ # if alarm doesn't exist create a new one
70
+ alarm = Kernel.const_get("CfnGuardian::Models::#{self.class.to_s.split('::').last}Alarm").new(resource)
71
+ properties.each {|attr,value| update_alarm(alarm,attr,value)}
66
72
  alarm.name = name
73
+ @alarms.push(alarm)
74
+ else
75
+ # if there is an existing alarm update the properties
76
+ properties.each {|attr,value| update_alarm(alarm,attr,value)}
67
77
  end
68
-
69
- properties.each {|attr,value| update_alarm(alarm,attr,value)}
70
- @alarms.push(alarm)
71
-
72
78
  end
73
79
 
74
- return @alarms.select{|a| a.enabled}.map {|a| a.to_h}
80
+ unless @override_group.nil?
81
+ @alarms.each {|a| a.group = @override_group}
82
+ end
83
+
84
+ return @alarms.select{|a| a.enabled}
75
85
  end
76
86
 
87
+ # Overidden by inheritted classes to define default events
77
88
  def default_events()
78
89
  return @events
79
90
  end
80
91
 
81
92
  def get_events()
82
93
  default_events()
83
- return @events.select{|e| e.enabled}.map {|e| e.to_h}
94
+ return @events.select{|e| e.enabled}
84
95
  end
85
96
 
97
+ # Overidden by inheritted classes to define default checks
86
98
  def default_checks()
87
99
  return @checks
88
100
  end
89
101
 
90
102
  def get_checks()
91
103
  default_checks()
92
- return @checks.map {|c| c.to_h}
104
+ return @checks
105
+ end
106
+
107
+ # Overidden by inheritted classes to define default checks
108
+ def default_metric_filters()
109
+ return @metric_filters
110
+ end
111
+
112
+ def get_metric_filters()
113
+ default_metric_filters()
114
+ return @metric_filters
93
115
  end
94
116
 
95
117
  def get_cost()
@@ -107,7 +129,7 @@ module CfnGuardian::Resource
107
129
  alarm.send("#{attr.to_underscore}=",value)
108
130
  rescue NoMethodError => e
109
131
  if !e.message.match?(/inherit/)
110
- logger.warn "Unknown key '#{attr}' for #{alarm.resource} alarm #{alarm.name}"
132
+ logger.warn "Unknown key '#{attr}' for #{alarm.resource_id} alarm #{alarm.name}"
111
133
  end
112
134
  end
113
135
  end