amarillo 0.3.2 → 0.4.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/bin/amarillo +34 -9
- data/lib/amarillo/environment.rb +4 -1
- data/lib/amarillo.rb +76 -25
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5229f8188b6086ad5c09326cc438d8375703602036e50534cbb69d684a2ec9e7
|
4
|
+
data.tar.gz: b1a7005a37527eb00fd44dbc45cfa0ea3e232f90bbd8ad004f712bb74d8be0a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b167194f5fbb58f945fb5674d9466473cf9777a04536cbb709bdf7e75e018666c7421beb28dfc300ad49c5e522f2250339f8b92c882273608525084708caa088
|
7
|
+
data.tar.gz: 1e40be3bb8babc91470dca9588c51467cd572e99dada1d9e716f4cbe57e669c0731956a3e36598c35a37a9a015369b88b01cb7ffe604442463d6b884dd1d907d
|
data/bin/amarillo
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
#
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2022 iAchieved.it LLC
|
4
4
|
#
|
5
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -35,12 +35,16 @@ OptionParser.new do |opts|
|
|
35
35
|
options[:list] = l
|
36
36
|
end
|
37
37
|
|
38
|
-
opts.on("-d", "--delete", "Delete certificate") do |d|
|
38
|
+
opts.on("-d", "--delete COMMONNAME", "Delete certificate specified by COMMONNAME") do |d|
|
39
39
|
options[:delete] = d
|
40
40
|
end
|
41
41
|
|
42
|
-
opts.on("-r", "--renew", "Renew certificates") do |r|
|
43
|
-
|
42
|
+
opts.on("-r", "--renew [COMMONNAME]", "Renew specific or all certificates") do |r|
|
43
|
+
if r
|
44
|
+
options[:renew] = r
|
45
|
+
else
|
46
|
+
options[:renew] = 'all'
|
47
|
+
end
|
44
48
|
end
|
45
49
|
|
46
50
|
opts.on("-z", "--zone ZONE", "Hosted zone") do |z|
|
@@ -55,6 +59,14 @@ OptionParser.new do |opts|
|
|
55
59
|
options[:name] = n
|
56
60
|
end
|
57
61
|
|
62
|
+
opts.on("-k", "--keytype KEYTYPE", "Valid key types: ") do |k|
|
63
|
+
options[:keytype] = k
|
64
|
+
end
|
65
|
+
|
66
|
+
opts.on("-s", "--script SCRIPT", "A script or command to execute after a successful certificate creation or renewal") do |s|
|
67
|
+
options[:script] = s
|
68
|
+
end
|
69
|
+
|
58
70
|
opts.on("-a", "--amarillo-home AMARILLO_HOME", "Home directory for configuration, keys, and certificates") do |o|
|
59
71
|
options[:amarillo_home] = a
|
60
72
|
end
|
@@ -103,8 +115,8 @@ else
|
|
103
115
|
email = options[:email]
|
104
116
|
end
|
105
117
|
|
106
|
-
if options[:name].nil? and options[:renew].nil? and options[:list].nil? then
|
107
|
-
puts "Usage: amarillo --name COMMONNAME [--zone ZONE] [--email EMAIL] [--amarillo-home AMARILLO_HOME]"
|
118
|
+
if options[:name].nil? and options[:renew].nil? and options[:list].nil? and options[:delete].nil? then
|
119
|
+
puts "Usage: amarillo [--name COMMONNAME|--renew|--renew COMMONNAME] [--zone ZONE] [--email EMAIL] [--amarillo-home AMARILLO_HOME]"
|
108
120
|
exit -1
|
109
121
|
else
|
110
122
|
name = options[:name]
|
@@ -119,13 +131,26 @@ end
|
|
119
131
|
y = Amarillo.new amarillo_home
|
120
132
|
|
121
133
|
if options[:renew] then
|
122
|
-
|
134
|
+
|
135
|
+
if options[:renew] != 'all' then
|
136
|
+
y.renewCertificate options[:renew]
|
137
|
+
else
|
138
|
+
y.renewCertificates
|
139
|
+
end
|
140
|
+
|
123
141
|
elsif options[:list] then
|
124
142
|
y.listCertificates
|
125
143
|
elsif options[:delete] then
|
126
|
-
y.deleteCertificate
|
144
|
+
y.deleteCertificate options[:delete]
|
127
145
|
else
|
128
|
-
|
146
|
+
config = {
|
147
|
+
"zone" => zone,
|
148
|
+
"commonName" => name,
|
149
|
+
"email" => email,
|
150
|
+
"key_type" => options[:keytype],
|
151
|
+
"script" => options[:script]
|
152
|
+
}
|
153
|
+
y.requestCertificate certConfig: config
|
129
154
|
end
|
130
155
|
|
131
156
|
|
data/lib/amarillo/environment.rb
CHANGED
@@ -105,7 +105,10 @@ HEREDOC
|
|
105
105
|
"email" => email,
|
106
106
|
"zone" => zone,
|
107
107
|
"nameservers" => ['208.67.222.222', '9.9.9.9'],
|
108
|
-
"key_type" => 'ec,secp384r1'
|
108
|
+
"key_type" => 'ec,secp384r1',
|
109
|
+
"owner" => 'root',
|
110
|
+
"group" => 'root',
|
111
|
+
"key_mode" => 0660
|
109
112
|
}}
|
110
113
|
File.write(@configFile, config.to_yaml)
|
111
114
|
else
|
data/lib/amarillo.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Inspired by Pete Keen's (pete@petekeen.net) post
|
4
4
|
# https://www.petekeen.net/lets-encrypt-without-certbot
|
5
5
|
#
|
6
|
-
# Copyright
|
6
|
+
# Copyright 2022 iAchieved.it LLC
|
7
7
|
#
|
8
8
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
9
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -86,7 +86,17 @@ class Amarillo
|
|
86
86
|
|
87
87
|
end
|
88
88
|
|
89
|
-
def requestCertificate(
|
89
|
+
def requestCertificate(configPath: nil, certConfig: nil)
|
90
|
+
|
91
|
+
if configPath
|
92
|
+
certConfig = YAML.load(File.read(configPath))
|
93
|
+
end
|
94
|
+
|
95
|
+
commonName = certConfig["commonName"]
|
96
|
+
email = certConfig["email"]
|
97
|
+
zone = certConfig["zone"]
|
98
|
+
key_type = certConfig["key_type"]
|
99
|
+
script = certConfig["script"]
|
90
100
|
|
91
101
|
@zone = zone
|
92
102
|
|
@@ -139,11 +149,16 @@ class Amarillo
|
|
139
149
|
|
140
150
|
@route53.change_resource_record_sets(options)
|
141
151
|
|
152
|
+
at_exit do
|
153
|
+
self.cleanup label, record_type, challengeValue
|
154
|
+
end
|
155
|
+
|
156
|
+
|
142
157
|
nameservers = @environment.get_zone_nameservers
|
143
158
|
|
144
159
|
@logger.info "Waiting for DNS record to propagate"
|
145
160
|
while !check_dns(commonName, nameservers, challengeValue)
|
146
|
-
sleep
|
161
|
+
sleep 5
|
147
162
|
@logger.info "Still waiting..."
|
148
163
|
end
|
149
164
|
|
@@ -152,20 +167,13 @@ class Amarillo
|
|
152
167
|
@logger.info "Requesting validation..."
|
153
168
|
authorization.dns.reload
|
154
169
|
while authorization.dns.status == 'pending'
|
155
|
-
sleep
|
170
|
+
sleep 5
|
156
171
|
@logger.info "DNS status: #{authorization.dns.status}"
|
157
172
|
authorization.dns.reload
|
158
173
|
end
|
159
174
|
|
160
175
|
@logger.info "Generating key"
|
161
176
|
|
162
|
-
# Create certificate yml
|
163
|
-
certConfig = {
|
164
|
-
"commonName" => commonName,
|
165
|
-
"email" => email,
|
166
|
-
"zone" => zone
|
167
|
-
}
|
168
|
-
|
169
177
|
if key_type
|
170
178
|
certConfig["key_type"] = key_type
|
171
179
|
else
|
@@ -178,16 +186,22 @@ class Amarillo
|
|
178
186
|
if type == 'ec' then
|
179
187
|
certPrivateKey = OpenSSL::PKey::EC.new(args).generate_key
|
180
188
|
elsif type == 'rsa' then
|
181
|
-
|
189
|
+
if args.to_i > 0
|
190
|
+
certPrivateKey = OpenSSL::PKey::RSA.new(args.to_i)
|
191
|
+
else
|
192
|
+
@logger.error("Invalid RSA key size: #{args}")
|
193
|
+
end
|
182
194
|
end
|
183
195
|
|
184
196
|
@logger.info "Requesting certificate..."
|
185
197
|
csr = Acme::Client::CertificateRequest.new private_key: certPrivateKey,
|
186
198
|
names: [commonName]
|
187
199
|
|
188
|
-
while order.status
|
200
|
+
while order.status != 'ready'
|
189
201
|
sleep(1)
|
202
|
+
@logger.info "Order status: #{order.status}"
|
190
203
|
order.reload
|
204
|
+
raise if order.status == 'invalid'
|
191
205
|
end
|
192
206
|
|
193
207
|
@logger.info "Order status: #{order.status}"
|
@@ -196,7 +210,7 @@ class Amarillo
|
|
196
210
|
order.finalize(csr: csr)
|
197
211
|
rescue
|
198
212
|
@logger.error("Error finalizing certificate order")
|
199
|
-
|
213
|
+
raise
|
200
214
|
end
|
201
215
|
|
202
216
|
keyOutputPath = "#{@keyPath}/#{commonName}.key"
|
@@ -207,7 +221,13 @@ class Amarillo
|
|
207
221
|
File.open(keyOutputPath, "w") do |f|
|
208
222
|
f.puts certPrivateKey.to_pem.to_s
|
209
223
|
end
|
210
|
-
|
224
|
+
|
225
|
+
keyMode = @config["defaults"]["key_mode"] || 0600
|
226
|
+
if keyMode
|
227
|
+
File.chmod(keyMode, keyOutputPath)
|
228
|
+
else
|
229
|
+
File.chmod(0600, keyOutputPath)
|
230
|
+
end
|
211
231
|
|
212
232
|
@logger.info "Saving certificate to #{certOutputPath}"
|
213
233
|
|
@@ -215,10 +235,20 @@ class Amarillo
|
|
215
235
|
f.puts order.certificate
|
216
236
|
end
|
217
237
|
|
238
|
+
owner = certConfig["owner"] || @config["defaults"]["owner"] || "root"
|
239
|
+
group = certConfig["group"] || @config["defaults"]["group"] || "root"
|
240
|
+
begin
|
241
|
+
FileUtils.chown(owner, group, keyOutputPath)
|
242
|
+
rescue
|
243
|
+
@logger.info "Unable to change ownership of key file #{keyOutputPath}"
|
244
|
+
end
|
245
|
+
|
218
246
|
certConfigFile = "#{@configsPath}/#{commonName}.yml"
|
247
|
+
|
248
|
+
@logger.info "Saving certificate configuration to #{certConfigFile}"
|
219
249
|
File.write(certConfigFile, certConfig.to_yaml)
|
220
250
|
|
221
|
-
self.
|
251
|
+
self.doRenewalAction script
|
222
252
|
|
223
253
|
end
|
224
254
|
|
@@ -247,10 +277,6 @@ class Amarillo
|
|
247
277
|
@route53.change_resource_record_sets(options)
|
248
278
|
end
|
249
279
|
|
250
|
-
def renewCertificate(zone, commonName, email)
|
251
|
-
|
252
|
-
end
|
253
|
-
|
254
280
|
def listCertificates
|
255
281
|
|
256
282
|
rows = []
|
@@ -284,6 +310,7 @@ class Amarillo
|
|
284
310
|
|
285
311
|
end
|
286
312
|
|
313
|
+
# Renew all certificates
|
287
314
|
def renewCertificates
|
288
315
|
t = Time.now
|
289
316
|
@logger.info "Renewing certificates"
|
@@ -291,10 +318,7 @@ class Amarillo
|
|
291
318
|
Dir["#{@configsPath}/*.yml"].each do |c|
|
292
319
|
config = YAML.load(File.read(c))
|
293
320
|
|
294
|
-
cn
|
295
|
-
email = config["email"]
|
296
|
-
zone = config["zone"]
|
297
|
-
key_type = config["key_type"]
|
321
|
+
cn = config["commonName"]
|
298
322
|
|
299
323
|
certificatePath = "#{@certificatePath}/#{cn}.crt"
|
300
324
|
raw = File.read certificatePath
|
@@ -303,12 +327,39 @@ class Amarillo
|
|
303
327
|
|
304
328
|
if daysToExpiration < 30 then
|
305
329
|
@logger.info "#{cn} certificate needs to be renewed"
|
306
|
-
self.requestCertificate
|
330
|
+
self.requestCertificate configPath: c
|
307
331
|
else
|
308
332
|
@logger.info "#{cn} certificate does not need to be renewed"
|
309
333
|
end
|
310
334
|
end
|
311
335
|
end
|
336
|
+
|
337
|
+
# Renew specific certificate, implied force
|
338
|
+
def renewCertificate(commonName)
|
339
|
+
|
340
|
+
configPath = "#{@configsPath}/#{commonName}.yml"
|
341
|
+
self.requestCertificate configPath: configPath
|
342
|
+
|
343
|
+
end
|
344
|
+
|
345
|
+
def doRenewalAction(renewal_action)
|
346
|
+
|
347
|
+
if renewal_action
|
348
|
+
|
349
|
+
@logger.info "Executing certificate renewal action: #{renewal_action}"
|
350
|
+
|
351
|
+
begin
|
352
|
+
%x( #{renewal_action} )
|
353
|
+
@logger.info "Renewal action returned: #{$?}"
|
354
|
+
rescue
|
355
|
+
@logger.error("Error executing certificate renewal action")
|
356
|
+
raise
|
357
|
+
end
|
358
|
+
|
359
|
+
end
|
360
|
+
|
361
|
+
end
|
362
|
+
|
312
363
|
end
|
313
364
|
|
314
365
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amarillo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- iAchieved.it LLC
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: acme-client
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '3.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '3.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: aws-sdk-core
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
requirements: []
|
112
|
-
rubygems_version: 3.
|
112
|
+
rubygems_version: 3.2.33
|
113
113
|
signing_key:
|
114
114
|
specification_version: 4
|
115
115
|
summary: Amarillo
|