amarillo 0.1.1 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a94e70709b0f4147b7c04711d1e1f58a54eeea7a743364ee4e8dc2d25984b83a
4
- data.tar.gz: 1f179ac5eea2d4919e22d09a44b491cbed3e2704128d9da71a9c00a10c177275
3
+ metadata.gz: f940486286d7bbf620a598ef9b5c0c4308ed97689f79fa9fdb8eaa74c51410d4
4
+ data.tar.gz: 4e472f27059977bbe7de8a7c2e1b6856223da2defe903c14b85d4ebab6749448
5
5
  SHA512:
6
- metadata.gz: 3acd69905f4138ab59a2de3a1cb8493a6e1ea9363ccaaa622c8d565112850e78ce11ea6fad940052c7a4795a3e19980bb2f24f392775ac4aeab463f19ef6d013
7
- data.tar.gz: 0025b7ca2705e934750ca1bf267644ddc9fd610072ab64a2d5fc7c762b1c16d200b6ea1c6f74b87723c2a75fe6251982478ee72cfc9bc7c5b11581211b2d1a9d
6
+ metadata.gz: 35b3b43b6fb573358633d85c89c3cf6e1d224eb76ceef671afebad54ce7c9c1c34780864ed3c4f92e031f374192a319e42e6c69f6776510e354b5af4ccd40818
7
+ data.tar.gz: 79cbf760076b5175cef127a00a8c29c3c21dbe815d72a18b424150960d5ec1c5108eca8b99c4098adcd6121981173ffe9056b5fe727370ccd770f26b6d7539fb
data/bin/amarillo CHANGED
@@ -23,9 +23,26 @@
23
23
  require 'optparse'
24
24
  require 'fileutils'
25
25
  require 'amarillo'
26
+ require 'amarillo/environment'
26
27
 
27
28
  options = {}
28
29
  OptionParser.new do |opts|
30
+ opts.on("-i", "--initialize", "Initialize amarillo defaults") do |i|
31
+ options[:initialize] = i
32
+ end
33
+
34
+ opts.on("-l", "--list", "List certificates") do |l|
35
+ options[:list] = l
36
+ end
37
+
38
+ opts.on("-d", "--delete", "Delete certificate") do |d|
39
+ options[:delete] = d
40
+ end
41
+
42
+ opts.on("-r", "--renew", "Renew certificates") do |r|
43
+ options[:renew] = r
44
+ end
45
+
29
46
  opts.on("-z", "--zone ZONE", "Hosted zone") do |z|
30
47
  options[:zone] = z
31
48
  end
@@ -38,8 +55,8 @@ OptionParser.new do |opts|
38
55
  options[:name] = n
39
56
  end
40
57
 
41
- opts.on("-o", "--output-directory OUTPUT_PATH", "Output directory of certificates and keys") do |o|
42
- options[:certificate_path] = o
58
+ opts.on("-a", "--amarillo-home AMARILLO_HOME", "Home directory for configuration, keys, and certificates") do |o|
59
+ options[:amarillo_home] = a
43
60
  end
44
61
 
45
62
  opts.on("-h", "--help") do |h|
