certman 0.1.0 → 0.2.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/.rubocop.yml +1 -1
- data/README.md +10 -5
- data/lib/certman/cli.rb +9 -2
- data/lib/certman/client.rb +99 -292
- data/lib/certman/resource/acm.rb +38 -0
- data/lib/certman/resource/route53.rb +140 -0
- data/lib/certman/resource/s3.rb +89 -0
- data/lib/certman/resource/ses.rb +83 -0
- data/lib/certman/resource/sts.rb +9 -0
- data/lib/certman/version.rb +1 -1
- data/lib/certman.rb +5 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc3463ae829bfaef6897bb714c2489d113113465
|
4
|
+
data.tar.gz: 55623eb5f306caa72832a252a06125632344efc9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fab1591da092475a654d4903b9984ec71f4c35b6f85ce69ff4bdfcde40123d3b53557e10f8450d2efff75d777da892542ff424107fecc1eb2b6a81aa42b031f
|
7
|
+
data.tar.gz: 285bae5e5f326b9b6d57e9d32dcf68b01461354ce63d0b3fed8599a68c49f45e67e28ebe3a8e12c1bdb3028537646efe857a6410a17551f758eccddc00a208e0
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -36,15 +36,20 @@ NOTICE! When requesting, Certman replace Active Receipt Rule Set. OK? Yes
|
|
36
36
|
[✔] [Route53] Check MX Record (successfull)
|
37
37
|
[✔] [S3] Create Bucket for SES inbound (successfull)
|
38
38
|
[✔] [SES] Create Domain Identity (successfull)
|
39
|
-
[✔] [Route53]
|
39
|
+
[✔] [Route53] Create TXT Record Set to verify Domain Identity (successfull)
|
40
40
|
[✔] [SES] Check Domain Identity Status *verified* (successfull)
|
41
|
-
[✔] [Route53]
|
41
|
+
[✔] [Route53] Create MX Record Set (successfull)
|
42
|
+
[✔] [SES] Create Receipt Rule Set (successfull)
|
42
43
|
[✔] [SES] Create Receipt Rule (successfull)
|
44
|
+
[✔] [SES] Replace Active Receipt Rule Set (successfull)
|
43
45
|
[✔] [ACM] Request Certificate (successfull)
|
44
46
|
[✔] [S3] Check approval mail (will take about 30 min) (successfull)
|
45
|
-
[✔] [SES]
|
46
|
-
[✔] [
|
47
|
-
[✔] [SES]
|
47
|
+
[✔] [SES] Revert Active Receipt Rule Set (successfull)
|
48
|
+
[✔] [SES] Delete Receipt Rule (successfull)
|
49
|
+
[✔] [SES] Delete Receipt Rule Set (successfull)
|
50
|
+
[✔] [Route53] Delete MX Record Set (successfull)
|
51
|
+
[✔] [Route53] Delete TXT Record Set (successfull)
|
52
|
+
[✔] [SES] Delete Verified Domain Identiry (successfull)
|
48
53
|
[✔] [S3] Delete Bucket (successfull)
|
49
54
|
Done.
|
50
55
|
|
data/lib/certman/cli.rb
CHANGED
@@ -1,12 +1,19 @@
|
|
1
1
|
module Certman
|
2
2
|
class CLI < Thor
|
3
3
|
desc 'request [DOMAIN]', 'Request ACM Certificate with only AWS managed services'
|
4
|
+
option :remain_resources, type: :boolean
|
4
5
|
def request(domain)
|
5
6
|
pastel = Pastel.new
|
6
7
|
prompt = TTY::Prompt.new
|
7
8
|
return unless prompt.yes?(pastel.red('NOTICE! Certman support *us-east-1* only, now. OK?'))
|
8
9
|
return unless prompt.yes?(pastel.red('NOTICE! When requesting, Certman replace Active Receipt Rule Set. OK?'))
|
9
|
-
|
10
|
+
client = Certman::Client.new(domain)
|
11
|
+
Signal.trap(:INT) do
|
12
|
+
puts ''
|
13
|
+
puts pastel.red('Rollback start.')
|
14
|
+
client.rollback
|
15
|
+
end
|
16
|
+
cert_arn = client.request(options[:remain_resources])
|
10
17
|
puts 'Done.'
|
11
18
|
puts ''
|
12
19
|
puts "certificate_arn: #{pastel.cyan(cert_arn)}"
|
@@ -15,7 +22,7 @@ module Certman
|
|
15
22
|
|
16
23
|
desc 'delete [DOMAIN]', 'Delete ACM Certificate'
|
17
24
|
def delete(domain)
|
18
|
-
Certman::Client.new(domain).
|
25
|
+
Certman::Client.new(domain).delete
|
19
26
|
puts 'Done.'
|
20
27
|
puts ''
|
21
28
|
end
|
data/lib/certman/client.rb
CHANGED
@@ -1,350 +1,157 @@
|
|
1
1
|
module Certman
|
2
2
|
class Client
|
3
|
-
|
3
|
+
include Certman::Resource::STS
|
4
|
+
include Certman::Resource::S3
|
5
|
+
include Certman::Resource::SES
|
6
|
+
include Certman::Resource::Route53
|
7
|
+
include Certman::Resource::ACM
|
4
8
|
|
5
9
|
def initialize(domain)
|
10
|
+
@do_rollback = false
|
6
11
|
@domain = domain
|
12
|
+
@cert_arn = nil
|
13
|
+
@savepoint = []
|
7
14
|
end
|
8
15
|
|
9
|
-
def
|
16
|
+
def request(remain_resources = false)
|
10
17
|
check_resource
|
11
18
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
# Create S3 for SES inbound
|
16
|
-
s = spinner('[S3] Create Bucket for SES inbound')
|
17
|
-
bucket_policy = <<-"EOF"
|
18
|
-
{
|
19
|
-
"Version": "2008-10-17",
|
20
|
-
"Statement": [
|
21
|
-
{
|
22
|
-
"Sid": "GiveSESPermissionToWriteEmail",
|
23
|
-
"Effect": "Allow",
|
24
|
-
"Principal": {
|
25
|
-
"Service": [
|
26
|
-
"ses.amazonaws.com"
|
27
|
-
]
|
28
|
-
},
|
29
|
-
"Action": [
|
30
|
-
"s3:PutObject"
|
31
|
-
],
|
32
|
-
"Resource": "arn:aws:s3:::#{bucket_name}/*",
|
33
|
-
"Condition": {
|
34
|
-
"StringEquals": {
|
35
|
-
"aws:Referer": "#{account_id}"
|
36
|
-
}
|
37
|
-
}
|
38
|
-
}
|
39
|
-
]
|
40
|
-
}
|
41
|
-
EOF
|
42
|
-
s3.create_bucket(
|
43
|
-
acl: 'private',
|
44
|
-
bucket: bucket_name
|
45
|
-
)
|
46
|
-
s3.put_bucket_policy(
|
47
|
-
bucket: bucket_name,
|
48
|
-
policy: bucket_policy,
|
49
|
-
use_accelerate_endpoint: false
|
50
|
-
)
|
51
|
-
s.success
|
52
|
-
|
53
|
-
# Create Domain Identity
|
54
|
-
s = spinner('[SES] Create Domain Identity')
|
55
|
-
res = ses.verify_domain_identity(domain: domain)
|
56
|
-
token = res.verification_token
|
57
|
-
s.success
|
58
|
-
|
59
|
-
# Add TXT Record Set with Route53
|
60
|
-
s = spinner('[Route53] Add TXT Record Set to verify Domain Identity')
|
61
|
-
root_domain = PublicSuffix.domain(domain)
|
62
|
-
hosted_zone = route53.list_hosted_zones.hosted_zones.find do |zone|
|
63
|
-
PublicSuffix.domain(zone.name) == root_domain
|
19
|
+
step('[S3] Create Bucket for SES inbound', :s3_bucket) do
|
20
|
+
create_bucket
|
64
21
|
end
|
65
|
-
route53.change_resource_record_sets(
|
66
|
-
change_batch: {
|
67
|
-
changes: [
|
68
|
-
{
|
69
|
-
action: 'CREATE',
|
70
|
-
resource_record_set: {
|
71
|
-
name: "_amazonses.#{domain}",
|
72
|
-
resource_records: [
|
73
|
-
{
|
74
|
-
value: '"' + token + '"'
|
75
|
-
}
|
76
|
-
],
|
77
|
-
ttl: 60,
|
78
|
-
type: 'TXT'
|
79
|
-
}
|
80
|
-
}
|
81
|
-
],
|
82
|
-
comment: 'Generate by certman'
|
83
|
-
},
|
84
|
-
hosted_zone_id: hosted_zone.id
|
85
|
-
)
|
86
|
-
s.success
|
87
22
|
|
88
|
-
|
89
|
-
|
90
|
-
is_break = false
|
91
|
-
100.times do
|
92
|
-
res = ses.get_identity_verification_attributes(
|
93
|
-
identities: [
|
94
|
-
domain
|
95
|
-
]
|
96
|
-
)
|
97
|
-
if res.verification_attributes[domain].verification_status == 'Success'
|
98
|
-
is_break = true
|
99
|
-
s.success
|
100
|
-
break
|
101
|
-
end
|
102
|
-
sleep 5
|
23
|
+
step('[SES] Create Domain Identity', :ses_domain_identity) do
|
24
|
+
create_domain_identity
|
103
25
|
end
|
104
|
-
s.error unless is_break
|
105
26
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
change_batch: {
|
110
|
-
changes: [
|
111
|
-
{
|
112
|
-
action: 'CREATE',
|
113
|
-
resource_record_set: {
|
114
|
-
name: domain,
|
115
|
-
resource_records: [
|
116
|
-
{
|
117
|
-
value: '10 inbound-smtp.us-east-1.amazonaws.com'
|
118
|
-
}
|
119
|
-
],
|
120
|
-
ttl: 60,
|
121
|
-
type: 'MX'
|
122
|
-
}
|
123
|
-
}
|
124
|
-
],
|
125
|
-
comment: 'Generate by certman'
|
126
|
-
},
|
127
|
-
hosted_zone_id: hosted_zone.id
|
128
|
-
)
|
129
|
-
s.success
|
27
|
+
step('[Route53] Create TXT Record Set to verify Domain Identity', :route53_txt) do
|
28
|
+
create_txt_rset
|
29
|
+
end
|
130
30
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
rule_set_name = "RuleSetGeneratedByCertman_#{domain}"
|
135
|
-
ses.create_receipt_rule_set(rule_set_name: rule_set_name)
|
136
|
-
ses.create_receipt_rule(
|
137
|
-
rule: {
|
138
|
-
recipients: ["admin@#{domain}"],
|
139
|
-
actions: [
|
140
|
-
{
|
141
|
-
s3_action: {
|
142
|
-
bucket_name: bucket_name
|
143
|
-
}
|
144
|
-
}
|
145
|
-
],
|
146
|
-
enabled: true,
|
147
|
-
name: rule_name,
|
148
|
-
scan_enabled: true,
|
149
|
-
tls_policy: 'Optional'
|
150
|
-
},
|
151
|
-
rule_set_name: rule_set_name
|
152
|
-
)
|
153
|
-
current_rule_set_name = nil
|
154
|
-
res = ses.describe_active_receipt_rule_set
|
155
|
-
current_rule_set_name = res.metadata.name if res.metadata
|
156
|
-
ses.set_active_receipt_rule_set(rule_set_name: rule_set_name)
|
157
|
-
s.success
|
31
|
+
step('[SES] Check Domain Identity Status *verified*', nil) do
|
32
|
+
check_domain_identity_verified
|
33
|
+
end
|
158
34
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
domain_name: domain,
|
163
|
-
subject_alternative_names: [domain],
|
164
|
-
domain_validation_options: [
|
165
|
-
{
|
166
|
-
domain_name: domain,
|
167
|
-
validation_domain: domain
|
168
|
-
}
|
169
|
-
]
|
170
|
-
)
|
171
|
-
cert_arn = res.certificate_arn
|
172
|
-
s.success
|
35
|
+
step('[Route53] Create MX Record Set', :route53_mx) do
|
36
|
+
create_mx_rset
|
37
|
+
end
|
173
38
|
|
174
|
-
|
175
|
-
|
176
|
-
is_break = false
|
177
|
-
60.times do
|
178
|
-
s3.list_objects(bucket: bucket_name).contents.map do |object|
|
179
|
-
res = s3.get_object(bucket: bucket_name, key: object.key)
|
180
|
-
res.body.read.match(%r{https://certificates\.amazon\.com/approvals[^\s]+}) do |md|
|
181
|
-
cert_uri = md[0]
|
182
|
-
handle = open(cert_uri)
|
183
|
-
document = Oga.parse_html(handle)
|
184
|
-
data = {}
|
185
|
-
document.css('form input').each do |input|
|
186
|
-
data[input.get('name')] = input.get('value')
|
187
|
-
end
|
188
|
-
res = Net::HTTP.post_form(URI.parse('https://certificates.amazon.com/approvals'), data)
|
189
|
-
if res.body =~ /Success/
|
190
|
-
s.success
|
191
|
-
else
|
192
|
-
s.error
|
193
|
-
end
|
194
|
-
is_break = true
|
195
|
-
break
|
196
|
-
end
|
197
|
-
end
|
198
|
-
break if is_break
|
199
|
-
sleep 30
|
39
|
+
step('[SES] Create Receipt Rule Set', :ses_rule_set) do
|
40
|
+
create_rule_set
|
200
41
|
end
|
201
|
-
s.error unless is_break
|
202
42
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
ses.delete_receipt_rule(
|
207
|
-
rule_name: rule_name,
|
208
|
-
rule_set_name: rule_set_name
|
209
|
-
)
|
210
|
-
ses.delete_receipt_rule_set(rule_set_name: rule_set_name)
|
211
|
-
s.success
|
43
|
+
step('[SES] Create Receipt Rule', :ses_rule) do
|
44
|
+
create_rule
|
45
|
+
end
|
212
46
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
change_batch: {
|
217
|
-
changes: [
|
218
|
-
{
|
219
|
-
action: 'DELETE',
|
220
|
-
resource_record_set: {
|
221
|
-
name: "_amazonses.#{domain}",
|
222
|
-
resource_records: [
|
223
|
-
{
|
224
|
-
value: '"' + token + '"'
|
225
|
-
}
|
226
|
-
],
|
227
|
-
ttl: 60,
|
228
|
-
type: 'TXT'
|
229
|
-
}
|
230
|
-
},
|
231
|
-
{
|
232
|
-
action: 'DELETE',
|
233
|
-
resource_record_set: {
|
234
|
-
name: domain,
|
235
|
-
resource_records: [
|
236
|
-
{
|
237
|
-
value: '10 inbound-smtp.us-east-1.amazonaws.com'
|
238
|
-
}
|
239
|
-
],
|
240
|
-
ttl: 60,
|
241
|
-
type: 'MX'
|
242
|
-
}
|
243
|
-
}
|
244
|
-
],
|
245
|
-
comment: 'Generate by certman'
|
246
|
-
},
|
247
|
-
hosted_zone_id: hosted_zone.id
|
248
|
-
)
|
249
|
-
s.success
|
47
|
+
step('[SES] Replace Active Receipt Rule Set', :ses_replace_active_rule_set) do
|
48
|
+
replace_active_rule_set
|
49
|
+
end
|
250
50
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
s.success
|
51
|
+
step('[ACM] Request Certificate', :acm_certificate) do
|
52
|
+
request_certificate
|
53
|
+
end
|
255
54
|
|
256
|
-
|
257
|
-
|
258
|
-
objects = s3.list_objects(bucket: bucket_name).contents.map do |object|
|
259
|
-
{ key: object.key }
|
55
|
+
step('[S3] Check approval mail (will take about 30 min)', nil) do
|
56
|
+
check_approval_mail
|
260
57
|
end
|
261
|
-
s3.delete_objects(
|
262
|
-
bucket: bucket_name,
|
263
|
-
delete: {
|
264
|
-
objects: objects
|
265
|
-
}
|
266
|
-
)
|
267
|
-
s3.delete_bucket(bucket: bucket_name)
|
268
|
-
s.success
|
269
58
|
|
270
|
-
|
59
|
+
cleanup_resources if !remain_resources || @do_rollback
|
60
|
+
|
61
|
+
@cert_arn
|
271
62
|
end
|
272
63
|
|
273
|
-
def
|
64
|
+
def delete
|
274
65
|
s = spinner('[ACM] Delete Certificate')
|
275
|
-
|
276
|
-
cert.domain_name == domain
|
277
|
-
end
|
278
|
-
raise 'Certificate does not exist' unless current_cert
|
279
|
-
acm.delete_certificate(certificate_arn: current_cert.certificate_arn)
|
66
|
+
delete_certificate
|
280
67
|
s.success
|
281
68
|
end
|
282
69
|
|
283
70
|
def check_resource
|
284
71
|
s = spinner('[ACM] Check Certificate')
|
285
|
-
|
286
|
-
cert.domain_name == domain
|
287
|
-
end
|
288
|
-
raise 'Certificate already exist' if current_cert
|
72
|
+
check_certificate
|
289
73
|
s.success
|
290
74
|
|
291
75
|
s = spinner('[Route53] Check Hosted Zone')
|
292
|
-
|
293
|
-
hosted_zone_id = nil
|
294
|
-
hosted_zone = route53.list_hosted_zones.hosted_zones.find do |zone|
|
295
|
-
if PublicSuffix.domain(zone.name) == root_domain
|
296
|
-
hosted_zone_id = zone.id
|
297
|
-
next true
|
298
|
-
end
|
299
|
-
end
|
300
|
-
raise "Hosted Zone #{root_domain} does not exist" unless hosted_zone
|
76
|
+
check_hosted_zone
|
301
77
|
s.success
|
302
78
|
|
303
79
|
s = spinner('[Route53] Check TXT Record')
|
304
|
-
|
305
|
-
hosted_zone_id: hosted_zone_id,
|
306
|
-
start_record_name: "_amazonses.#{domain}.",
|
307
|
-
start_record_type: 'TXT'
|
308
|
-
)
|
309
|
-
raise "_amazonses.#{domain} TXT already exist" unless res.resource_record_sets.empty?
|
80
|
+
check_txt_rset
|
310
81
|
s.success
|
311
82
|
|
312
83
|
s = spinner('[Route53] Check MX Record')
|
313
|
-
|
314
|
-
hosted_zone_id: hosted_zone_id,
|
315
|
-
start_record_name: "#{domain}.",
|
316
|
-
start_record_type: 'MX'
|
317
|
-
)
|
318
|
-
raise "#{domain} MX already exist" unless res.resource_record_sets.empty?
|
84
|
+
check_mx_rset
|
319
85
|
s.success
|
320
86
|
|
321
87
|
true
|
322
88
|
end
|
323
89
|
|
324
|
-
|
325
|
-
|
326
|
-
def sts
|
327
|
-
@sts ||= Aws::STS::Client.new
|
90
|
+
def rollback
|
91
|
+
@do_rollback = true
|
328
92
|
end
|
329
93
|
|
330
|
-
|
331
|
-
|
94
|
+
private
|
95
|
+
|
96
|
+
def step(message, save)
|
97
|
+
return if @do_rollback
|
98
|
+
s = spinner(message)
|
99
|
+
begin
|
100
|
+
yield
|
101
|
+
@savepoint.push(save)
|
102
|
+
s.success
|
103
|
+
rescue
|
104
|
+
puts "Error: #{$ERROR_INFO}"
|
105
|
+
@do_rollback = true
|
106
|
+
s.error
|
107
|
+
end
|
332
108
|
end
|
333
109
|
|
334
|
-
def
|
335
|
-
@
|
110
|
+
def cleanup_resources
|
111
|
+
@savepoint.reverse.each do |state|
|
112
|
+
case state
|
113
|
+
when :s3_bucket
|
114
|
+
s = spinner('[S3] Delete Bucket')
|
115
|
+
delete_bucket
|
116
|
+
s.success
|
117
|
+
when :ses_domain_identity
|
118
|
+
s = spinner('[SES] Delete Verified Domain Identiry')
|
119
|
+
delete_domain_identity
|
120
|
+
s.success
|
121
|
+
when :route53_txt
|
122
|
+
s = spinner('[Route53] Delete TXT Record Set')
|
123
|
+
delete_txt_rset
|
124
|
+
s.success
|
125
|
+
when :route53_mx
|
126
|
+
s = spinner('[Route53] Delete MX Record Set')
|
127
|
+
delete_mx_rset
|
128
|
+
s.success
|
129
|
+
when :ses_rule_set
|
130
|
+
s = spinner('[SES] Delete Receipt Rule Set')
|
131
|
+
delete_rule_set
|
132
|
+
s.success
|
133
|
+
when :ses_rule
|
134
|
+
s = spinner('[SES] Delete Receipt Rule')
|
135
|
+
delete_rule
|
136
|
+
s.success
|
137
|
+
when :ses_replace_active_rule_set
|
138
|
+
s = spinner('[SES] Revert Active Receipt Rule Set')
|
139
|
+
revert_active_rue_set
|
140
|
+
s.success
|
141
|
+
end
|
142
|
+
end
|
336
143
|
end
|
337
144
|
|
338
|
-
def
|
339
|
-
@
|
145
|
+
def bucket_name
|
146
|
+
@bucket_name ||= "#{@domain}-generated-by-certman-for-ses-inbound-"
|
340
147
|
end
|
341
148
|
|
342
|
-
def
|
343
|
-
@
|
149
|
+
def rule_name
|
150
|
+
@rule_name ||= "S3RuleGeneratedByCertman_#{@domain}"
|
344
151
|
end
|
345
152
|
|
346
|
-
def
|
347
|
-
@
|
153
|
+
def rule_set_name
|
154
|
+
@rule_set_name ||= "RuleSetGeneratedByCertman_#{@domain}"
|
348
155
|
end
|
349
156
|
|
350
157
|
def spinner(message)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Certman
|
2
|
+
module Resource
|
3
|
+
module ACM
|
4
|
+
def request_certificate
|
5
|
+
res = acm.request_certificate(
|
6
|
+
domain_name: @domain,
|
7
|
+
subject_alternative_names: [@domain],
|
8
|
+
domain_validation_options: [
|
9
|
+
{
|
10
|
+
domain_name: @domain,
|
11
|
+
validation_domain: @domain
|
12
|
+
}
|
13
|
+
]
|
14
|
+
)
|
15
|
+
@cert_arn = res.certificate_arn
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete_certificate
|
19
|
+
current_cert = acm.list_certificates.certificate_summary_list.find do |cert|
|
20
|
+
cert.domain_name == @domain
|
21
|
+
end
|
22
|
+
raise 'Certificate does not exist' unless current_cert
|
23
|
+
acm.delete_certificate(certificate_arn: current_cert.certificate_arn)
|
24
|
+
end
|
25
|
+
|
26
|
+
def check_certificate
|
27
|
+
current_cert = acm.list_certificates.certificate_summary_list.find do |cert|
|
28
|
+
cert.domain_name == @domain
|
29
|
+
end
|
30
|
+
raise 'Certificate already exist' if current_cert
|
31
|
+
end
|
32
|
+
|
33
|
+
def acm
|
34
|
+
@acm ||= Aws::ACM::Client.new
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Certman
|
2
|
+
module Resource
|
3
|
+
# rubocop:disable Metrics/ModuleLength
|
4
|
+
module Route53
|
5
|
+
def create_txt_rset
|
6
|
+
root_domain = PublicSuffix.domain(@domain)
|
7
|
+
@hosted_zone = route53.list_hosted_zones.hosted_zones.find do |zone|
|
8
|
+
PublicSuffix.domain(zone.name) == root_domain
|
9
|
+
end
|
10
|
+
route53.change_resource_record_sets(
|
11
|
+
change_batch: {
|
12
|
+
changes: [
|
13
|
+
{
|
14
|
+
action: 'CREATE',
|
15
|
+
resource_record_set: {
|
16
|
+
name: "_amazonses.#{@domain}",
|
17
|
+
resource_records: [
|
18
|
+
{
|
19
|
+
value: '"' + @token + '"'
|
20
|
+
}
|
21
|
+
],
|
22
|
+
ttl: 60,
|
23
|
+
type: 'TXT'
|
24
|
+
}
|
25
|
+
}
|
26
|
+
],
|
27
|
+
comment: 'Generate by certman'
|
28
|
+
},
|
29
|
+
hosted_zone_id: @hosted_zone.id
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_mx_rset
|
34
|
+
route53.change_resource_record_sets(
|
35
|
+
change_batch: {
|
36
|
+
changes: [
|
37
|
+
{
|
38
|
+
action: 'CREATE',
|
39
|
+
resource_record_set: {
|
40
|
+
name: @domain,
|
41
|
+
resource_records: [
|
42
|
+
{
|
43
|
+
value: '10 inbound-smtp.us-east-1.amazonaws.com'
|
44
|
+
}
|
45
|
+
],
|
46
|
+
ttl: 60,
|
47
|
+
type: 'MX'
|
48
|
+
}
|
49
|
+
}
|
50
|
+
],
|
51
|
+
comment: 'Generate by certman'
|
52
|
+
},
|
53
|
+
hosted_zone_id: @hosted_zone.id
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_txt_rset
|
58
|
+
route53.change_resource_record_sets(
|
59
|
+
change_batch: {
|
60
|
+
changes: [
|
61
|
+
{
|
62
|
+
action: 'DELETE',
|
63
|
+
resource_record_set: {
|
64
|
+
name: "_amazonses.#{@domain}",
|
65
|
+
resource_records: [
|
66
|
+
{
|
67
|
+
value: '"' + @token + '"'
|
68
|
+
}
|
69
|
+
],
|
70
|
+
ttl: 60,
|
71
|
+
type: 'TXT'
|
72
|
+
}
|
73
|
+
}
|
74
|
+
],
|
75
|
+
comment: 'Generate by certman'
|
76
|
+
},
|
77
|
+
hosted_zone_id: @hosted_zone.id
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete_mx_rset
|
82
|
+
route53.change_resource_record_sets(
|
83
|
+
change_batch: {
|
84
|
+
changes: [
|
85
|
+
{
|
86
|
+
action: 'DELETE',
|
87
|
+
resource_record_set: {
|
88
|
+
name: @domain,
|
89
|
+
resource_records: [
|
90
|
+
{
|
91
|
+
value: '10 inbound-smtp.us-east-1.amazonaws.com'
|
92
|
+
}
|
93
|
+
],
|
94
|
+
ttl: 60,
|
95
|
+
type: 'MX'
|
96
|
+
}
|
97
|
+
}
|
98
|
+
],
|
99
|
+
comment: 'Generate by certman'
|
100
|
+
},
|
101
|
+
hosted_zone_id: @hosted_zone.id
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def check_hosted_zone
|
106
|
+
root_domain = PublicSuffix.domain(@domain)
|
107
|
+
@hosted_zone_id = nil
|
108
|
+
hosted_zone = route53.list_hosted_zones.hosted_zones.find do |zone|
|
109
|
+
if PublicSuffix.domain(zone.name) == root_domain
|
110
|
+
@hosted_zone_id = zone.id
|
111
|
+
next true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
raise "Hosted Zone #{root_domain} does not exist" unless hosted_zone
|
115
|
+
end
|
116
|
+
|
117
|
+
def check_txt_rset
|
118
|
+
res = route53.list_resource_record_sets(
|
119
|
+
hosted_zone_id: @hosted_zone_id,
|
120
|
+
start_record_name: "_amazonses.#{@domain}.",
|
121
|
+
start_record_type: 'TXT'
|
122
|
+
)
|
123
|
+
raise "_amazonses.#{@domain} TXT already exist" unless res.resource_record_sets.empty?
|
124
|
+
end
|
125
|
+
|
126
|
+
def check_mx_rset
|
127
|
+
res = route53.list_resource_record_sets(
|
128
|
+
hosted_zone_id: @hosted_zone_id,
|
129
|
+
start_record_name: "#{@domain}.",
|
130
|
+
start_record_type: 'MX'
|
131
|
+
)
|
132
|
+
raise "#{@domain} MX already exist" unless res.resource_record_sets.empty?
|
133
|
+
end
|
134
|
+
|
135
|
+
def route53
|
136
|
+
@route53 ||= Aws::Route53::Client.new
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Certman
|
2
|
+
module Resource
|
3
|
+
module S3
|
4
|
+
def create_bucket
|
5
|
+
account_id = sts.get_caller_identity.account
|
6
|
+
bucket_policy = <<-"EOF"
|
7
|
+
{
|
8
|
+
"Version": "2008-10-17",
|
9
|
+
"Statement": [
|
10
|
+
{
|
11
|
+
"Sid": "GiveSESPermissionToWriteEmail",
|
12
|
+
"Effect": "Allow",
|
13
|
+
"Principal": {
|
14
|
+
"Service": [
|
15
|
+
"ses.amazonaws.com"
|
16
|
+
]
|
17
|
+
},
|
18
|
+
"Action": [
|
19
|
+
"s3:PutObject"
|
20
|
+
],
|
21
|
+
"Resource": "arn:aws:s3:::#{bucket_name}/*",
|
22
|
+
"Condition": {
|
23
|
+
"StringEquals": {
|
24
|
+
"aws:Referer": "#{account_id}"
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
]
|
29
|
+
}
|
30
|
+
EOF
|
31
|
+
s3.create_bucket(
|
32
|
+
acl: 'private',
|
33
|
+
bucket: bucket_name
|
34
|
+
)
|
35
|
+
s3.put_bucket_policy(
|
36
|
+
bucket: bucket_name,
|
37
|
+
policy: bucket_policy,
|
38
|
+
use_accelerate_endpoint: false
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_approval_mail
|
43
|
+
is_break = false
|
44
|
+
60.times do
|
45
|
+
s3.list_objects(bucket: bucket_name).contents.map do |object|
|
46
|
+
res = s3.get_object(bucket: bucket_name, key: object.key)
|
47
|
+
res.body.read.match(%r{https://certificates\.amazon\.com/approvals[^\s]+}) do |md|
|
48
|
+
cert_uri = md[0]
|
49
|
+
handle = open(cert_uri)
|
50
|
+
document = Oga.parse_html(handle)
|
51
|
+
data = {}
|
52
|
+
document.css('form input').each do |input|
|
53
|
+
data[input.get('name')] = input.get('value')
|
54
|
+
end
|
55
|
+
res = Net::HTTP.post_form(URI.parse('https://certificates.amazon.com/approvals'), data)
|
56
|
+
raise 'Can not approve' unless res.body =~ /Success/
|
57
|
+
# success
|
58
|
+
is_break = true
|
59
|
+
break
|
60
|
+
end
|
61
|
+
end
|
62
|
+
break if is_break
|
63
|
+
break if @do_rollback
|
64
|
+
sleep 30
|
65
|
+
end
|
66
|
+
raise 'Can not approve' unless is_break
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete_bucket
|
70
|
+
objects = s3.list_objects(bucket: bucket_name).contents.map do |object|
|
71
|
+
{ key: object.key }
|
72
|
+
end
|
73
|
+
unless objects.empty?
|
74
|
+
s3.delete_objects(
|
75
|
+
bucket: bucket_name,
|
76
|
+
delete: {
|
77
|
+
objects: objects
|
78
|
+
}
|
79
|
+
)
|
80
|
+
end
|
81
|
+
s3.delete_bucket(bucket: bucket_name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def s3
|
85
|
+
@s3 ||= Aws::S3::Client.new
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Certman
|
2
|
+
module Resource
|
3
|
+
module SES
|
4
|
+
def create_domain_identity
|
5
|
+
res = ses.verify_domain_identity(domain: @domain)
|
6
|
+
@token = res.verification_token
|
7
|
+
end
|
8
|
+
|
9
|
+
def check_domain_identity_verified
|
10
|
+
is_break = false
|
11
|
+
100.times do
|
12
|
+
res = ses.get_identity_verification_attributes(
|
13
|
+
identities: [
|
14
|
+
@domain
|
15
|
+
]
|
16
|
+
)
|
17
|
+
if res.verification_attributes[@domain].verification_status == 'Success'
|
18
|
+
# success
|
19
|
+
is_break = true
|
20
|
+
break
|
21
|
+
end
|
22
|
+
break if @do_rollback
|
23
|
+
sleep 5
|
24
|
+
end
|
25
|
+
raise 'Can not check verified' unless is_break
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_domain_identity
|
29
|
+
ses.delete_identity(identity: @domain)
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_rule_set
|
33
|
+
ses.create_receipt_rule_set(rule_set_name: rule_set_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_rule
|
37
|
+
ses.create_receipt_rule(
|
38
|
+
rule: {
|
39
|
+
recipients: ["admin@#{@domain}"],
|
40
|
+
actions: [
|
41
|
+
{
|
42
|
+
s3_action: {
|
43
|
+
bucket_name: bucket_name
|
44
|
+
}
|
45
|
+
}
|
46
|
+
],
|
47
|
+
enabled: true,
|
48
|
+
name: rule_name,
|
49
|
+
scan_enabled: true,
|
50
|
+
tls_policy: 'Optional'
|
51
|
+
},
|
52
|
+
rule_set_name: rule_set_name
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def replace_active_rule_set
|
57
|
+
@current_rule_set_name = nil
|
58
|
+
res = ses.describe_active_receipt_rule_set
|
59
|
+
@current_rule_set_name = res.metadata.name if res.metadata
|
60
|
+
ses.set_active_receipt_rule_set(rule_set_name: rule_set_name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def delete_rule_set
|
64
|
+
ses.delete_receipt_rule_set(rule_set_name: rule_set_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete_rule
|
68
|
+
ses.delete_receipt_rule(
|
69
|
+
rule_name: rule_name,
|
70
|
+
rule_set_name: rule_set_name
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def revert_active_rue_set
|
75
|
+
ses.set_active_receipt_rule_set(rule_set_name: @current_rule_set_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def ses
|
79
|
+
@ses ||= Aws::SES::Client.new
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/certman/version.rb
CHANGED
data/lib/certman.rb
CHANGED
@@ -8,6 +8,11 @@ require 'open-uri'
|
|
8
8
|
require 'tty-prompt'
|
9
9
|
require 'tty-spinner'
|
10
10
|
require 'pastel'
|
11
|
+
require 'certman/resource/sts'
|
12
|
+
require 'certman/resource/s3'
|
13
|
+
require 'certman/resource/ses'
|
14
|
+
require 'certman/resource/route53'
|
15
|
+
require 'certman/resource/acm'
|
11
16
|
require 'certman/client'
|
12
17
|
require 'certman/cli'
|
13
18
|
require 'certman/log'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: certman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- k1LoW
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
@@ -230,6 +230,11 @@ files:
|
|
230
230
|
- lib/certman/cli.rb
|
231
231
|
- lib/certman/client.rb
|
232
232
|
- lib/certman/log.rb
|
233
|
+
- lib/certman/resource/acm.rb
|
234
|
+
- lib/certman/resource/route53.rb
|
235
|
+
- lib/certman/resource/s3.rb
|
236
|
+
- lib/certman/resource/ses.rb
|
237
|
+
- lib/certman/resource/sts.rb
|
233
238
|
- lib/certman/version.rb
|
234
239
|
homepage: https://github.com/k1LoW/certman
|
235
240
|
licenses:
|