terrafying-components 1.11.17 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/terrafying/components/security.rb +7 -0
- data/lib/terrafying/components/security/config.rb +238 -0
- data/lib/terrafying/components/security/config_aggregator.rb +77 -0
- data/lib/terrafying/components/security/pagerduty_topic.rb +61 -0
- data/lib/terrafying/components/security/store.rb +125 -0
- data/lib/terrafying/components/security/trail.rb +304 -0
- data/lib/terrafying/components/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e8b1f7525856d30f5b3c2b5c776299d2431c3ac30302e9a38b7bde28524f3c7
|
4
|
+
data.tar.gz: c2b07db3700099242c7fc134742b15ccfee8aa9bf42a4bc8cacf2eb53831c7a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fb6c161d5f16620b0f11e1314fb9bed55eafe18c73186d2b0cc38936f78897950bb32f701b78317aa34be29c34416f5a5bcafd718b3c57895feda0d1e6d176b
|
7
|
+
data.tar.gz: 63f1f9a590d7603dec804f1d684532ed82b21b24869eb0b3aff6f461a09cea35a00773afa27123ab2e744e7ee573d947374c04c5d06bb5c463db8b4103c85594
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terrafying/components/security/config'
|
4
|
+
require 'terrafying/components/security/config_aggregator'
|
5
|
+
require 'terrafying/components/security/pagerduty_topic'
|
6
|
+
require 'terrafying/components/security/store'
|
7
|
+
require 'terrafying/components/security/trail'
|
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terrafying'
|
4
|
+
|
5
|
+
module Terrafying
|
6
|
+
|
7
|
+
module Components
|
8
|
+
|
9
|
+
module Security
|
10
|
+
|
11
|
+
class Config < Terrafying::Context
|
12
|
+
|
13
|
+
def self.create(*args)
|
14
|
+
Config.new.create(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.bucket_statements(bucket_name)
|
18
|
+
[
|
19
|
+
{
|
20
|
+
Sid: "AWSConfigAclCheck",
|
21
|
+
Effect: "Allow",
|
22
|
+
Principal: {
|
23
|
+
Service: "config.amazonaws.com"
|
24
|
+
},
|
25
|
+
Action: "s3:GetBucketAcl",
|
26
|
+
Resource: "arn:aws:s3:::#{bucket_name}"
|
27
|
+
},
|
28
|
+
{
|
29
|
+
Sid: "AWSConfigWrite",
|
30
|
+
Effect: "Allow",
|
31
|
+
Principal: {
|
32
|
+
Service: "config.amazonaws.com"
|
33
|
+
},
|
34
|
+
Action: "s3:PutObject",
|
35
|
+
Resource: "arn:aws:s3:::#{bucket_name}/*",
|
36
|
+
Condition: {
|
37
|
+
StringEquals: {
|
38
|
+
"s3:x-amz-acl" => "bucket-owner-full-control"
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def create(
|
47
|
+
name,
|
48
|
+
provider:,
|
49
|
+
store:,
|
50
|
+
include_global:
|
51
|
+
)
|
52
|
+
|
53
|
+
ident = tf_safe(name)
|
54
|
+
|
55
|
+
@name = name
|
56
|
+
@ident = ident
|
57
|
+
@provider = provider
|
58
|
+
@include_global = include_global
|
59
|
+
|
60
|
+
role = resource :aws_iam_role, ident, {
|
61
|
+
provider: @provider,
|
62
|
+
name: name,
|
63
|
+
assume_role_policy: {
|
64
|
+
Version: "2012-10-17",
|
65
|
+
Statement: [
|
66
|
+
{
|
67
|
+
Action: "sts:AssumeRole",
|
68
|
+
Principal: {
|
69
|
+
Service: "config.amazonaws.com"
|
70
|
+
},
|
71
|
+
Effect: "Allow",
|
72
|
+
Sid: ""
|
73
|
+
}
|
74
|
+
]
|
75
|
+
}.to_json,
|
76
|
+
}
|
77
|
+
|
78
|
+
policy = resource :aws_iam_policy, ident, {
|
79
|
+
provider: @provider,
|
80
|
+
policy: {
|
81
|
+
Version: "2012-10-17",
|
82
|
+
Statement: store.write_statements,
|
83
|
+
}.to_json,
|
84
|
+
}
|
85
|
+
|
86
|
+
resource :aws_iam_role_policy_attachment, ident, {
|
87
|
+
provider: @provider,
|
88
|
+
role: role["name"],
|
89
|
+
policy_arn: policy["arn"],
|
90
|
+
}
|
91
|
+
|
92
|
+
resource :aws_iam_role_policy_attachment, "#{ident}-config-policy", {
|
93
|
+
provider: @provider,
|
94
|
+
role: role["name"],
|
95
|
+
policy_arn: "arn:aws:iam::aws:policy/service-role/AWSConfigRole",
|
96
|
+
}
|
97
|
+
|
98
|
+
recorder = resource :aws_config_configuration_recorder, ident, {
|
99
|
+
provider: @provider,
|
100
|
+
name: name,
|
101
|
+
role_arn: role["arn"],
|
102
|
+
recording_group: {
|
103
|
+
include_global_resource_types: include_global,
|
104
|
+
},
|
105
|
+
}
|
106
|
+
|
107
|
+
resource :aws_config_delivery_channel, ident, {
|
108
|
+
provider: @provider,
|
109
|
+
s3_bucket_name: store.name,
|
110
|
+
s3_key_prefix: "config",
|
111
|
+
depends_on: [ "aws_config_configuration_recorder.#{ident}" ],
|
112
|
+
}
|
113
|
+
|
114
|
+
resource :aws_config_configuration_recorder_status, ident, {
|
115
|
+
provider: @provider,
|
116
|
+
name: recorder["name"],
|
117
|
+
is_enabled: true,
|
118
|
+
depends_on: [ "aws_config_delivery_channel.#{ident}" ],
|
119
|
+
}
|
120
|
+
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
124
|
+
def rule!(name:, source:, input: nil)
|
125
|
+
ident = tf_safe("#{@name}-#{name}")
|
126
|
+
|
127
|
+
if source.is_a? Symbol
|
128
|
+
source_config = {
|
129
|
+
owner: "AWS",
|
130
|
+
source_identifier: source.to_s,
|
131
|
+
}
|
132
|
+
else
|
133
|
+
raise "Can't support a non-AWS source at the moment"
|
134
|
+
end
|
135
|
+
|
136
|
+
resource :aws_config_config_rule, ident, {
|
137
|
+
provider: @provider,
|
138
|
+
name: name,
|
139
|
+
source: source_config,
|
140
|
+
input_parameters: input,
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
def cis_benchmark!
|
145
|
+
|
146
|
+
if @include_global #IAM is a global resource, so the rules are only applicable where they are collected
|
147
|
+
|
148
|
+
# 1.2 Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password
|
149
|
+
rule!(
|
150
|
+
name: "AllUsersMFA",
|
151
|
+
source: :IAM_USER_MFA_ENABLED,
|
152
|
+
)
|
153
|
+
rule!(
|
154
|
+
name: "AllConsoleUsersMFA",
|
155
|
+
source: :MFA_ENABLED_FOR_IAM_CONSOLE_ACCESS,
|
156
|
+
)
|
157
|
+
|
158
|
+
# 1.3 Ensure credentials unused for 90 days or greater are disabled
|
159
|
+
rule!(
|
160
|
+
name: "CredentialsOlder90Disabled",
|
161
|
+
source: :IAM_USER_UNUSED_CREDENTIALS_CHECK,
|
162
|
+
input: {
|
163
|
+
"maxCredentialUsageAge" => "90",
|
164
|
+
}.to_json,
|
165
|
+
)
|
166
|
+
|
167
|
+
# 1.4 Ensure access keys are rotated every 90 days or less
|
168
|
+
rule!(
|
169
|
+
name: "AccessKeysRotated",
|
170
|
+
source: :ACCESS_KEYS_ROTATED,
|
171
|
+
input: {
|
172
|
+
"maxAccessKeyAge" => "90",
|
173
|
+
}.to_json,
|
174
|
+
)
|
175
|
+
|
176
|
+
# 1.12 Ensure no root account access key exists
|
177
|
+
rule!(
|
178
|
+
name: "NoRootAccessKey",
|
179
|
+
source: :IAM_ROOT_ACCESS_KEY_CHECK,
|
180
|
+
)
|
181
|
+
|
182
|
+
# 1.13 Ensure MFA is enabled for the "root" account
|
183
|
+
rule!(
|
184
|
+
name: "RootMFA",
|
185
|
+
source: :ROOT_ACCOUNT_MFA_ENABLED,
|
186
|
+
)
|
187
|
+
|
188
|
+
# 1.14 Ensure hardware MFA is enabled for the "root" account
|
189
|
+
rule!(
|
190
|
+
name: "RootHardwareMFA",
|
191
|
+
source: :ROOT_ACCOUNT_MFA_ENABLED,
|
192
|
+
)
|
193
|
+
|
194
|
+
# 1.16 Ensure IAM policies are attached only to groups or roles
|
195
|
+
#rule!(
|
196
|
+
# name: "
|
197
|
+
# )
|
198
|
+
|
199
|
+
|
200
|
+
# 1.22 Ensure IAM policies that allow full "*:*" administrative privileges are not created
|
201
|
+
rule!(
|
202
|
+
name: "NoIAMAdminAccess",
|
203
|
+
source: :IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS,
|
204
|
+
)
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
# 2.8 Ensure rotation for customer created CMKs is enabled
|
209
|
+
#rule!(
|
210
|
+
# name: "EnsureCMKRotationEnabled",
|
211
|
+
# source: lamba,
|
212
|
+
# )
|
213
|
+
|
214
|
+
# 2.9 Ensure VPC flow logging is enabled in all VPCs
|
215
|
+
#rule!(
|
216
|
+
# name: "EnsureFlowLoggingEnabled",
|
217
|
+
# source: lamba,
|
218
|
+
# )
|
219
|
+
|
220
|
+
|
221
|
+
# 4.1 Ensure no security groups allow ingress from 0.0.0.0/0 to port 22
|
222
|
+
# 4.2 Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389
|
223
|
+
rule!(
|
224
|
+
name: "NoNaughtyIncomingTraffic",
|
225
|
+
source: :RESTRICTED_INCOMING_TRAFFIC,
|
226
|
+
input: {
|
227
|
+
"blockedPort1" => "22",
|
228
|
+
"blockedPort2" => "3389",
|
229
|
+
}.to_json
|
230
|
+
)
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terrafying'
|
4
|
+
|
5
|
+
module Terrafying
|
6
|
+
|
7
|
+
module Components
|
8
|
+
|
9
|
+
module Security
|
10
|
+
|
11
|
+
class ConfigAggregator < Terrafying::Context
|
12
|
+
|
13
|
+
def self.create(*args)
|
14
|
+
ConfigAggregator.new.create(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(
|
18
|
+
name,
|
19
|
+
whole_organisation: false
|
20
|
+
)
|
21
|
+
|
22
|
+
ident = tf_safe(name)
|
23
|
+
|
24
|
+
role = resource :aws_iam_role, ident, {
|
25
|
+
name: name,
|
26
|
+
assume_role_policy: {
|
27
|
+
Version: "2012-10-17",
|
28
|
+
Statement: [
|
29
|
+
{
|
30
|
+
Action: "sts:AssumeRole",
|
31
|
+
Principal: {
|
32
|
+
Service: "config.amazonaws.com"
|
33
|
+
},
|
34
|
+
Effect: "Allow",
|
35
|
+
Sid: ""
|
36
|
+
}
|
37
|
+
]
|
38
|
+
}.to_json,
|
39
|
+
}
|
40
|
+
|
41
|
+
resource :aws_iam_role_policy_attachment, "#{ident}-config-org-policy", {
|
42
|
+
provider: @provider,
|
43
|
+
role: role["name"],
|
44
|
+
policy_arn: "arn:aws:iam::aws:policy/service-role/AWSConfigRoleForOrganizations",
|
45
|
+
}
|
46
|
+
|
47
|
+
source = {}
|
48
|
+
|
49
|
+
if whole_organisation
|
50
|
+
source[:organization_aggregation_source] = {
|
51
|
+
all_regions: true,
|
52
|
+
role_arn: role["arn"],
|
53
|
+
}
|
54
|
+
else
|
55
|
+
source[:account_aggregation_source] = {
|
56
|
+
account_ids: [ aws.account_id ],
|
57
|
+
all_regions: true,
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
resource :aws_config_configuration_aggregator, ident, {
|
62
|
+
depends_on: [ "aws_iam_role_policy_attachment.#{ident}-config-org-policy" ],
|
63
|
+
|
64
|
+
name: name,
|
65
|
+
organization_aggregation_source: {
|
66
|
+
all_regions: true,
|
67
|
+
role_arn: role["arn"],
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terrafying'
|
4
|
+
|
5
|
+
module Terrafying
|
6
|
+
|
7
|
+
module Components
|
8
|
+
|
9
|
+
module Security
|
10
|
+
|
11
|
+
class PagerdutyTopic < Terrafying::Context
|
12
|
+
|
13
|
+
attr_reader :arn
|
14
|
+
|
15
|
+
def self.create(*args)
|
16
|
+
PagerdutyTopic.new.create(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create(
|
20
|
+
name,
|
21
|
+
escalation_policy_id:
|
22
|
+
)
|
23
|
+
|
24
|
+
ident = tf_safe(name)
|
25
|
+
|
26
|
+
service = resource :pagerduty_service, ident, {
|
27
|
+
name: name,
|
28
|
+
auto_resolve_timeout: 14400,
|
29
|
+
acknowledgement_timeout: 600,
|
30
|
+
escalation_policy: escalation_policy_id,
|
31
|
+
}
|
32
|
+
|
33
|
+
vendor = data :pagerduty_vendor, ident, {
|
34
|
+
name: "Amazon CloudWatch",
|
35
|
+
}
|
36
|
+
|
37
|
+
integration = resource :pagerduty_service_integration, ident, {
|
38
|
+
name: "SNS",
|
39
|
+
vendor: vendor["id"],
|
40
|
+
service: service["id"],
|
41
|
+
}
|
42
|
+
|
43
|
+
topic = resource :aws_sns_topic, ident, {}
|
44
|
+
|
45
|
+
@arn = topic["arn"]
|
46
|
+
|
47
|
+
resource :aws_sns_topic_subscription, ident, {
|
48
|
+
topic_arn: @arn,
|
49
|
+
protocol: "https",
|
50
|
+
endpoint: "https://events.pagerduty.com/integration/#{integration["integration_key"]}/enqueue",
|
51
|
+
endpoint_auto_confirms: true,
|
52
|
+
}
|
53
|
+
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terrafying'
|
4
|
+
|
5
|
+
module Terrafying
|
6
|
+
|
7
|
+
module Components
|
8
|
+
|
9
|
+
module Security
|
10
|
+
|
11
|
+
class Store < Terrafying::Context
|
12
|
+
|
13
|
+
attr_reader :name, :key_arn
|
14
|
+
|
15
|
+
def self.create(*args)
|
16
|
+
Store.new.create(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create(
|
20
|
+
name,
|
21
|
+
bucket_policy: nil,
|
22
|
+
key_policy: nil
|
23
|
+
)
|
24
|
+
|
25
|
+
ident = tf_safe(name)
|
26
|
+
|
27
|
+
@name = name
|
28
|
+
@key = resource :aws_kms_key, ident, { policy: key_policy }
|
29
|
+
@key_arn = @key["arn"]
|
30
|
+
|
31
|
+
resource :aws_kms_alias, ident, {
|
32
|
+
name: "alias/#{name}",
|
33
|
+
target_key_id: @key["id"],
|
34
|
+
}
|
35
|
+
|
36
|
+
@bucket = resource :aws_s3_bucket, ident, {
|
37
|
+
bucket: name,
|
38
|
+
acl: "private",
|
39
|
+
force_destroy: false,
|
40
|
+
versioning: {
|
41
|
+
enabled: true,
|
42
|
+
},
|
43
|
+
policy: bucket_policy,
|
44
|
+
server_side_encryption_configuration: {
|
45
|
+
rule: {
|
46
|
+
apply_server_side_encryption_by_default: {
|
47
|
+
kms_master_key_id: @key["arn"],
|
48
|
+
sse_algorithm: "aws:kms",
|
49
|
+
}
|
50
|
+
}
|
51
|
+
},
|
52
|
+
tags: {
|
53
|
+
Name: name,
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_statements(prefix: "*")
|
61
|
+
bucket_glob = [@bucket["arn"], prefix].join("/")
|
62
|
+
|
63
|
+
[
|
64
|
+
{
|
65
|
+
Effect: "Allow",
|
66
|
+
Action: [
|
67
|
+
"s3:ListBucket",
|
68
|
+
"s3:GetBucketAcl",
|
69
|
+
],
|
70
|
+
Resource: @bucket["arn"],
|
71
|
+
},
|
72
|
+
{
|
73
|
+
Effect: "Allow",
|
74
|
+
Action: [
|
75
|
+
"s3:GetObject*",
|
76
|
+
],
|
77
|
+
Resource: bucket_glob,
|
78
|
+
},
|
79
|
+
{
|
80
|
+
Effect: "Allow",
|
81
|
+
Action: [
|
82
|
+
"kms:Decrypt",
|
83
|
+
],
|
84
|
+
Resource: @key["arn"],
|
85
|
+
}
|
86
|
+
]
|
87
|
+
end
|
88
|
+
|
89
|
+
def write_statements(prefix: "*")
|
90
|
+
bucket_glob = [@bucket["arn"], prefix].join("/")
|
91
|
+
|
92
|
+
[
|
93
|
+
{
|
94
|
+
Effect: "Allow",
|
95
|
+
Action: [
|
96
|
+
"s3:ListBucket",
|
97
|
+
"s3:GetBucketAcl",
|
98
|
+
],
|
99
|
+
Resource: @bucket["arn"],
|
100
|
+
},
|
101
|
+
{
|
102
|
+
Effect: "Allow",
|
103
|
+
Action: [
|
104
|
+
"s3:GetObject*",
|
105
|
+
"s3:PutObject*",
|
106
|
+
],
|
107
|
+
Resource: bucket_glob,
|
108
|
+
},
|
109
|
+
{
|
110
|
+
Effect: "Allow",
|
111
|
+
Action: [
|
112
|
+
"kms:Encrypt",
|
113
|
+
"kms:Decrypt",
|
114
|
+
"kms:GenerateDataKey"
|
115
|
+
],
|
116
|
+
Resource: @key["arn"],
|
117
|
+
}
|
118
|
+
]
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terrafying'
|
4
|
+
|
5
|
+
module Terrafying
|
6
|
+
|
7
|
+
module Components
|
8
|
+
|
9
|
+
module Security
|
10
|
+
|
11
|
+
class Trail < Terrafying::Context
|
12
|
+
|
13
|
+
def self.create(*args)
|
14
|
+
Trail.new.create(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.bucket_statements(bucket_name)
|
18
|
+
[
|
19
|
+
{
|
20
|
+
Sid: "AWSCloudTrailAclCheck",
|
21
|
+
Effect: "Allow",
|
22
|
+
Principal: {
|
23
|
+
Service: "cloudtrail.amazonaws.com"
|
24
|
+
},
|
25
|
+
Action: "s3:GetBucketAcl",
|
26
|
+
Resource: "arn:aws:s3:::#{bucket_name}"
|
27
|
+
},
|
28
|
+
{
|
29
|
+
Sid: "AWSCloudTrailWrite",
|
30
|
+
Effect: "Allow",
|
31
|
+
Principal: {
|
32
|
+
Service: "cloudtrail.amazonaws.com"
|
33
|
+
},
|
34
|
+
Action: "s3:PutObject",
|
35
|
+
Resource: "arn:aws:s3:::#{bucket_name}/*",
|
36
|
+
Condition: {
|
37
|
+
StringEquals: {
|
38
|
+
"s3:x-amz-acl" => "bucket-owner-full-control"
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.key_statements
|
46
|
+
[
|
47
|
+
{
|
48
|
+
Sid: "Allow CloudTrail to encrypt logs",
|
49
|
+
Effect: "Allow",
|
50
|
+
Principal: {"Service": ["cloudtrail.amazonaws.com"]},
|
51
|
+
Action: "kms:GenerateDataKey*",
|
52
|
+
Resource: "*",
|
53
|
+
Condition: {"StringLike": {"kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:#{aws.account_id}:trail/*"}}
|
54
|
+
},
|
55
|
+
{
|
56
|
+
Sid: "Allow CloudTrail to describe key",
|
57
|
+
Effect: "Allow",
|
58
|
+
Principal: {"Service": ["cloudtrail.amazonaws.com"]},
|
59
|
+
Action: "kms:DescribeKey",
|
60
|
+
Resource: "*"
|
61
|
+
},
|
62
|
+
{
|
63
|
+
Sid: "Allow principals in the account to decrypt log files",
|
64
|
+
Effect: "Allow",
|
65
|
+
Principal: {"AWS": "*"},
|
66
|
+
Action: [
|
67
|
+
"kms:Decrypt",
|
68
|
+
"kms:ReEncryptFrom"
|
69
|
+
],
|
70
|
+
Resource: "*",
|
71
|
+
Condition: {
|
72
|
+
StringEquals: {"kms:CallerAccount": aws.account_id},
|
73
|
+
StringLike: {"kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:#{aws.account_id}:trail/*"}
|
74
|
+
}
|
75
|
+
},
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
def create(
|
80
|
+
name,
|
81
|
+
store:,
|
82
|
+
topic:,
|
83
|
+
include_all_regions: true,
|
84
|
+
include_all_organisation: true
|
85
|
+
)
|
86
|
+
|
87
|
+
@name = name
|
88
|
+
@topic = topic
|
89
|
+
|
90
|
+
@log_group = resource :aws_cloudwatch_log_group, "cloudtrail-#{name}", {
|
91
|
+
name: "cloudtrail-#{name}",
|
92
|
+
}
|
93
|
+
|
94
|
+
log_role = resource :aws_iam_role, "cloudtrail-#{name}-logs", {
|
95
|
+
name: "cloudtrail-#{name}-logs",
|
96
|
+
assume_role_policy: {
|
97
|
+
Version: "2012-10-17",
|
98
|
+
Statement: [
|
99
|
+
{
|
100
|
+
Sid: "",
|
101
|
+
Effect: "Allow",
|
102
|
+
Principal: {
|
103
|
+
Service: "cloudtrail.amazonaws.com"
|
104
|
+
},
|
105
|
+
Action: "sts:AssumeRole",
|
106
|
+
},
|
107
|
+
],
|
108
|
+
}.to_json,
|
109
|
+
}
|
110
|
+
|
111
|
+
log_role_policy = resource :aws_iam_policy, "cloudtrail-#{name}-logs", {
|
112
|
+
name: "cloudtrail-#{name}-logs",
|
113
|
+
policy: {
|
114
|
+
Version: "2012-10-17",
|
115
|
+
Statement: [
|
116
|
+
{
|
117
|
+
Sid: "AWSCloudTrailCreateLogStream2014110",
|
118
|
+
Effect: "Allow",
|
119
|
+
Action: [
|
120
|
+
"logs:CreateLogStream"
|
121
|
+
],
|
122
|
+
Resource: [
|
123
|
+
@log_group["arn"],
|
124
|
+
]
|
125
|
+
},
|
126
|
+
{
|
127
|
+
Sid: "AWSCloudTrailPutLogEvents20141101",
|
128
|
+
Effect: "Allow",
|
129
|
+
Action: [
|
130
|
+
"logs:PutLogEvents"
|
131
|
+
],
|
132
|
+
Resource: [
|
133
|
+
@log_group["arn"],
|
134
|
+
]
|
135
|
+
}
|
136
|
+
]
|
137
|
+
}.to_json
|
138
|
+
}
|
139
|
+
|
140
|
+
resource :aws_iam_role_policy_attachment, "cloudtrail-#{name}-logs", {
|
141
|
+
role: log_role["name"],
|
142
|
+
policy_arn: log_role_policy["arn"],
|
143
|
+
}
|
144
|
+
|
145
|
+
resource :aws_cloudtrail, "#{name}", {
|
146
|
+
name: "#{name}",
|
147
|
+
s3_bucket_name: store.name,
|
148
|
+
s3_key_prefix: "cloudtrail",
|
149
|
+
include_global_service_events: true,
|
150
|
+
is_multi_region_trail: include_all_regions,
|
151
|
+
is_organization_trail: include_all_organisation,
|
152
|
+
enable_log_file_validation: true,
|
153
|
+
kms_key_id: store.key_arn,
|
154
|
+
|
155
|
+
cloud_watch_logs_group_arn: @log_group["arn"],
|
156
|
+
cloud_watch_logs_role_arn: log_role["arn"],
|
157
|
+
|
158
|
+
event_selector: [
|
159
|
+
{
|
160
|
+
read_write_type: "All",
|
161
|
+
include_management_events: true,
|
162
|
+
|
163
|
+
data_resource: {
|
164
|
+
type: "AWS::Lambda::Function",
|
165
|
+
values: ["arn:aws:lambda"],
|
166
|
+
},
|
167
|
+
},
|
168
|
+
{
|
169
|
+
read_write_type: "All",
|
170
|
+
include_management_events: true,
|
171
|
+
|
172
|
+
data_resource: {
|
173
|
+
type: "AWS::S3::Object",
|
174
|
+
values: ["arn:aws:s3:::"],
|
175
|
+
},
|
176
|
+
},
|
177
|
+
],
|
178
|
+
}
|
179
|
+
self
|
180
|
+
end
|
181
|
+
|
182
|
+
def alert!(name:, pattern:, threshold: 1, topic: @topic)
|
183
|
+
|
184
|
+
ident = "cloudwatch-#{@name}-#{name}"
|
185
|
+
|
186
|
+
resource :aws_cloudwatch_log_metric_filter, ident, {
|
187
|
+
name: name,
|
188
|
+
pattern: pattern,
|
189
|
+
log_group_name: @log_group["name"],
|
190
|
+
|
191
|
+
metric_transformation: {
|
192
|
+
name: "EventCount",
|
193
|
+
namespace: "CloudTrail/#{name}",
|
194
|
+
value: "1",
|
195
|
+
},
|
196
|
+
}
|
197
|
+
|
198
|
+
resource :aws_cloudwatch_metric_alarm, ident, {
|
199
|
+
alarm_name: name,
|
200
|
+
comparison_operator: "GreaterThanOrEqualToThreshold",
|
201
|
+
evaluation_periods: "1",
|
202
|
+
metric_name: "EventCount",
|
203
|
+
namespace: "CloudTrail/#{name}",
|
204
|
+
period: "300",
|
205
|
+
statistic: "Sum",
|
206
|
+
threshold: "#{threshold}",
|
207
|
+
alarm_actions: [ topic ],
|
208
|
+
}
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
def cis_benchmark!
|
213
|
+
|
214
|
+
# 3.1 Ensure a log metric filter and alarm exist for unauthorized API calls
|
215
|
+
alert!(
|
216
|
+
name: "UnauthorizedAPICalls",
|
217
|
+
pattern: "{ ($.errorCode = \"*UnauthorizedOperation\") || ($.errorCode = \"AccessDenied*\") }",
|
218
|
+
)
|
219
|
+
|
220
|
+
# 3.2 Ensure a log metric filter and alarm exist for Management Console sign-in without MFA
|
221
|
+
alert!(
|
222
|
+
name: "NoMFAConsoleSignin",
|
223
|
+
pattern: "{ ($.eventName = \"ConsoleLogin\") && ($.additionalEventData.MFAUsed != \"Yes\") }",
|
224
|
+
)
|
225
|
+
|
226
|
+
# 3.3 Ensure a log metric filter and alarm exist for usage of "root" account
|
227
|
+
alert!(
|
228
|
+
name: "RootUsage",
|
229
|
+
pattern: "{ $.userIdentity.type = \"Root\" && $.userIdentity.invokedBy NOT EXISTS && $.eventType != \"AwsServiceEvent\" }",
|
230
|
+
)
|
231
|
+
|
232
|
+
# 3.4 Ensure a log metric filter and alarm exist for IAM policy changes
|
233
|
+
alert!(
|
234
|
+
name: "IAMChanges",
|
235
|
+
pattern: "{($.eventName=DeleteGroupPolicy)||($.eventName=DeleteRolePolicy)||($.eventName=DeleteUserPolicy)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=PutUserPolicy)||($.eventName=CreatePolicy)||($.eventName=DeletePolicy)||($.eventName=CreatePolicyVersion)||($.eventName=DeletePolicyVersion)||($.eventName=AttachRolePolicy)||($.eventName=DetachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=DetachUserPolicy)||($.eventName=AttachGroupPolicy)||($.eventName=DetachGroupPolicy)}",
|
236
|
+
)
|
237
|
+
|
238
|
+
# 3.5 Ensure a log metric filter and alarm exist for CloudTrail configuration changes
|
239
|
+
alert!(
|
240
|
+
name: "CloudTrailCfgChanges",
|
241
|
+
pattern: "{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }",
|
242
|
+
)
|
243
|
+
|
244
|
+
# 3.6 Ensure a log metric filter and alarm exist for AWS Management Console authentication failures
|
245
|
+
alert!(
|
246
|
+
name: "ConsoleSigninFailures",
|
247
|
+
pattern: "{ ($.eventName = ConsoleLogin) && ($.errorMessage = \"Failed authentication\") }",
|
248
|
+
)
|
249
|
+
|
250
|
+
# 3.7 Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs
|
251
|
+
alert!(
|
252
|
+
name: "DisableOrDeleteCMK",
|
253
|
+
pattern: "{ ($.eventSource = kms.amazonaws.com) && (($.eventName = DisableKey) || ($.eventName = ScheduleKeyDeletion)) }",
|
254
|
+
)
|
255
|
+
|
256
|
+
# 3.8 Ensure a log metric filter and alarm exist for S3 bucket policy changes
|
257
|
+
alert!(
|
258
|
+
name: "S3BucketPolicyChanges",
|
259
|
+
pattern: "{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) || ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }",
|
260
|
+
)
|
261
|
+
|
262
|
+
# 3.9 Ensure a log metric filter and alarm exist for AWS Config configuration changes
|
263
|
+
alert!(
|
264
|
+
name: "AWSConfigChanges",
|
265
|
+
pattern: "{ ($.eventSource = config.amazonaws.com) && (($.eventName=StopConfigurationRecorder)||($.eventName=DeleteDeliveryChannel)||($.eventName=PutDeliveryChannel)||($.eventName=PutConfigurationRecorder)) }",
|
266
|
+
)
|
267
|
+
|
268
|
+
# 3.10 Ensure a log metric filter and alarm exist for security group changes
|
269
|
+
alert!(
|
270
|
+
name: "SecurityGroupChanges",
|
271
|
+
pattern: "{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup)}",
|
272
|
+
)
|
273
|
+
|
274
|
+
# 3.11 Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL)
|
275
|
+
alert!(
|
276
|
+
name: "NACLChanges",
|
277
|
+
pattern: "{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }",
|
278
|
+
)
|
279
|
+
|
280
|
+
# 3.12 Ensure a log metric filter and alarm exist for changes to network gateways
|
281
|
+
alert!(
|
282
|
+
name: "NetworkGWChanges",
|
283
|
+
pattern: "{ ($.eventName = CreateCustomerGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = DetachInternetGateway) }",
|
284
|
+
)
|
285
|
+
|
286
|
+
# 3.13 Ensure a log metric filter and alarm exist for route table changes
|
287
|
+
alert!(
|
288
|
+
name: "RouteTableChanges",
|
289
|
+
pattern: "{ ($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) || ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation) || ($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DisassociateRouteTable) }",
|
290
|
+
)
|
291
|
+
|
292
|
+
# 3.14 Ensure a log metric filter and alarm exist for VPC changes
|
293
|
+
alert!(
|
294
|
+
name: "VPCChanges",
|
295
|
+
pattern: "{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }",
|
296
|
+
)
|
297
|
+
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: terrafying-components
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- uSwitch Limited
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -115,6 +115,12 @@ files:
|
|
115
115
|
- lib/terrafying/components/loadbalancer.rb
|
116
116
|
- lib/terrafying/components/ports.rb
|
117
117
|
- lib/terrafying/components/prometheus.rb
|
118
|
+
- lib/terrafying/components/security.rb
|
119
|
+
- lib/terrafying/components/security/config.rb
|
120
|
+
- lib/terrafying/components/security/config_aggregator.rb
|
121
|
+
- lib/terrafying/components/security/pagerduty_topic.rb
|
122
|
+
- lib/terrafying/components/security/store.rb
|
123
|
+
- lib/terrafying/components/security/trail.rb
|
118
124
|
- lib/terrafying/components/selfsignedca.rb
|
119
125
|
- lib/terrafying/components/service.rb
|
120
126
|
- lib/terrafying/components/staticset.rb
|