@@ -55,57 +72,60 @@ Usage: amarillo --zone ZONE --name COMMONNAME --email EMAIL [--output-path OUTP
55
72
  exit 0
56
73
  end
57
74
 
58
- if options[:zone].nil? or
59
- options[:email].nil? or
60
- options[:name].nil? then
75
+ if options[:initialize] then
76
+ e = Amarillo::Environment.new
61
77
 
62
- puts "Usage: amarillo --zone ZONE --name COMMONNAME --email EMAIL [--output-directory OUTPUT_PATH]"
78
+ e.init options[:zone], options[:email]
63
79
 
64
- exit -1
80
+ exit 0
65
81
  end
66
82
 
67
- if options[:output_path].nil?
68
- certificate_path = "/etc/ssl/amarillo"
69
- key_path = "/etc/ssl/amarillo/private"
83
+ e = Amarillo::Environment.new
84
+ e.load_config
85
+
86
+ if options[:zone].nil?
87
+ zone = e.config["defaults"]["zone"]
88
+ if zone.nil? or zone == '' then
89
+ puts "Error: Specify a default zone in config.yml or use --zone"
90
+ exit -1
91
+ end
70
92
  else
71
- certificate_path = options[:output_path]
72
- key_path = "#{certificate_path}/private"
93
+ zone = options[:zone]
73
94
  end
74
95
 
75
- # Try to create the certificate_path and key_path
76
- keypath_dirname = File.dirname(key_path)
77
- unless File.directory?(keypath_dirname)
78
- begin
79
- FileUtils.mkdir_p(keypath_dirname)
80
- rescue
81
- writableMessage = <<-"HEREDOC"
82
- Error: #{certificate_path} and #{key_path} are not writable directories to store keys and certificates.
83
- HEREDOC
84
- print writableMessage
85
- exit -1
86
- end
96
+ if options[:email].nil?
97
+ email = e.config["defaults"]["email"]
98
+ if email.nil? or email == '' then
99
+ puts "Error: Specify a default e-mail address in config.yml or use --email"
100
+ exit -1
101
+ end
102
+ else
103
+ email = options[:email]
87
104
  end
88
105
 
89
- # Check for existense of aws.env
90
- awsEnvPath = Pathname.new("/etc/amarillo/aws.env")
91
- if not awsEnvPath.exist? then
92
- awsEnvMessage = <<-"HEREDOC"
93
- Error: /etc/amarillo/aws.env AWS credentials file not found.
94
-
95
- /etc/amarillo/aws.env must exist and set both aws_access_key_id and aws_secret_access_key for an IAM user with AmazonRoute53FullAccess permissions.
96
-
97
- Example:
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]"
108
+ exit -1
109
+ else
110
+ name = options[:name]
111
+ end
98
112
 
99
- [default]
100
- aws_access_key_id = your_access_key_id
101
- aws_secret_access_key = your_secret_access_key
102
- HEREDOC
113
+ if options[:amarillo_home].nil?
114
+ amarillo_home = "/usr/local/etc/amarillo"
115
+ else
116
+ amarillo_home = options[:amarillo_home]
117
+ end
103
118
 
104
- print awsEnvMessage
119
+ y = Amarillo.new amarillo_home
105
120
 
106
- exit -1
121
+ if options[:renew] then
122
+ y.renewCertificates
123
+ elsif options[:list] then
124
+ y.listCertificates
125
+ elsif options[:delete] then
126
+ y.deleteCertificate name
127
+ else
128
+ y.requestCertificate zone, name, email, nil
107
129
  end
108
130
 
109
131
 
