acme-r53-cli 0.0.1
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 +7 -0
- data/bin/acme-r53.rb +221 -0
- data/lib/acme-cli/cli.rb +3 -0
- data/lib/acme-cli/version.rb +5 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0bffb5e1041e5b53ca07b2bfc74e50f54f72d52ac4887d064bb9d1c2fa819a3a
|
4
|
+
data.tar.gz: a537081d73193d5a501dc84f3c969e4163536169cbb3cd976d5346d4be2d85ef
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a10e69c4040799553083bbbb5c07f6a3ef1a6606a3400aa291174124acd8d48092ef07da6715c4fab730a55d178c714b1f12457ad2de467dd38a512a2f98c769
|
7
|
+
data.tar.gz: 780afcc3f216d9beb8d66f596c6a4e12b2e7dd11058154134972264b400214184ba1f19cbdf19b0d1ed26beb465d607e787da179a1515944f1fb4b03c1aab9b8
|
data/bin/acme-r53.rb
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "openssl"
|
4
|
+
|
5
|
+
require "docopt"
|
6
|
+
require "acme-client"
|
7
|
+
require "aws-sdk-route53"
|
8
|
+
require "pry"
|
9
|
+
|
10
|
+
require "acme-cli/version"
|
11
|
+
|
12
|
+
doc = <<DOCOPT
|
13
|
+
acme.rb
|
14
|
+
|
15
|
+
Usage:
|
16
|
+
acme.rb sign [options] <domain_name> [<alt_name>...]
|
17
|
+
acme.rb register [options] --email <email> [--agree-terms]
|
18
|
+
acme.rb -h | --help
|
19
|
+
acme.rb -v | --version
|
20
|
+
|
21
|
+
Options:
|
22
|
+
-h --help Show this message
|
23
|
+
-v --version Show the version
|
24
|
+
--account <account.pem> Provide existing account key
|
25
|
+
--domain <domain.pem> Provide domain private key
|
26
|
+
--staging Use LE Staging directory
|
27
|
+
|
28
|
+
DOCOPT
|
29
|
+
|
30
|
+
def load_key(passed_in, passed_in_path, default_path)
|
31
|
+
if passed_in
|
32
|
+
STDERR.puts "Loading #{passed_in_path}"
|
33
|
+
if File.file?(passed_in_path)
|
34
|
+
STDERR.puts "[Error]: Cannot load #{passed_in_path}"
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
OpenSSL::PKey::RSA.new(File.read(passed_in_path))
|
38
|
+
else
|
39
|
+
if File.file?(default_path)
|
40
|
+
STDERR.puts "Loading #{default_path}"
|
41
|
+
OpenSSL::PKey::RSA.new(File.read(default_path))
|
42
|
+
else
|
43
|
+
STDERR.puts "Creating key #{default_path}"
|
44
|
+
key = OpenSSL::PKey::RSA.new(4096)
|
45
|
+
File.write(default_path, key)
|
46
|
+
key
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_test_name(entry, challenge)
|
52
|
+
if entry.include?("*")
|
53
|
+
"#{challenge.record_name}.#{entry.gsub("*.", "")}"
|
54
|
+
else
|
55
|
+
"#{challenge.record_name}.#{entry}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_csr_names(identifier)
|
60
|
+
if identifier.include?("*")
|
61
|
+
[
|
62
|
+
identifier
|
63
|
+
]
|
64
|
+
else
|
65
|
+
[
|
66
|
+
identifier
|
67
|
+
]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_zone_name(record)
|
72
|
+
parts = record.split(/\./)
|
73
|
+
parts.slice(parts.length - 2, parts.length - 1).join(".") + "."
|
74
|
+
end
|
75
|
+
|
76
|
+
def update_dns(entry, challenge)
|
77
|
+
r53 = Aws::Route53::Client.new
|
78
|
+
top_domain = get_zone_name(entry)
|
79
|
+
zone = r53.list_hosted_zones.hosted_zones.select do |zone|
|
80
|
+
zone.name == top_domain
|
81
|
+
end.first
|
82
|
+
|
83
|
+
record = get_test_name(entry, challenge)
|
84
|
+
|
85
|
+
change = r53.change_resource_record_sets({
|
86
|
+
change_batch: {
|
87
|
+
changes: [{
|
88
|
+
action: "CREATE",
|
89
|
+
resource_record_set: {
|
90
|
+
resource_records: [
|
91
|
+
{
|
92
|
+
value: "\"#{challenge.record_content}\"",
|
93
|
+
},
|
94
|
+
],
|
95
|
+
name: record,
|
96
|
+
ttl: 10,
|
97
|
+
type: challenge.record_type
|
98
|
+
}
|
99
|
+
}]
|
100
|
+
},
|
101
|
+
hosted_zone_id: zone.id
|
102
|
+
})
|
103
|
+
|
104
|
+
r53.wait_until(:resource_record_sets_changed, id: change.change_info.id)
|
105
|
+
|
106
|
+
[zone.id, record, challenge.record_type, challenge.record_content]
|
107
|
+
end
|
108
|
+
|
109
|
+
def delete_dns_record(zone_id, record, type, value)
|
110
|
+
r53 = Aws::Route53::Client.new
|
111
|
+
|
112
|
+
r53.change_resource_record_sets({
|
113
|
+
change_batch: {
|
114
|
+
changes: [{
|
115
|
+
action: "DELETE",
|
116
|
+
resource_record_set: {
|
117
|
+
resource_records: [
|
118
|
+
{
|
119
|
+
value: "\"#{value}\"",
|
120
|
+
},
|
121
|
+
],
|
122
|
+
name: record,
|
123
|
+
ttl: 10,
|
124
|
+
type: type
|
125
|
+
}
|
126
|
+
}]
|
127
|
+
},
|
128
|
+
hosted_zone_id: zone_id
|
129
|
+
})
|
130
|
+
end
|
131
|
+
|
132
|
+
options = nil
|
133
|
+
|
134
|
+
begin
|
135
|
+
options = Docopt::docopt(doc)
|
136
|
+
rescue Docopt::Exit => e
|
137
|
+
STDERR.puts e.message
|
138
|
+
end
|
139
|
+
|
140
|
+
exit 1 if options == nil
|
141
|
+
|
142
|
+
if options["--version"]
|
143
|
+
STDERR.puts AcmeCli::Version::VERSION
|
144
|
+
exit 0
|
145
|
+
end
|
146
|
+
|
147
|
+
## Load account key, or create on if missing
|
148
|
+
account_key = load_key(
|
149
|
+
!!options["--account"],
|
150
|
+
options["--account"],
|
151
|
+
"./account.pem"
|
152
|
+
)
|
153
|
+
|
154
|
+
directory = if options["--staging"]
|
155
|
+
"https://acme-staging-v02.api.letsencrypt.org/directory"
|
156
|
+
else
|
157
|
+
"https://acme-v02.api.letsencrypt.org/directory"
|
158
|
+
end
|
159
|
+
|
160
|
+
client = Acme::Client.new(
|
161
|
+
private_key: account_key,
|
162
|
+
directory: directory
|
163
|
+
)
|
164
|
+
|
165
|
+
if options["register"]
|
166
|
+
account = client.new_account(
|
167
|
+
contact: "mailto:#{options["<email>"]}",
|
168
|
+
terms_of_service_agreed: options["--agree-terms"]
|
169
|
+
)
|
170
|
+
STDERR.puts "Created account #{account.kid}"
|
171
|
+
elsif options["sign"]
|
172
|
+
identifiers = if options["<alt_name>"]
|
173
|
+
options["<alt_name>"].unshift(options["<domain_name>"])
|
174
|
+
else
|
175
|
+
[options["<domain_name>"]]
|
176
|
+
end
|
177
|
+
STDERR.puts "Creating order for: #{identifiers.join(", ")}"
|
178
|
+
order = client.new_order(identifiers: identifiers)
|
179
|
+
|
180
|
+
order.authorizations.each_with_index do |auth, idx|
|
181
|
+
challenge = auth.dns
|
182
|
+
|
183
|
+
STDERR.puts "Creating dns record for: #{identifiers[idx]}"
|
184
|
+
record_created = update_dns(identifiers[idx], challenge)
|
185
|
+
|
186
|
+
STDERR.puts "Validating dns record: #{identifiers[idx]}"
|
187
|
+
challenge.request_validation
|
188
|
+
while challenge.status == 'pending'
|
189
|
+
sleep(2)
|
190
|
+
challenge.reload
|
191
|
+
end
|
192
|
+
|
193
|
+
STDERR.puts "Validated dns record: #{identifiers[idx]}" if challenge.status == "valid"
|
194
|
+
|
195
|
+
STDERR.puts "Delete dns record for: #{identifiers.first}"
|
196
|
+
delete_dns_record(*record_created)
|
197
|
+
|
198
|
+
if challenge.status != 'valid'
|
199
|
+
STDERR.puts "failed challenge for #{identifiers[idx]}: #{challenge.status}"
|
200
|
+
exit 1
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
domain_key = load_key(
|
205
|
+
!!options["--domain"],
|
206
|
+
options["--domain"],
|
207
|
+
"./domain.pem"
|
208
|
+
)
|
209
|
+
|
210
|
+
STDERR.puts "Signing cert for: #{identifiers.join(", ")}"
|
211
|
+
|
212
|
+
csr = Acme::Client::CertificateRequest.new(
|
213
|
+
private_key: domain_key,
|
214
|
+
common_name: identifiers.first,
|
215
|
+
names: identifiers[1..-1]
|
216
|
+
)
|
217
|
+
order.finalize(csr: csr)
|
218
|
+
sleep(1) while order.status == 'processing'
|
219
|
+
|
220
|
+
puts order.certificate
|
221
|
+
end
|
data/lib/acme-cli/cli.rb
ADDED
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acme-r53-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ethan Apocaca
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-05-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: docopt
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk-route53
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.9'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: acme-client
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
description: A cli interface for ACMEv2 DNS challenges with Route53
|
56
|
+
email: papodaca@gmail.com
|
57
|
+
executables:
|
58
|
+
- acme-r53.rb
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- bin/acme-r53.rb
|
63
|
+
- lib/acme-cli/cli.rb
|
64
|
+
- lib/acme-cli/version.rb
|
65
|
+
homepage: https://github.com/papodaca/acme-cli
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.7.6
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: A cli interface for ACMEv2 DNS challenges with Route53
|
89
|
+
test_files: []
|