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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +1 -0
  3. data/.github/workflows/build-gem.yml +25 -0
  4. data/.github/workflows/release-gem.yml +25 -0
  5. data/.github/workflows/release-image.yml +33 -0
  6. data/.rspec +1 -0
  7. data/Dockerfile +19 -0
  8. data/Gemfile.lock +39 -21
  9. data/README.md +9 -378
  10. data/cfn-guardian.gemspec +7 -5
  11. data/docs/alarm_templates.md +130 -0
  12. data/docs/cli.md +182 -0
  13. data/docs/composite_alarms.md +24 -0
  14. data/docs/custom_checks/azure_file_check.md +28 -0
  15. data/docs/custom_checks/domain_expiry.md +10 -0
  16. data/docs/custom_checks/http.md +59 -0
  17. data/docs/custom_checks/log_group_metric_filters.md +27 -0
  18. data/docs/custom_checks/nrpe.md +29 -0
  19. data/docs/custom_checks/port.md +40 -0
  20. data/docs/custom_checks/sftp.md +73 -0
  21. data/docs/custom_checks/sql.md +44 -0
  22. data/docs/custom_checks/tls.md +25 -0
  23. data/docs/custom_metrics.md +71 -0
  24. data/docs/event_subscriptions.md +67 -0
  25. data/docs/maintenance_mode.md +85 -0
  26. data/docs/notifiers.md +33 -0
  27. data/docs/overview.md +22 -0
  28. data/docs/resources.md +93 -0
  29. data/docs/variables.md +58 -0
  30. data/lib/cfnguardian.rb +325 -37
  31. data/lib/cfnguardian/cloudwatch.rb +132 -0
  32. data/lib/cfnguardian/codecommit.rb +54 -0
  33. data/lib/cfnguardian/codepipeline.rb +138 -0
  34. data/lib/cfnguardian/compile.rb +142 -18
  35. data/lib/cfnguardian/config/defaults.yaml +103 -0
  36. data/lib/cfnguardian/deploy.rb +2 -16
  37. data/lib/cfnguardian/display_formatter.rb +163 -0
  38. data/lib/cfnguardian/drift.rb +79 -0
  39. data/lib/cfnguardian/error.rb +4 -0
  40. data/lib/cfnguardian/log.rb +0 -1
  41. data/lib/cfnguardian/models/alarm.rb +193 -59
  42. data/lib/cfnguardian/models/check.rb +128 -33
  43. data/lib/cfnguardian/models/composite.rb +21 -0
  44. data/lib/cfnguardian/models/event.rb +201 -49
  45. data/lib/cfnguardian/models/event_subscription.rb +96 -0
  46. data/lib/cfnguardian/models/metric_filter.rb +28 -0
  47. data/lib/cfnguardian/resources/amazonmq_rabbitmq.rb +136 -0
  48. data/lib/cfnguardian/resources/application_targetgroup.rb +2 -0
  49. data/lib/cfnguardian/resources/azure_file.rb +20 -0
  50. data/lib/cfnguardian/resources/base.rb +155 -33
  51. data/lib/cfnguardian/resources/ec2_instance.rb +11 -0
  52. data/lib/cfnguardian/resources/ecs_service.rb +2 -2
  53. data/lib/cfnguardian/resources/http.rb +17 -1
  54. data/lib/cfnguardian/resources/internal_http.rb +74 -0
  55. data/lib/cfnguardian/resources/internal_port.rb +33 -0
  56. data/lib/cfnguardian/resources/internal_sftp.rb +58 -0
  57. data/lib/cfnguardian/resources/log_group.rb +26 -0
  58. data/lib/cfnguardian/resources/network_targetgroup.rb +1 -0
  59. data/lib/cfnguardian/resources/port.rb +25 -0
  60. data/lib/cfnguardian/resources/rds_cluster.rb +14 -0
  61. data/lib/cfnguardian/resources/rds_instance.rb +73 -0
  62. data/lib/cfnguardian/resources/redshift_cluster.rb +2 -2
  63. data/lib/cfnguardian/resources/sftp.rb +50 -0
  64. data/lib/cfnguardian/resources/sql.rb +3 -3
  65. data/lib/cfnguardian/resources/tls.rb +66 -0
  66. data/lib/cfnguardian/s3.rb +3 -2
  67. data/lib/cfnguardian/stacks/main.rb +94 -72
  68. data/lib/cfnguardian/stacks/resources.rb +111 -43
  69. data/lib/cfnguardian/string.rb +12 -0
  70. data/lib/cfnguardian/version.rb +1 -1
  71. 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
+
@@ -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
@@ -0,0 +1,4 @@
1
+ module CfnGuardian
2
+ class ValidationError < StandardError
3
+ end
4
+ end
@@ -13,7 +13,6 @@ module Logging
13
13
 