110
- y = Amarillo.new(certificate_path, key_path, awsEnvPath)
111
- y.requestCertificate(options[:zone], options[:name], options[:email])
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Inspired by Pete Keen's (pete@petekeen.net) post
4
+ # https://www.petekeen.net/lets-encrypt-without-certbot
5
+ #
6
+ # Copyright 2021 iAchieved.it LLC
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ require 'logger'
27
+ require 'yaml'
28
+ require 'aws-sdk-core'
29
+
30
+ DefaultAmarilloHome = "/usr/local/etc/amarillo/"
31
+
32
+ class Amarillo::Environment
33
+
34
+ attr_reader :certificatePath, :keyPath, :configPath, :configsPath, :config, :awsEnvFile
35
+
36
+ def initialize(amarilloHome: DefaultAmarilloHome)
37
+
38
+ @logger = Logger.new(STDOUT)
39
+ @logger.level = Logger::INFO
40
+
41
+ @amarilloHome = amarilloHome
42
+ @certificatePath = amarilloHome + "/certificates"
43
+ @keyPath = amarilloHome + "/keys"
44
+ @configPath = amarilloHome
45
+ @configsPath = amarilloHome + "/configs"
46
+ @configFile = amarilloHome + "/config.yml"
47
+ @awsEnvFile = amarilloHome + "/aws.env"
48
+
49
+ end
50
+
51
+ # Public method to create default configuration files
52
+ def init(zone = nil, email = nil)
53
+
54
+ unless File.exist?(@configsPath) and File.directory?(@configsPath)
55
+ begin
56
+ @logger.info "Creating #{@configsPath} directory"
57
+ FileUtils.mkpath(@configsPath)
58
+ rescue
59
+ @logger.error("Cannot create #{@configsPath} directory")
60
+ return false
61
+ end
62
+ end
63
+
64
+ unless File.exist?(@certificatePath) and File.directory?(@certificatePath)
65
+ begin
66
+ @logger.info "Creating #{@certificatePath} directory"
67
+ FileUtils.mkpath(@certificatePath)
68
+ rescue
69
+ @logger.error("Cannot create #{@certificatePath} directory")
70
+ return false
71
+ end
72
+ end
73
+
74
+ unless File.exist?(@keyPath) and File.directory?(@keyPath)
75
+ begin
76
+ @logger.info "Creating #{@keyPath} directory"
77
+ FileUtils.mkpath(@keyPath)
78
+ rescue
79
+ @logger.error("Cannot create #{@keyPath} directory")
80
+ return false
81
+ end
82
+ end
83
+
84
+ # Create aws.env
85
+ unless File.exist?(@awsEnvFile) then
86
+ awsEnv = <<-HEREDOC
87
+ [default]
88
+ aws_access_key_id =
89
+ aws_secret_access_key =
90
+ HEREDOC
91
+ @logger.info("Creating blank #{@awsEnvFile}")
92
+ @logger.warn("NOTE: aws_access_key_id and aws_secret_access_key must be specified in this file.")
93
+ File.write(@awsEnvFile, awsEnv)
94
+ else
95
+ @logger.info("Refusing to overwrite #{@awsEnvFile}")
96
+ end
97
+
98
+ # Create config.yml
99
+ unless File.exist?(@configFile) then
100
+ @logger.info("Creating default configuration #{@configFile}")
101
+ config = {
102
+ "defaults" => {
103
+ "region" => 'us-east-2',
104
+ "profile" => 'default',
105
+ "email" => email,
106
+ "zone" => zone,
107
+ "nameservers" => ['208.67.222.222', '9.9.9.9'],
108
+ "key_type" => 'ec,secp384r1'
109
+ }}
110
+ File.write(@configFile, config.to_yaml)
111
+ else
112
+ @logger.info("Refusing to overwrite #{@configFile}")
113
+ end
114
+
115
+ # Create RSA private key for Let's Encrypt account
116
+ privateKeyPath = "#{@keyPath}/letsencrypt.key"
117
+
118
+ unless File.exist? privateKeyPath then
119
+ @logger.info "Generating 4096-bit RSA private key for Let's Encrypt account"
120
+
121
+ privateKey = OpenSSL::PKey::RSA.new(4096)
122
+
123
+
124
+ File.open(privateKeyPath, "w") do |f|
125
+ f.puts privateKey.to_pem.to_s
126
+ end
127
+ File.chmod(0400, privateKeyPath)
128
+ end
129
+ end
130
+
131
+ #
132
+ # Verify paths exist and are writable
133
+ # Verify aws.env exists and is formatted correctly
134
+ # Verify config.yml exists and is formatted correctly
135
+ #
136
+ def verify
137
+ @logger.info "Verifying amarillo environment"
138
+ if not verify_env() then return false end
139
+ if not verify_awsenv() then return false end
140
+ if not verify_config() then return false end
141
+ return true
142
+ end
143
+
144
+ def verify_env
145
+ unless File.stat(@certificatePath).writable? then
146
+ @logger.error(@certificatePath + " is not writable")
147
+ return false
148
+ end
149
+
150
+ unless File.stat(@keyPath).writable? then
151
+ @logger.error(@keyPath + " is not writable")
152
+ return false
153
+ end
154
+
155
+ return true
156
+ end
157
+
158
+ def verify_awsenv()
159
+ awsEnvFile = Pathname.new(@awsEnvFile)
160
+ if not awsEnvFile.exist? then
161
+ @logger.error("#{awsEnvFile} does not exist")
162
+ return false
163
+ end
164
+
165
+ awsCredentials = Aws::SharedCredentials.new(path: "#{@awsEnvFile}")
166
+
167
+ if awsCredentials.credentials.access_key_id.length != 20 then
168
+ @logger.error("#{@awsEnvFile} aws_access_key_id does not appear to be valid")
169
+ return false
170
+ end
171
+
172
+ if awsCredentials.credentials.secret_access_key.length != 40 then
173
+ @logger.error("#{@awsEnvFile} aws_secret_access_key does not appear to be valid")
174
+ return false
175
+ end
176
+
177
+ return true
178
+ end
179
+
180
+ def verify_config()
181
+ if not File.exist?(@configFile) then
182
+ @logger.error("#{@configFile} does not exist")
183
+ return false
184
+ end
185
+
186
+ begin
187
+ YAML.load(File.read(@configFile))
188
+ rescue
189
+ @logger.error("Unable to load configuration file")
190
+ return false
191
+ end
192
+
193
+ return true
194
+ end
195
+
196
+ def load_config()
197
+ if verify_config() then
198
+ @config = YAML.load(File.read(@configFile))
199
+ end
200
+ end
201
+
202
+ def get_zone_nameservers
203
+
204
+ self.load_config
205
+
206
+ nameservers = @config["defaults"]["nameservers"]
207
+ zone = @config["defaults"]["zone"]
208
+
209
+ @logger.info "Looking up nameservers for #{zone}"
210
+
211
+ zone_nameservers = []
212
+ Resolv::DNS.open(nameserver: nameservers) do |dns|
213
+ while zone_nameservers.length == 0
214
+ zone_nameservers = dns.getresources(
215
+ zone,
216
+ Resolv::DNS::Resource::IN::NS
217
+ ).map(&:name).map(&:to_s)
218
+ end
219
+ end
220
+
221
+ @logger.info "Found #{zone_nameservers.length} nameservers for zone #{zone}: #{zone_nameservers}"
222
+
223
+ return zone_nameservers
224
+ end
225
+
226
+ end
data/lib/amarillo.rb CHANGED
@@ -29,14 +29,24 @@ require 'openssl' # Key Generation
29
29
  require 'aws-sdk-core' # Credentials
