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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c445377d251318c7c321d2482081a0fea5d79e47fb09534a584e0560569495d8
4
- data.tar.gz: 156972b6c25200b5b0bfc9721e7fe179230a8e61745c39ca0f9c70ced7b879c5
3
+ metadata.gz: 3577f779cfb8aaaefa37ed0ad43635a37ae154cb34dbc0a6442177dd2abc1786
4
+ data.tar.gz: 1ed1adf2f61d82697438d1338b99276405d04c6688f84047a297a6ae4bdf8a5e
5
5
  SHA512:
6
- metadata.gz: 9802e7383383368c37b2df829ee2e79bc34470caab1eabbd447a48a8473d2b8306d9b2d577b39e1559e3fae9bb7a479d144b68a11739de6e50c9323a23292458
7
- data.tar.gz: 3866dc90d8e9a0cdf6e5fba8633e76710dd356efd14e4c2a6b50ca8cf3cc57eb57aeab453c0d9d40b3afd3035c74ec02d3c8453bd937012c1facd0619bb1cfae
6
+ metadata.gz: 9b23eba0da42b8a9bfa34251c545699f8468bb14d16392ae271f032fec6d01399995a8aeef66c52ae58a3f84a2e080e67d2ad46dd38c22445631539681d21bcc
7
+ data.tar.gz: 3715ab338a9947a5c0697a41011a74b3f91335da4b68148498fd9a0a118457c8d1f15290b4cffbe6285191ebb0f2b4a73f3b88dbc85377eba4173cf10a1740d2
@@ -0,0 +1,7 @@
1
+ class ::Hash
2
+ def merge_with_arrays_merged(newhash)
3
+ merge(newhash) do |_key, oldval, newval|
4
+ oldval.is_a?(Array) ? oldval | Array(newval) : newval
5
+ end
6
+ end
7
+ end
@@ -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