14
14
  def logger
15
15
  @logger ||= Logger.new($stdout)
16
- @logger.level = Logger::DEBUG
17
16
  @logger.formatter = proc do |severity, datetime, progname, msg|
18
17
  "\e[#{colors[severity.to_sym]}m#{severity}: #{msg}\e[0m\n"
19
18
  end
@@ -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 Alarm
6
+ class BaseAlarm
6
7
 
7
- attr_reader :type
8
- attr_accessor :class,
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
- :resource,
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
- @class = nil
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
- @resource_name = Digest::MD5.hexdigest resource['Id']
47
- @resource = resource['Id']
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 < Alarm
68
+ class ApiGatewayAlarm < BaseAlarm
65
69
  def initialize(resource)
66
70
  super(resource)
67
- @class = 'ApiGateway'
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 < Alarm
77
+ class ApplicationTargetGroupAlarm < BaseAlarm
74
78
  def initialize(resource)
75
79
  super(resource)
76
- @class = 'ApplicationTargetGroup'
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 < Alarm
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
- @class = 'AmazonMQBroker'
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 < Alarm
132
+ class CloudFrontDistributionAlarm < BaseAlarm
95
133
  def initialize(resource)
96
134
  super(resource)
97
- @class = 'CloudFrontDistribution'
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 < Alarm
146
+ class AutoScalingGroupAlarm < BaseAlarm
109
147
  def initialize(resource)
110
148
  super(resource)
111
- @class = 'AutoScalingGroup'
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 < Alarm
155
+ class DomainExpiryAlarm < BaseAlarm
118
156
  def initialize(resource)
119
157
  super(resource)
120
- @class = 'DomainExpiry'
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 < Alarm
165
+ class DynamoDBTableAlarm < BaseAlarm
128
166
  def initialize(resource)
129
167
  super(resource)
130
- @class = 'DynamoDBTable'
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 < Alarm
174
+ class Ec2InstanceAlarm < BaseAlarm
137
175
  def initialize(resource)
138
176
  super(resource)
139
- @class = 'Ec2Instance'
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 < Alarm
183
+ class ECSClusterAlarm < BaseAlarm
146
184
  def initialize(resource)
147
185
  super(resource)
148
- @class = 'ECSCluster'
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 < Alarm
195
+ class ECSServiceAlarm < BaseAlarm
158
196
  def initialize(resource)
159
197
  super(resource)
160
- @class = 'ECSService'
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 < Alarm
207
+ class ElastiCacheReplicationGroupAlarm < BaseAlarm
170
208
  def initialize(resource)
171
209
  super(resource)
172
- @class = 'ElastiCacheReplicationGroup'
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 < Alarm
216
+ class ElasticLoadBalancerAlarm < BaseAlarm
179
217
  def initialize(resource)
180
218
  super(resource)
181
- @class = 'ElasticLoadBalancer'
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 < Alarm
225
+ class ElasticFileSystemAlarm < BaseAlarm
188
226
  def initialize(resource)
189
227
  super(resource)
190
- @class = 'ElasticFileSystem'
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 < Alarm
234
+ class HttpAlarm < BaseAlarm
197
235
  def initialize(resource)
198
236
  super(resource)
199
- @class = 'Http'
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 NrpeAlarm < Alarm
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
- @class = 'Nrpe'
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 < Alarm
297
+ class LambdaAlarm < BaseAlarm
220
298
  def initialize(resource)
221
299
  super(resource)
222
- @class = 'Lambda'
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 < Alarm
308
+ class NetworkTargetGroupAlarm < BaseAlarm
231
309
  def initialize(resource)
232
310
  super(resource)
233
- @class = 'NetworkTargetGroup'
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 < Alarm
320
+ class RedshiftClusterAlarm < BaseAlarm
243
321
  def initialize(resource)
244
322
  super(resource)
245
- @class = 'RedshiftCluster'
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 < Alarm
329
+ class RDSClusterInstanceAlarm < BaseAlarm
252
330
  def initialize(resource)
253
331
  super(resource)
254
- @class = 'RDSClusterInstance'
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 < Alarm
338
+ class RDSInstanceAlarm < BaseAlarm
261
339
  def initialize(resource)
262
340
  super(resource)
263
- @class = 'RDSInstance'
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 < Alarm
347
+ class SqlAlarm < BaseAlarm
270
348
  def initialize(resource)
271
349
  super(resource)
272
- @class = 'Sql'
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 < Alarm
358
+ class SQSQueueAlarm < BaseAlarm
281
359
  def initialize(resource)
282
360
  super(resource)
283
- @class = 'SQSQueue'
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