30
30
  require 'aws-sdk-route53' # Route 53
31
31
  require 'resolv' # DNS Resolvers
32
+ require 'yaml' # YAML
33
+ require 'terminal-table' # Tablular output
32
34
 
33
35
  class Amarillo
34
36
 
35
- def initialize(certificatePath, keyPath, awsEnvPath)
37
+ def initialize(amarilloHome)
36
38
 
37
- @certificatePath = certificatePath
38
- @keyPath = keyPath
39
- @awsEnvPath = awsEnvPath
39
+ @environment = Amarillo::Environment.new(amarilloHome: amarilloHome)
40
+
41
+ if not @environment.verify then raise "Cannot initialize amarillo" end
42
+
43
+ @environment.load_config
44
+
45
+ @certificatePath = @environment.certificatePath
46
+ @keyPath = @environment.keyPath
47
+ @config = @environment.config
48
+ @awsEnvFile = @environment.awsEnvFile
49
+ @configsPath = @environment.configsPath
40
50
 
41
51
  @logger = Logger.new(STDOUT)
42
52
  @logger.level = Logger::INFO
@@ -65,19 +75,35 @@ class Amarillo
65
75
  valid
66
76
  end
67
77
 
68
- def requestCertificate(zone, commonName, email)
78
+ def get_route53
79
+ shared_creds = Aws::SharedCredentials.new(path: "#{@awsEnvFile}")
80
+
81
+ Aws.config.update(credentials: shared_creds)
82
+
83
+ region = @config["defaults"]["region"] ? @config["defaults"]["region"] : 'us-east-2'
84
+ @route53 = Aws::Route53::Client.new(region: region)
85
+ @hzone = @route53.list_hosted_zones(max_items: 100).hosted_zones.detect { |z| z.name == "#{@zone}." }
86
+
87
+ end
88
+
89
+ def requestCertificate(zone, commonName, email, key_type)
90
+
91
+ @zone = zone
92
+
93
+ acmeUrl = @config["defaults"]["acme_url"] ? @config["defaults"]["acme_url"] : 'https://acme-v02.api.letsencrypt.org/directory'
94
+
95
+ # Load private key
69
96
 
70
- @logger.info "Generating 4096-bit RSA private key"
71
- key = OpenSSL::PKey::RSA.new(4096)
72
- client = Acme::Client.new(
73
- private_key: key,
74
- directory: 'https://acme-v02.api.letsencrypt.org/directory'
75
- )
97
+ @logger.info "Loading 4096-bit RSA private key for Let's Encrypt account"
98
+ @logger.info "Let's Encrypt directory set to #{acmeUrl}"
76
99
 
77
- account = client.new_account(
78
- contact: "mailto:#{email}",
79
- terms_of_service_agreed: true
80
- )
100
+ key = OpenSSL::PKey::RSA.new File.read "#{@keyPath}/letsencrypt.key"
101
+
102
+ client = Acme::Client.new private_key: key,
103
+ directory: acmeUrl
104
+
105
+ account = client.new_account contact: "mailto:#{email}",
106
+ terms_of_service_agreed: true
81
107
 
82
108
  # Generate a certificate order
83
109
  @logger.info "Creating certificate order request for #{commonName}"
@@ -90,16 +116,7 @@ class Amarillo
90
116
 
91
117
  @logger.info "Challenge value for #{commonName} in #{zone} zone is #{challengeValue}"
92
118
 
