terrafying-components 1.4.3 → 1.4.4
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/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
@@ -0,0 +1,358 @@
|
|
1
|
+
|
2
|
+
require 'digest'
|
3
|
+
require 'netaddr'
|
4
|
+
|
5
|
+
require 'terrafying/components/ignition'
|
6
|
+
require 'terrafying/generator'
|
7
|
+
|
8
|
+
|
9
|
+
IN4MASK = 0xffffffff
|
10
|
+
|
11
|
+
def cidr_to_split_address(raw_cidr)
|
12
|
+
cidr = NetAddr::CIDR.create(raw_cidr)
|
13
|
+
|
14
|
+
masklen = 32 - cidr.bits
|
15
|
+
maskaddr = ((IN4MASK >> masklen) << masklen)
|
16
|
+
|
17
|
+
maskip = (0..3).map { |i|
|
18
|
+
(maskaddr >> (24 - 8 * i)) & 0xff
|
19
|
+
}.join('.')
|
20
|
+
|
21
|
+
return "#{cidr.first} #{maskip}"
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
module Terrafying
|
26
|
+
|
27
|
+
module Components
|
28
|
+
|
29
|
+
class VPN < Terrafying::Context
|
30
|
+
|
31
|
+
attr_reader :name, :cidr
|
32
|
+
|
33
|
+
def self.create_in(vpc, name, provider, options={})
|
34
|
+
VPN.new.create_in vpc, name, provider, options
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize()
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_in(vpc, name, oauth2_provider, options={})
|
42
|
+
options = {
|
43
|
+
group: "uSwitch Developers",
|
44
|
+
cidr: "10.8.0.0/24",
|
45
|
+
public: true,
|
46
|
+
subnets: vpc.subnets.fetch(:public, []),
|
47
|
+
static: false,
|
48
|
+
route_all_traffic: false,
|
49
|
+
units: [],
|
50
|
+
tags: {},
|
51
|
+
service: {},
|
52
|
+
}.merge(options)
|
53
|
+
|
54
|
+
@name = name
|
55
|
+
@vpc = vpc
|
56
|
+
@cidr = options[:cidr]
|
57
|
+
@fqdn = vpc.zone.qualify(name)
|
58
|
+
|
59
|
+
if ! oauth2_provider.is_a?(Hash)
|
60
|
+
raise "You need to give a provider hash containing a type, client_id and client_secret"
|
61
|
+
end
|
62
|
+
|
63
|
+
has_provider = oauth2_provider[:type] != "none"
|
64
|
+
|
65
|
+
if has_provider and ! [:type, :client_id, :client_secret].all? {|k| oauth2_provider.has_key?(k) }
|
66
|
+
raise "You need to set type, client_id and client_secret"
|
67
|
+
end
|
68
|
+
|
69
|
+
units = [
|
70
|
+
openvpn_service,
|
71
|
+
openvpn_authz_service(options[:route_all_traffic]),
|
72
|
+
caddy_service(options[:ca])
|
73
|
+
]
|
74
|
+
files = [
|
75
|
+
openvpn_conf,
|
76
|
+
openvpn_env,
|
77
|
+
openvpn_ip_delay,
|
78
|
+
caddy_conf(options[:ca], has_provider)
|
79
|
+
]
|
80
|
+
keypairs = []
|
81
|
+
|
82
|
+
if has_provider
|
83
|
+
vpn_hash = Digest::SHA2.digest(vpc.name + name + oauth2_provider[:client_secret] + oauth2_provider[:client_id])
|
84
|
+
cookie_secret = Base64.strict_encode64(vpn_hash.byteslice(0,16))
|
85
|
+
|
86
|
+
units.push(oauth2_proxy_service(oauth2_provider, cookie_secret))
|
87
|
+
end
|
88
|
+
|
89
|
+
if options.has_key?(:ca)
|
90
|
+
keypairs.push(options[:ca].create_keypair_in(self, @fqdn))
|
91
|
+
end
|
92
|
+
|
93
|
+
if options[:static]
|
94
|
+
subnet = options[:subnets].first
|
95
|
+
instances = [{ subnet: subnet, ip_address: subnet.ip_addresses.first }]
|
96
|
+
else
|
97
|
+
instances = [{}]
|
98
|
+
end
|
99
|
+
|
100
|
+
@service = add! Service.create_in(
|
101
|
+
vpc, name,
|
102
|
+
{
|
103
|
+
public: options[:public],
|
104
|
+
ports: [22, 443, { number: 1194, type: "udp" }],
|
105
|
+
tags: options[:tags],
|
106
|
+
units: units + options[:units],
|
107
|
+
files: files,
|
108
|
+
keypairs: keypairs,
|
109
|
+
subnets: options[:subnets],
|
110
|
+
instances: instances,
|
111
|
+
iam_policy_statements: [
|
112
|
+
{
|
113
|
+
Effect: "Allow",
|
114
|
+
Action: [
|
115
|
+
"ec2:DescribeRouteTables",
|
116
|
+
],
|
117
|
+
Resource: [
|
118
|
+
"*"
|
119
|
+
]
|
120
|
+
}
|
121
|
+
],
|
122
|
+
}.merge(options[:service])
|
123
|
+
)
|
124
|
+
|
125
|
+
if oauth2_provider[:type] == "azure" and oauth2_provider[:register]
|
126
|
+
|
127
|
+
provider :null, {}
|
128
|
+
|
129
|
+
resource :null_resource, "ad-app-configure", {
|
130
|
+
triggers: {
|
131
|
+
service_resources: @service.resources.join(","),
|
132
|
+
},
|
133
|
+
provisioner: [
|
134
|
+
{
|
135
|
+
"local-exec" => {
|
136
|
+
when: "create",
|
137
|
+
command: "#{File.expand_path(File.dirname(__FILE__))}/support/register-vpn '#{oauth2_provider[:client_id]}' '#{oauth2_provider[:tenant_id]}' '#{@fqdn}'"
|
138
|
+
},
|
139
|
+
},
|
140
|
+
{
|
141
|
+
"local-exec" => {
|
142
|
+
when: "destroy",
|
143
|
+
command: "#{File.expand_path(File.dirname(__FILE__))}/support/deregister-vpn '#{oauth2_provider[:client_id]}' '#{oauth2_provider[:tenant_id]}' '#{@fqdn}'"
|
144
|
+
}
|
145
|
+
},
|
146
|
+
],
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
def openvpn_service
|
154
|
+
Ignition.container_unit(
|
155
|
+
"openvpn", "kylemanna/openvpn",
|
156
|
+
{
|
157
|
+
host_networking: true,
|
158
|
+
privileged: true,
|
159
|
+
volumes: [
|
160
|
+
"/etc/ssl/openvpn:/etc/ssl/openvpn:ro",
|
161
|
+
"/etc/openvpn:/etc/openvpn",
|
162
|
+
],
|
163
|
+
required_units: [ "docker.service", "network-online.target", "openvpn-authz.service" ],
|
164
|
+
}
|
165
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
def openvpn_authz_service(route_all_traffic)
|
169
|
+
optional_arguments = []
|
170
|
+
|
171
|
+
if route_all_traffic
|
172
|
+
optional_arguments << "--route-all"
|
173
|
+
end
|
174
|
+
|
175
|
+
Ignition.container_unit(
|
176
|
+
"openvpn-authz", "quay.io/uswitch/openvpn-authz:stable",
|
177
|
+
{
|
178
|
+
host_networking: true,
|
179
|
+
volumes: [
|
180
|
+
"/etc/ssl/openvpn:/etc/ssl/openvpn",
|
181
|
+
"/var/openvpn-authz:/var/openvpn-authz",
|
182
|
+
],
|
183
|
+
environment_variables: [
|
184
|
+
"AWS_REGION=#{aws.region}",
|
185
|
+
],
|
186
|
+
arguments: optional_arguments + [
|
187
|
+
"--fqdn #{@fqdn}",
|
188
|
+
"--cache /var/openvpn-authz",
|
189
|
+
"/etc/ssl/openvpn",
|
190
|
+
],
|
191
|
+
}
|
192
|
+
)
|
193
|
+
end
|
194
|
+
|
195
|
+
def oauth2_proxy_service(oauth2_provider, cookie_secret)
|
196
|
+
optional_arguments = []
|
197
|
+
|
198
|
+
if oauth2_provider.has_key?(:permit_groups)
|
199
|
+
optional_arguments << "-permit-groups '#{oauth2_provider[:permit_groups].join(",")}'"
|
200
|
+
end
|
201
|
+
|
202
|
+
Ignition.container_unit(
|
203
|
+
"oauth2_proxy", "quay.io/uswitch/oauth2_proxy:stable",
|
204
|
+
{
|
205
|
+
host_networking: true,
|
206
|
+
arguments: [
|
207
|
+
"-client-id='#{oauth2_provider[:client_id]}'",
|
208
|
+
"-client-secret='#{oauth2_provider[:client_secret]}'",
|
209
|
+
"-email-domain='*'",
|
210
|
+
"-cookie-secret='#{cookie_secret}'",
|
211
|
+
"-provider=#{oauth2_provider[:type]}",
|
212
|
+
"-http-address='0.0.0.0:4180'",
|
213
|
+
"-redirect-url='https://#{@fqdn}/oauth2/callback'",
|
214
|
+
"-upstream='http://localhost:8080'",
|
215
|
+
"-approval-prompt=''",
|
216
|
+
"-cookie-secure",
|
217
|
+
"-pass-access-token=true",
|
218
|
+
"-pass-groups",
|
219
|
+
] + optional_arguments
|
220
|
+
}
|
221
|
+
)
|
222
|
+
end
|
223
|
+
|
224
|
+
def caddy_service(ca)
|
225
|
+
optional_volumes = []
|
226
|
+
|
227
|
+
if ca
|
228
|
+
optional_volumes << "/etc/ssl/#{ca.name}:/etc/ssl/#{ca.name}:ro"
|
229
|
+
end
|
230
|
+
|
231
|
+
Ignition.container_unit(
|
232
|
+
"caddy", "abiosoft/caddy:0.10.10",
|
233
|
+
{
|
234
|
+
host_networking: true,
|
235
|
+
volumes: [
|
236
|
+
"/etc/ssl/certs:/etc/ssl/cert:ro",
|
237
|
+
"/etc/caddy/Caddyfile:/etc/Caddyfile",
|
238
|
+
"/etc/caddy/certs:/etc/caddy/certs",
|
239
|
+
] + optional_volumes,
|
240
|
+
environment_variables: [
|
241
|
+
"CADDYPATH=/etc/caddy/certs",
|
242
|
+
],
|
243
|
+
}
|
244
|
+
)
|
245
|
+
end
|
246
|
+
|
247
|
+
def caddy_conf(ca, has_provider)
|
248
|
+
port = has_provider ? "4180" : "8080"
|
249
|
+
tls = ca ? "/etc/ssl/#{ca.name}/#{@fqdn}/cert /etc/ssl/#{ca.name}/#{@fqdn}/key" : "cloud@uswitch.com"
|
250
|
+
{
|
251
|
+
path: "/etc/caddy/Caddyfile",
|
252
|
+
mode: "0644",
|
253
|
+
contents: <<EOF
|
254
|
+
#{@fqdn}:443
|
255
|
+
tls #{tls}
|
256
|
+
proxy / localhost:#{port}
|
257
|
+
EOF
|
258
|
+
}
|
259
|
+
end
|
260
|
+
|
261
|
+
def openvpn_conf
|
262
|
+
{
|
263
|
+
path: "/etc/openvpn/openvpn.conf",
|
264
|
+
mode: "0644",
|
265
|
+
contents: <<EOF
|
266
|
+
server #{cidr_to_split_address(@cidr)}
|
267
|
+
verb 3
|
268
|
+
|
269
|
+
iproute /etc/openvpn/ovpn_ip.sh
|
270
|
+
|
271
|
+
key /etc/ssl/openvpn/server/key
|
272
|
+
ca /etc/ssl/openvpn/ca/cert
|
273
|
+
cert /etc/ssl/openvpn/server/cert
|
274
|
+
dh /etc/ssl/openvpn/dh.pem
|
275
|
+
tls-auth /etc/ssl/openvpn/ta.key
|
276
|
+
|
277
|
+
cipher AES-256-CBC
|
278
|
+
auth SHA512
|
279
|
+
tls-version-min 1.2
|
280
|
+
|
281
|
+
key-direction 0
|
282
|
+
keepalive 10 60
|
283
|
+
persist-key
|
284
|
+
persist-tun
|
285
|
+
|
286
|
+
proto udp
|
287
|
+
# Rely on Docker to do port mapping, internally always 1194
|
288
|
+
port 1194
|
289
|
+
dev tun0
|
290
|
+
status /tmp/openvpn-status.log
|
291
|
+
|
292
|
+
user nobody
|
293
|
+
group nogroup
|
294
|
+
EOF
|
295
|
+
}
|
296
|
+
end
|
297
|
+
|
298
|
+
def openvpn_env
|
299
|
+
{
|
300
|
+
path: "/etc/openvpn/ovpn_env.sh",
|
301
|
+
mode: "0644",
|
302
|
+
contents: <<EOF
|
303
|
+
declare -x OVPN_SERVER=#{@cidr}
|
304
|
+
EOF
|
305
|
+
}
|
306
|
+
end
|
307
|
+
|
308
|
+
# OpenVPN doesn't wait long enough for the tun0 device to init
|
309
|
+
# https://github.com/kylemanna/docker-openvpn/issues/370
|
310
|
+
def openvpn_ip_delay
|
311
|
+
{
|
312
|
+
path: '/etc/openvpn/ovpn_ip.sh',
|
313
|
+
mode: '0755',
|
314
|
+
contents: <<~IP_SCRIPT
|
315
|
+
#!/usr/bin/env bash
|
316
|
+
sleep 0.1
|
317
|
+
/sbin/ip $*
|
318
|
+
IP_SCRIPT
|
319
|
+
}
|
320
|
+
end
|
321
|
+
|
322
|
+
def with_endpoint_service(*args)
|
323
|
+
@service.with_endpoint_service(*args)
|
324
|
+
end
|
325
|
+
|
326
|
+
def security_group
|
327
|
+
@service.security_group
|
328
|
+
end
|
329
|
+
|
330
|
+
def ingress_security_group
|
331
|
+
@service.ingress_security_group
|
332
|
+
end
|
333
|
+
|
334
|
+
def egress_security_group
|
335
|
+
@service.egress_security_group
|
336
|
+
end
|
337
|
+
|
338
|
+
def pingable_by(*services)
|
339
|
+
@service.pingable_by(*services)
|
340
|
+
end
|
341
|
+
|
342
|
+
def used_by(*services)
|
343
|
+
@service.used_by(*services)
|
344
|
+
end
|
345
|
+
|
346
|
+
def pingable_by_cidr(*cidrs)
|
347
|
+
@service.pingable_by_cidr(*cidrs)
|
348
|
+
end
|
349
|
+
|
350
|
+
def used_by_cidr(*cidrs)
|
351
|
+
@service.used_by_cidr(*cidrs)
|
352
|
+
end
|
353
|
+
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'terrafying/generator'
|
2
|
+
|
3
|
+
module Terrafying
|
4
|
+
|
5
|
+
module Components
|
6
|
+
|
7
|
+
class Zone < Terrafying::Context
|
8
|
+
|
9
|
+
attr_reader :id, :fqdn
|
10
|
+
|
11
|
+
def self.find(fqdn)
|
12
|
+
Zone.new.find fqdn
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.find_by_tag(tag)
|
16
|
+
Zone.new.find_by_tag tag
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.create(fqdn, options={})
|
20
|
+
Zone.new.create fqdn, options
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize()
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def find(fqdn)
|
29
|
+
zone = aws.hosted_zone(fqdn)
|
30
|
+
|
31
|
+
@id = zone.id
|
32
|
+
@fqdn = fqdn
|
33
|
+
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def find_by_tag(tag)
|
38
|
+
zone = aws.hosted_zone_by_tag(tag)
|
39
|
+
@id = zone.id
|
40
|
+
@fqdn = zone.name.chomp(".")
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def create(fqdn, options={})
|
46
|
+
options = {
|
47
|
+
tags: {},
|
48
|
+
}.merge(options)
|
49
|
+
|
50
|
+
ident = tf_safe(fqdn)
|
51
|
+
|
52
|
+
@fqdn = fqdn
|
53
|
+
@id = resource :aws_route53_zone, ident, {
|
54
|
+
name: fqdn,
|
55
|
+
tags: options[:tags],
|
56
|
+
}
|
57
|
+
|
58
|
+
if options[:parent_zone]
|
59
|
+
ns = (0..3).map{ |i| output_of(:aws_route53_zone, ident, "name_servers.#{i}") }
|
60
|
+
|
61
|
+
resource :aws_route53_record, "#{ident}-ns", {
|
62
|
+
zone_id: options[:parent_zone].id,
|
63
|
+
name: fqdn,
|
64
|
+
type: "NS",
|
65
|
+
ttl: "30",
|
66
|
+
records: ns,
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_record(name, records)
|
74
|
+
add_record_in(self, name,records)
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_record_in(ctx, name,records)
|
78
|
+
fqdn = qualify(name)
|
79
|
+
ctx.resource :aws_route53_record, tf_safe(fqdn), {
|
80
|
+
zone_id: @id,
|
81
|
+
name: fqdn,
|
82
|
+
type: "A",
|
83
|
+
ttl: 300,
|
84
|
+
records: records,
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def add_alias(name, config)
|
89
|
+
add_alias_in(self, name, config)
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_alias_in(ctx, name, config)
|
93
|
+
fqdn = qualify(name)
|
94
|
+
ctx.resource :aws_route53_record, tf_safe(fqdn), {
|
95
|
+
zone_id: @id,
|
96
|
+
name: fqdn,
|
97
|
+
type: "A",
|
98
|
+
alias: config,
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def add_srv(name, service_name, port, type, hosts)
|
103
|
+
add_srv_in(self, name, service_name, port, type, hosts)
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_srv_in(ctx, name, service_name, port, type, hosts)
|
107
|
+
fqdn = qualify(name)
|
108
|
+
ident = tf_safe(fqdn)
|
109
|
+
|
110
|
+
ctx.resource :aws_route53_record, "srv-#{ident}-#{service_name}", {
|
111
|
+
zone_id: @id,
|
112
|
+
name: "_#{service_name}._#{type}.#{fqdn}",
|
113
|
+
type: "SRV",
|
114
|
+
ttl: "60",
|
115
|
+
records: hosts.map { |host| "0 0 #{port} #{host}" }
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_cname(name, *records)
|
120
|
+
add_cname_in(self, name, *records)
|
121
|
+
end
|
122
|
+
|
123
|
+
def add_cname_in(ctx, name, *records)
|
124
|
+
fqdn = qualify(name)
|
125
|
+
ident = fqdn.tr('.', '-')
|
126
|
+
ctx.resource :aws_route53_record, ident,
|
127
|
+
{
|
128
|
+
zone_id: @id,
|
129
|
+
name: fqdn,
|
130
|
+
type: 'CNAME',
|
131
|
+
ttl: 300,
|
132
|
+
records: records
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def qualify(name)
|
137
|
+
"#{name}.#{@fqdn}"
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|