terrafying-components 1.11.17 → 1.12.0
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/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
|