93
- # Update Route 53
94
-
95
- shared_creds = Aws::SharedCredentials.new(path: "#{@awsEnvPath}")
96
- Aws.config.update(credentials: shared_creds)
97
-
98
- # TODO: Allow the user to set the region
99
- route53 = Aws::Route53::Client.new(region: 'us-east-2')
100
- hzone = route53.list_hosted_zones(max_items: 100)
101
- .hosted_zones
102
- .detect { |z| z.name == "#{zone}." }
119
+ self.get_route53
103
120
 
104
121
  change = {
105
122
  action: 'UPSERT',
@@ -114,30 +131,20 @@ class Amarillo
114
131
  }
115
132
 
116
133
  options = {
117
- hosted_zone_id: hzone.id,
134
+ hosted_zone_id: @hzone.id,
118
135
  change_batch: {
119
136
  changes: [change]
120
137
  }
121
138
  }
122
139
 
123
- route53.change_resource_record_sets(options)
124
-
125
- nameservers = []
140
+ @route53.change_resource_record_sets(options)
126
141
 
127
- @logger.info "Looking up nameservers for #{zone}"
128
-
129
- Resolv::DNS.open(nameserver: '9.9.9.9') do |dns|
130
- while nameservers.length == 0
131
- nameservers = dns.getresources(
132
- zone,
133
- Resolv::DNS::Resource::IN::NS
134
- ).map(&:name).map(&:to_s)
135
- end
136
- end
142
+ nameservers = @environment.get_zone_nameservers
137
143
 
138
- @logger.info "Waiting for DNS record to propogate"
144
+ @logger.info "Waiting for DNS record to propagate"
139
145
  while !check_dns(commonName, nameservers, challengeValue)
140
- sleep 1
146
+ sleep 2
147
+ @logger.info "Still waiting..."
141
148
  end
142
149
 
143
150
  authorization.dns.request_validation
@@ -150,17 +157,45 @@ class Amarillo
150
157
  authorization.dns.reload
151
158
  end
152
159
 
153
- @logger.info "Requesting certificate..."
160
+ @logger.info "Generating key"
161
+
162
+ # Create certificate yml
163
+ certConfig = {
164
+ "commonName" => commonName,
165
+ "email" => email,
166
+ "zone" => zone
167
+ }
154
168
 
155
- cert_key = OpenSSL::PKey::RSA.new(4096)
156
- csr = Acme::Client::CertificateRequest.new(
157
- private_key: cert_key,
158
- names: [commonName]
159
- )
169
+ if key_type
170
+ certConfig["key_type"] = key_type
171
+ else
172
+ key_type = @config["defaults"]["key_type"]
173
+ certConfig["key_type"] = key_type
174
+ end
160
175
 
161
- order.finalize(csr: csr)
176
+ type, args = key_type.split(',')
162
177
 
163
- sleep(1) while order.status == 'processing'
178
+ if type == 'ec' then
179
+ certPrivateKey = OpenSSL::PKey::EC.new(args).generate_key
180
+ elsif type == 'rsa' then
181
+ certPrivateKey = OpenSSL::PKey::RSA.new(args)
182
+ end
183
+
184
+ @logger.info "Requesting certificate..."
185
+ csr = Acme::Client::CertificateRequest.new private_key: certPrivateKey,
186
+ names: [commonName]
187
+
188
+ begin
189
+ order.finalize(csr: csr)
190
+ rescue
191
+ @logger.error("ERROR")
192
+ self.cleanup label, record_type, challengeValue
193
+ end
194
+
195
+ while order.status == 'processing'
196
+ sleep(1)
197
+ order.reload
198
+ end
164
199
 
165
200
  keyOutputPath = "#{@keyPath}/#{commonName}.key"
166
201
  certOutputPath = "#{@certificatePath}/#{commonName}.crt"
@@ -168,8 +203,9 @@ class Amarillo
168
203
  @logger.info "Saving private key to #{keyOutputPath}"
169
204
 
170
205
  File.open(keyOutputPath, "w") do |f|
171
- f.puts cert_key.to_pem.to_s
206
+ f.puts certPrivateKey.to_pem.to_s
172
207
  end
208
+ File.chmod(0600, keyOutputPath)
173
209
 
174
210
  @logger.info "Saving certificate to #{certOutputPath}"
175
211
 
@@ -177,6 +213,14 @@ class Amarillo
177
213
  f.puts order.certificate
178
214
  end
179
215
 
