terrafying-components 1.4.3 → 1.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/hash/merge_with_arrays.rb +7 -0
- data/lib/terrafying/components.rb +10 -0
- data/lib/terrafying/components/auditd.rb +158 -0
- data/lib/terrafying/components/ca.rb +55 -0
- data/lib/terrafying/components/dynamicset.rb +229 -0
- data/lib/terrafying/components/endpoint.rb +96 -0
- data/lib/terrafying/components/endpointservice.rb +63 -0
- data/lib/terrafying/components/ignition.rb +117 -0
- data/lib/terrafying/components/instance.rb +112 -0
- data/lib/terrafying/components/instanceprofile.rb +81 -0
- data/lib/terrafying/components/letsencrypt.rb +146 -0
- data/lib/terrafying/components/loadbalancer.rb +159 -0
- data/lib/terrafying/components/ports.rb +35 -0
- data/lib/terrafying/components/selfsignedca.rb +171 -0
- data/lib/terrafying/components/service.rb +148 -0
- data/lib/terrafying/components/staticset.rb +153 -0
- data/lib/terrafying/components/subnet.rb +105 -0
- data/lib/terrafying/components/support/deregister-vpn +48 -0
- data/lib/terrafying/components/support/register-vpn +46 -0
- data/lib/terrafying/components/templates/ignition.yaml +115 -0
- data/lib/terrafying/components/usable.rb +129 -0
- data/lib/terrafying/components/version.rb +5 -0
- data/lib/terrafying/components/vpc.rb +417 -0
- data/lib/terrafying/components/vpn.rb +358 -0
- data/lib/terrafying/components/zone.rb +144 -0
- metadata +28 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3577f779cfb8aaaefa37ed0ad43635a37ae154cb34dbc0a6442177dd2abc1786
|
4
|
+
data.tar.gz: 1ed1adf2f61d82697438d1338b99276405d04c6688f84047a297a6ae4bdf8a5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b23eba0da42b8a9bfa34251c545699f8468bb14d16392ae271f032fec6d01399995a8aeef66c52ae58a3f84a2e080e67d2ad46dd38c22445631539681d21bcc
|
7
|
+
data.tar.gz: 3715ab338a9947a5c0697a41011a74b3f91335da4b68148498fd9a0a118457c8d1f15290b4cffbe6285191ebb0f2b4a73f3b88dbc85377eba4173cf10a1740d2
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
require 'terrafying/components/endpoint'
|
3
|
+
require 'terrafying/components/endpointservice'
|
4
|
+
require 'terrafying/components/selfsignedca'
|
5
|
+
require 'terrafying/components/letsencrypt'
|
6
|
+
require 'terrafying/components/service'
|
7
|
+
require 'terrafying/components/subnet'
|
8
|
+
require 'terrafying/components/vpc'
|
9
|
+
require 'terrafying/components/vpn'
|
10
|
+
require 'terrafying/components/zone'
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Terrafying
|
4
|
+
module Components
|
5
|
+
class Auditd
|
6
|
+
def self.fluentd_conf(role, tags = [])
|
7
|
+
new.fluentd_conf(role, tags)
|
8
|
+
end
|
9
|
+
|
10
|
+
def fluentd_conf(role, tags)
|
11
|
+
tags = default_tags.merge(
|
12
|
+
custom_tags(tags)
|
13
|
+
)
|
14
|
+
|
15
|
+
{
|
16
|
+
files: [
|
17
|
+
systemd_input,
|
18
|
+
ec2_filter(tags),
|
19
|
+
s3_output(role)
|
20
|
+
],
|
21
|
+
iam_policy_statements: [
|
22
|
+
allow_describe_instances,
|
23
|
+
allow_assume(role)
|
24
|
+
]
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def custom_tags(tags)
|
29
|
+
tags.map { |t| [t, wrap_tag(t)] }.to_h
|
30
|
+
end
|
31
|
+
|
32
|
+
def wrap_tag(t)
|
33
|
+
t = "tagset_#{t}" unless t.start_with? 'tagset_'
|
34
|
+
t.downcase
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_tags
|
38
|
+
{
|
39
|
+
name: 'tagset_name',
|
40
|
+
instance_id: 'instance_id',
|
41
|
+
instance_type: 'instance_type',
|
42
|
+
private_ip: 'private_ip',
|
43
|
+
az: 'availability_zone',
|
44
|
+
vpc_id: 'vpc_id',
|
45
|
+
ami_id: 'image_id',
|
46
|
+
account_id: 'account_id'
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def allow_describe_instances
|
51
|
+
{
|
52
|
+
Effect: 'Allow',
|
53
|
+
Action: %w[ec2:DescribeInstances ec2:DescribeTags ec2:DescribeRouteTables],
|
54
|
+
Resource: ['*']
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def allow_assume(role)
|
59
|
+
{
|
60
|
+
Effect: 'Allow',
|
61
|
+
Action: ['sts:AssumeRole'],
|
62
|
+
Resource: [role]
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def file_of(name, content)
|
67
|
+
{
|
68
|
+
path: "/etc/fluentd/conf.d/#{name}.conf",
|
69
|
+
mode: 0o644,
|
70
|
+
contents: content
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def systemd_input
|
75
|
+
file_of(
|
76
|
+
'10_auditd_input_systemd',
|
77
|
+
<<~'SYSTEMD_INPUT'
|
78
|
+
<source>
|
79
|
+
@type systemd
|
80
|
+
tag auditd
|
81
|
+
filters [{ "_TRANSPORT": "audit" }, { "_COMM": "sshd" }]
|
82
|
+
path /fluentd/log/journal
|
83
|
+
read_from_head false
|
84
|
+
<storage>
|
85
|
+
@type local
|
86
|
+
persistent false
|
87
|
+
path /fluentd/var/audit.pos
|
88
|
+
</storage>
|
89
|
+
<entry>
|
90
|
+
field_map {
|
91
|
+
"MESSAGE": "log",
|
92
|
+
"_PID": ["process", "pid"],
|
93
|
+
"_CMDLINE": "process",
|
94
|
+
"_COMM": "cmd",
|
95
|
+
"_AUDIT_SESSION": "audit_session",
|
96
|
+
"_AUDIT_LOGINUID": "audit_loginuid"
|
97
|
+
}
|
98
|
+
fields_strip_underscores true
|
99
|
+
fields_lowercase true
|
100
|
+
</entry>
|
101
|
+
</source>
|
102
|
+
SYSTEMD_INPUT
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
def ec2_filter(tags)
|
107
|
+
file_of(
|
108
|
+
'20_auditd_filter_ec2',
|
109
|
+
<<~EC2_FILTER
|
110
|
+
<filter auditd>
|
111
|
+
@type ec2_metadata
|
112
|
+
metadata_refresh_seconds 300
|
113
|
+
<record>
|
114
|
+
#{map_tags(tags)}
|
115
|
+
</record>
|
116
|
+
</filter>
|
117
|
+
EC2_FILTER
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
def map_tags(tags)
|
122
|
+
tags.map { |k, v| "#{k} ${#{v}}" }
|
123
|
+
.reduce { |out, e| +out << "\n #{e}" }
|
124
|
+
end
|
125
|
+
|
126
|
+
def s3_output(audit_role)
|
127
|
+
file_of(
|
128
|
+
'30_auditd_output_s3',
|
129
|
+
<<~S3_OUTPUT
|
130
|
+
<match auditd>
|
131
|
+
@type s3
|
132
|
+
<assume_role_credentials>
|
133
|
+
role_arn #{audit_role}
|
134
|
+
role_session_name "auditd-logging-\#{Socket.gethostname}"
|
135
|
+
</assume_role_credentials>
|
136
|
+
auto_create_bucket false
|
137
|
+
s3_bucket uswitch-auditd-logs
|
138
|
+
s3_region eu-west-1
|
139
|
+
acl bucket-owner-full-control
|
140
|
+
path auditd/%Y/%m/%d/
|
141
|
+
s3_object_key_format "\%{path}\%{time_slice}_\#{Socket.gethostname}.\%{file_extension}"
|
142
|
+
<buffer time>
|
143
|
+
@type file
|
144
|
+
path /fluent/var/s3
|
145
|
+
timekey 300 # 5 minute partitions
|
146
|
+
timekey_wait 0s
|
147
|
+
timekey_use_utc true
|
148
|
+
</buffer>
|
149
|
+
<format>
|
150
|
+
@type json
|
151
|
+
</format>
|
152
|
+
</match>
|
153
|
+
S3_OUTPUT
|
154
|
+
)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
module Terrafying
|
3
|
+
|
4
|
+
module Components
|
5
|
+
|
6
|
+
module CA
|
7
|
+
|
8
|
+
def create_keypair(name, options={})
|
9
|
+
create_keypair_in(self, name, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def reference_keypair(ctx, name)
|
13
|
+
key_ident = "#{@name}-#{tf_safe(name)}"
|
14
|
+
|
15
|
+
ref = {
|
16
|
+
name: name,
|
17
|
+
ca: self,
|
18
|
+
source: {
|
19
|
+
cert: File.join("s3://", @bucket, @prefix, @name, name, "cert"),
|
20
|
+
key: File.join("s3://", @bucket, @prefix, @name, name, "key"),
|
21
|
+
},
|
22
|
+
resources: [
|
23
|
+
"aws_s3_bucket_object.#{key_ident}-key",
|
24
|
+
"aws_s3_bucket_object.#{key_ident}-cert"
|
25
|
+
],
|
26
|
+
iam_statement: {
|
27
|
+
Effect: "Allow",
|
28
|
+
Action: [
|
29
|
+
"s3:GetObjectAcl",
|
30
|
+
"s3:GetObject",
|
31
|
+
],
|
32
|
+
Resource: [
|
33
|
+
"arn:aws:s3:::#{File.join(@bucket, @prefix, @name, "ca.cert")}",
|
34
|
+
"arn:aws:s3:::#{File.join(@bucket, @prefix, @name, name, "cert")}",
|
35
|
+
"arn:aws:s3:::#{File.join(@bucket, @prefix, @name, name, "key")}",
|
36
|
+
]
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
if self == ctx
|
41
|
+
ref[:resources] << "aws_s3_bucket_object.#{@name}-cert"
|
42
|
+
end
|
43
|
+
|
44
|
+
ref
|
45
|
+
end
|
46
|
+
|
47
|
+
def <=>(other)
|
48
|
+
@name <=> other.name
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
|
2
|
+
require 'terrafying/components/usable'
|
3
|
+
|
4
|
+
require_relative './ports'
|
5
|
+
|
6
|
+
module Terrafying
|
7
|
+
|
8
|
+
module Components
|
9
|
+
|
10
|
+
class DynamicSet < Terrafying::Context
|
11
|
+
|
12
|
+
attr_reader :name, :asg
|
13
|
+
|
14
|
+
include Usable
|
15
|
+
|
16
|
+
def self.create_in(vpc, name, options={})
|
17
|
+
DynamicSet.new.create_in vpc, name, options
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.find_in(vpc, name)
|
21
|
+
DynamicSet.new.find_in vpc, name
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize()
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_in(vpc, name)
|
29
|
+
@name = "#{vpc.name}-#{name}"
|
30
|
+
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_in(vpc, name, options={})
|
35
|
+
options = {
|
36
|
+
public: false,
|
37
|
+
ami: aws.ami("base-image-59a5b709", owners=["136393635417"]),
|
38
|
+
instance_type: "t2.micro",
|
39
|
+
instances: { min: 1, max: 1, desired: 1, tags: {} },
|
40
|
+
ports: [],
|
41
|
+
instance_profile: nil,
|
42
|
+
security_groups: [],
|
43
|
+
tags: {},
|
44
|
+
ssh_group: vpc.ssh_group,
|
45
|
+
subnets: vpc.subnets.fetch(:private, []),
|
46
|
+
depends_on: [],
|
47
|
+
rolling_update: :simple,
|
48
|
+
}.merge(options)
|
49
|
+
|
50
|
+
ident = "#{tf_safe(vpc.name)}-#{name}"
|
51
|
+
|
52
|
+
@name = ident
|
53
|
+
@ports = enrich_ports(options[:ports])
|
54
|
+
|
55
|
+
@security_group = resource :aws_security_group, ident, {
|
56
|
+
name: "dynamicset-#{ident}",
|
57
|
+
description: "Describe the ingress and egress of the service #{ident}",
|
58
|
+
tags: options[:tags],
|
59
|
+
vpc_id: vpc.id,
|
60
|
+
egress: [
|
61
|
+
{
|
62
|
+
from_port: 0,
|
63
|
+
to_port: 0,
|
64
|
+
protocol: -1,
|
65
|
+
cidr_blocks: ["0.0.0.0/0"],
|
66
|
+
}
|
67
|
+
],
|
68
|
+
}
|
69
|
+
|
70
|
+
path_mtu_setup!
|
71
|
+
|
72
|
+
launch_config = resource :aws_launch_configuration, ident, {
|
73
|
+
name_prefix: "#{ident}-",
|
74
|
+
image_id: options[:ami],
|
75
|
+
instance_type: options[:instance_type],
|
76
|
+
user_data: options[:user_data],
|
77
|
+
iam_instance_profile: options[:instance_profile] && options[:instance_profile].id,
|
78
|
+
associate_public_ip_address: options[:public],
|
79
|
+
root_block_device: {
|
80
|
+
volume_type: 'gp2',
|
81
|
+
volume_size: 32,
|
82
|
+
},
|
83
|
+
security_groups: [
|
84
|
+
vpc.internal_ssh_security_group,
|
85
|
+
@security_group,
|
86
|
+
].push(*options[:security_groups]),
|
87
|
+
lifecycle: {
|
88
|
+
create_before_destroy: true,
|
89
|
+
},
|
90
|
+
depends_on: options[:instance_profile] ? options[:instance_profile].resource_names : [],
|
91
|
+
}
|
92
|
+
|
93
|
+
if options[:instances][:track]
|
94
|
+
instances = instances_by_tags(Name: ident)
|
95
|
+
if instances
|
96
|
+
options[:instances] = options[:instances].merge(instances)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
if options.has_key?(:health_check)
|
101
|
+
raise 'Health check needs a type and grace_period' if ! options[:health_check].has_key?(:type) and ! options[:health_check].has_key?(:grace_period)
|
102
|
+
else
|
103
|
+
options = {
|
104
|
+
health_check: {
|
105
|
+
type: "EC2",
|
106
|
+
grace_period: 0
|
107
|
+
},
|
108
|
+
}.merge(options)
|
109
|
+
end
|
110
|
+
tags = { Name: ident, service_name: name,}.merge(options[:tags]).merge(options[:instances][:tags]).map { |k,v| { Key: k, Value: v, PropagateAtLaunch: true }}
|
111
|
+
|
112
|
+
asg = resource :aws_cloudformation_stack, ident, {
|
113
|
+
name: ident,
|
114
|
+
disable_rollback: true,
|
115
|
+
template_body: generate_template(
|
116
|
+
options[:health_check], options[:instances], launch_config,
|
117
|
+
options[:subnets].map(&:id), tags, options[:rolling_update]
|
118
|
+
),
|
119
|
+
}
|
120
|
+
|
121
|
+
@asg = output_of(:aws_cloudformation_stack, ident, 'outputs["AsgName"]')
|
122
|
+
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def attach_load_balancer(load_balancer)
|
127
|
+
load_balancer.target_groups.each.with_index { |target_group, i|
|
128
|
+
resource :aws_autoscaling_attachment, "#{load_balancer.name}-#{@name}-#{i}", {
|
129
|
+
autoscaling_group_name: @asg,
|
130
|
+
alb_target_group_arn: target_group
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
self.used_by(load_balancer) if load_balancer.type == "application"
|
135
|
+
end
|
136
|
+
|
137
|
+
def generate_template(health_check, instances, launch_config, subnets,tags, rolling_update)
|
138
|
+
template = {
|
139
|
+
Resources: {
|
140
|
+
AutoScalingGroup: {
|
141
|
+
Type: "AWS::AutoScaling::AutoScalingGroup",
|
142
|
+
Properties: {
|
143
|
+
Cooldown: "300",
|
144
|
+
HealthCheckType: "#{health_check[:type]}",
|
145
|
+
HealthCheckGracePeriod: health_check[:grace_period],
|
146
|
+
LaunchConfigurationName: "#{launch_config}",
|
147
|
+
MaxSize: "#{instances[:max]}",
|
148
|
+
MetricsCollection: [
|
149
|
+
{
|
150
|
+
Granularity: "1Minute",
|
151
|
+
Metrics: [
|
152
|
+
"GroupMinSize",
|
153
|
+
"GroupMaxSize",
|
154
|
+
"GroupDesiredCapacity",
|
155
|
+
"GroupInServiceInstances",
|
156
|
+
"GroupPendingInstances",
|
157
|
+
"GroupStandbyInstances",
|
158
|
+
"GroupTerminatingInstances",
|
159
|
+
"GroupTotalInstances"
|
160
|
+
]
|
161
|
+
},
|
162
|
+
],
|
163
|
+
MinSize: "#{instances[:min]}",
|
164
|
+
DesiredCapacity: "#{instances[:desired]}",
|
165
|
+
Tags: tags,
|
166
|
+
TerminationPolicies: [
|
167
|
+
"Default"
|
168
|
+
],
|
169
|
+
VPCZoneIdentifier: subnets
|
170
|
+
}
|
171
|
+
}
|
172
|
+
},
|
173
|
+
Outputs: {
|
174
|
+
AsgName: {
|
175
|
+
Description: "The name of the auto scaling group",
|
176
|
+
Value: {
|
177
|
+
Ref: "AutoScalingGroup",
|
178
|
+
},
|
179
|
+
},
|
180
|
+
},
|
181
|
+
}
|
182
|
+
|
183
|
+
if rolling_update == :signal
|
184
|
+
template[:Resources][:AutoScalingGroup][:UpdatePolicy] = {
|
185
|
+
AutoScalingRollingUpdate: {
|
186
|
+
MinInstancesInService: "#{instances[:desired]}",
|
187
|
+
MaxBatchSize: "#{instances[:desired]}",
|
188
|
+
PauseTime: "PT10M",
|
189
|
+
WaitOnResourceSignals: true,
|
190
|
+
}
|
191
|
+
}
|
192
|
+
elsif rolling_update
|
193
|
+
template[:Resources][:AutoScalingGroup][:UpdatePolicy] = {
|
194
|
+
AutoScalingRollingUpdate: {
|
195
|
+
MinInstancesInService: "#{instances[:min]}",
|
196
|
+
MaxBatchSize: "1",
|
197
|
+
PauseTime: "PT0S"
|
198
|
+
}
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
JSON.pretty_generate(template)
|
203
|
+
end
|
204
|
+
|
205
|
+
def instances_by_tags(tags = {})
|
206
|
+
begin
|
207
|
+
asgs = aws.asgs_by_tags(tags)
|
208
|
+
|
209
|
+
if asgs.count != 1
|
210
|
+
raise "Didn't find only one ASG :("
|
211
|
+
end
|
212
|
+
|
213
|
+
instances = {
|
214
|
+
min: asgs[0].min_size,
|
215
|
+
max: asgs[0].max_size,
|
216
|
+
desired: asgs[0].desired_capacity,
|
217
|
+
}
|
218
|
+
rescue RuntimeError => err
|
219
|
+
$stderr.puts("instances_by_tags: #{err}")
|
220
|
+
instances = nil
|
221
|
+
end
|
222
|
+
|
223
|
+
instances
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|