216
+ certConfigFile = "#{@configsPath}/#{commonName}.yml"
217
+ File.write(certConfigFile, certConfig.to_yaml)
218
+
219
+ self.cleanup label, record_type, challengeValue
220
+
221
+ end
222
+
223
+ def cleanup(label, record_type, challengeValue)
180
224
  @logger.info "Cleaning up..."
181
225
 
182
226
  change = {
@@ -192,12 +236,78 @@ class Amarillo
192
236
  }
193
237
 
194
238
  options = {
195
- hosted_zone_id: hzone.id,
239
+ hosted_zone_id: @hzone.id,
196
240
  change_batch: {
197
241
  changes: [change]
198
242
  }
199
243
  }
200
244
 
201
- route53.change_resource_record_sets(options)
245
+ @route53.change_resource_record_sets(options)
246
+ end
247
+
248
+ def renewCertificate(zone, commonName, email)
249
+
250
+ end
251
+
252
+ def listCertificates
253
+
254
+ rows = []
255
+
256
+ Dir["#{@configsPath}/*.yml"].each do |c|
257
+ config = YAML.load(File.read(c))
258
+
259
+ cn = config["commonName"]
260
+
261
+ certificatePath = "#{@certificatePath}/#{cn}.crt"
262
+ raw = File.read certificatePath
263
+ certificate = OpenSSL::X509::Certificate.new raw
264
+
265
+ rows << [config["commonName"], config["email"],
266
+ config["zone"], config["key_type"], certificate.not_after]
267
+
268
+ end
269
+
270
+ t = Terminal::Table.new :headings => ['commonName','email','zone','keytype','expiration'], :rows => rows
271
+ puts t
272
+ end
273
+
274
+ def deleteCertificate(commonName)
275
+ @logger.info "Deleting certificate #{commonName}"
276
+
277
+ certConfigFile = @configsPath + "/#{commonName}.yml"
278
+ certificatePath = @certificatePath + "/#{commonName}.crt"
279
+ keyPath = @keyPath + "/#{commonName}.key"
280
+
281
+ `rm -f #{certConfigFile} #{certificatePath} #{keyPath}`
282
+
283
+ end
284
+
285
+ def renewCertificates
286
+ t = Time.now
287
+ @logger.info "Renewing certificates"
288
+
289
+ Dir["#{@configsPath}/*.yml"].each do |c|
290
+ config = YAML.load(File.read(c))
291
+
292
+ cn = config["commonName"]
293
+ email = config["email"]
294
+ zone = config["zone"]
295
+ key_type = config["key_type"]
296
+
297
+ certificatePath = "#{@certificatePath}/#{cn}.crt"
298
+ raw = File.read certificatePath
299
+ certificate = OpenSSL::X509::Certificate.new raw
300
+ daysToExpiration = (certificate.not_after - t).to_i / (24 * 60 * 60)
301
+
302
+ if daysToExpiration < 30 then
303
+ @logger.info "#{cn} certificate needs to be renewed"
304
+ self.requestCertificate zone, cn, email, key_type
305
+ else
306
+ @logger.info "#{cn} certificate does not need to be renewed"
307
+ end
308
+ end
202
309
  end
203
310
  end
311
+
312
+
313
+ require 'amarillo/environment'
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.1.1
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - iAchieved.it LLC
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-28 00:00:00.000000000 Z
11
+ date: 2022-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acme-client
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.48'
69
+ - !ruby/object:Gem::Dependency
70
+ name: terminal-table
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
69
83
  description: A tool for managing Let's Encrypt dns-01 certificates
70
84
  email: joe@iachieved.it
71
85
  executables:
@@ -75,11 +89,12 @@ extra_rdoc_files: []
75
89
  files:
76
90
  - bin/amarillo
77
91
  - lib/amarillo.rb
92
+ - lib/amarillo/environment.rb
78
93
  homepage: https://github.com/iachievedit/amarillo
79
94
  licenses:
80
95
  - MIT
81
96
  metadata: {}
82
- post_install_message:
97
+ post_install_message:
83
98
  rdoc_options: []
84
99
  require_paths:
85
100
  - lib
@@ -94,8 +109,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
109
  - !ruby/object:Gem::Version
95
110
  version: '0'
96
111
  requirements: []
97
- rubygems_version: 3.0.3
98
- signing_key:
112
+ rubygems_version: 3.3.3
113
+ signing_key:
99
114
  specification_version: 4
100
115
  summary: Amarillo
101
116
  test_files: []