openvoxserver-ca 3.0.0.pre.rc1

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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +17 -0
  3. data/.github/release.yml +41 -0
  4. data/.github/workflows/gem_release.yaml +106 -0
  5. data/.github/workflows/prepare_release.yml +28 -0
  6. data/.github/workflows/release.yml +28 -0
  7. data/.github/workflows/unit_tests.yaml +45 -0
  8. data/.gitignore +14 -0
  9. data/.rspec +2 -0
  10. data/.travis.yml +16 -0
  11. data/CHANGELOG.md +15 -0
  12. data/CODEOWNERS +4 -0
  13. data/CODE_OF_CONDUCT.md +74 -0
  14. data/CONTRIBUTING.md +15 -0
  15. data/Gemfile +20 -0
  16. data/LICENSE +202 -0
  17. data/README.md +118 -0
  18. data/Rakefile +30 -0
  19. data/bin/console +14 -0
  20. data/bin/setup +8 -0
  21. data/exe/puppetserver-ca +10 -0
  22. data/lib/puppetserver/ca/action/clean.rb +109 -0
  23. data/lib/puppetserver/ca/action/delete.rb +286 -0
  24. data/lib/puppetserver/ca/action/enable.rb +140 -0
  25. data/lib/puppetserver/ca/action/generate.rb +330 -0
  26. data/lib/puppetserver/ca/action/import.rb +196 -0
  27. data/lib/puppetserver/ca/action/list.rb +253 -0
  28. data/lib/puppetserver/ca/action/migrate.rb +97 -0
  29. data/lib/puppetserver/ca/action/prune.rb +289 -0
  30. data/lib/puppetserver/ca/action/revoke.rb +108 -0
  31. data/lib/puppetserver/ca/action/setup.rb +188 -0
  32. data/lib/puppetserver/ca/action/sign.rb +146 -0
  33. data/lib/puppetserver/ca/certificate_authority.rb +418 -0
  34. data/lib/puppetserver/ca/cli.rb +145 -0
  35. data/lib/puppetserver/ca/config/puppet.rb +309 -0
  36. data/lib/puppetserver/ca/config/puppetserver.rb +84 -0
  37. data/lib/puppetserver/ca/errors.rb +40 -0
  38. data/lib/puppetserver/ca/host.rb +176 -0
  39. data/lib/puppetserver/ca/local_certificate_authority.rb +304 -0
  40. data/lib/puppetserver/ca/logger.rb +49 -0
  41. data/lib/puppetserver/ca/stub.rb +17 -0
  42. data/lib/puppetserver/ca/utils/cli_parsing.rb +67 -0
  43. data/lib/puppetserver/ca/utils/config.rb +61 -0
  44. data/lib/puppetserver/ca/utils/file_system.rb +109 -0
  45. data/lib/puppetserver/ca/utils/http_client.rb +232 -0
  46. data/lib/puppetserver/ca/utils/inventory.rb +84 -0
  47. data/lib/puppetserver/ca/utils/signing_digest.rb +27 -0
  48. data/lib/puppetserver/ca/version.rb +5 -0
  49. data/lib/puppetserver/ca/x509_loader.rb +170 -0
  50. data/lib/puppetserver/ca.rb +7 -0
  51. data/openvoxserver-ca.gemspec +31 -0
  52. data/tasks/spec.rake +15 -0
  53. data/tasks/vox.rake +19 -0
  54. metadata +154 -0
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # OpenVox Server's CA CLI Library
2
+
3
+ This gem provides the functionality behind the OpenVox Server CA interactions.
4
+ The actual CLI executable lives within the OpenVox Server project.
5
+ This is a community implementation of the `puppetserver-ca` gem.
6
+
7
+
8
+ ## Installation
9
+
10
+ You may install it yourself with:
11
+
12
+ $ gem install openvoxserver-ca
13
+
14
+
15
+ ## Usage
16
+
17
+ For initial CA setup, we provide two options. These need to be run before starting
18
+ Puppet Server for the first time.
19
+
20
+ To set up a default CA, with a self-signed root cert and an intermediate signing cert:
21
+ ```
22
+ puppetserver ca setup
23
+ ```
24
+
25
+ To import a custom CA:
26
+ ```
27
+ puppetserver ca import --cert-bundle certs.pem --crl-chain crls.pem --private-key ca_key.pem
28
+ ```
29
+
30
+ The remaining actions provided by this gem require a running OpenVox Server (Puppet Server), since
31
+ it primarily uses the CA's API endpoints to do its work. The following examples
32
+ assume that you are using the gem packaged within OpenVox Server.
33
+
34
+ To sign a pending certificate request:
35
+ ```
36
+ puppetserver ca sign --certname foo.example.com
37
+ ```
38
+
39
+ To list certificates and CSRs:
40
+ ```
41
+ puppetserver ca list --all
42
+ ```
43
+
44
+ To revoke a signed certificate:
45
+ ```
46
+ puppetserver ca revoke --certname foo.example.com
47
+ ```
48
+
49
+ To revoke the cert and clean up all SSL files for a given certname:
50
+ ```
51
+ puppetserver ca clean --certname foo.example.com
52
+ ```
53
+
54
+ To create a new keypair and certificate for a certname:
55
+ ```
56
+ puppetserver ca generate --certname foo.example.com
57
+ ```
58
+
59
+ To remove duplicated entries from Puppet's CRL:
60
+ ```
61
+ puppetserver ca prune
62
+ ```
63
+
64
+ To enable verbose mode:
65
+ ```
66
+ puppetserver ca --verbose <action>
67
+ ```
68
+
69
+ For more details, see the help output:
70
+ ```
71
+ puppetserver ca --help
72
+ ```
73
+
74
+ This code in this project is licensed under the Apache Software License v2,
75
+ please see the included [License](https://github.com/OpenVoxProject/openvoxserver-ca-cli/blob/main/LICENSE.md)
76
+ for more details.
77
+
78
+
79
+ ## Development
80
+
81
+ After checking out the repo, run `bin/setup` to install dependencies. Then,
82
+ run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an
83
+ interactive prompt that will allow you to experiment.
84
+
85
+ To install this gem onto your local machine, run `bundle exec rake install`.
86
+
87
+ ### Testing
88
+ To test your changes on a VM:
89
+ 1. Build the gem with your changes: `gem build openvoxserver-ca.gemspec`
90
+ 1. Copy the gem to your VM: `scp openvoxserver-ca-<version>.gem <your-vm>:.`
91
+ 1. Install openvox-server by installing the relevant release package and then installing the openvox-server package. For example:
92
+ ```
93
+ $ wget https://yum.voxpupuli.org/openvox8-release-el-9.noarch.rpm
94
+ $ rpm -i openvox8-release-el-9.noarch.rpm
95
+ $ yum update
96
+ $ yum install -y openvox-server
97
+ ```
98
+ 1. Restart your shell so that puppet's bin dir is on your $PATH: `exec bash`
99
+ 1. Install the gem into puppet's gem directory using puppet's gem command:
100
+ ```
101
+ $ /opt/puppetlabs/puppet/bin/gem install --install-dir "/opt/puppetlabs/puppet/lib/ruby/vendor_gems" openvoxserver-ca-<version>.gem
102
+ ```
103
+ 1. To confirm that installation was successful, run `puppetserver ca --help`
104
+
105
+ ## Contributing & Support
106
+
107
+ Bug reports and feature requests are welcome via GitHub issues.
108
+
109
+ For interactive questions feel free to post to #puppet or #puppet-dev on the Puppet Community Slack channel.
110
+
111
+ Contributions are welcome at https://github.com/OpenVoxProject/openvoxserver-ca-cli/pulls.
112
+ Contributors should both be sure to read the
113
+ [contributing document](https://github.com/OpenVoxProject/openvoxserver-ca-cli/blob/main/CONTRIBUTING.md)
114
+ and sign the [contributor license agreement](https://cla.puppet.com/).
115
+
116
+ Everyone interacting with the project’s codebase, issue tracker, etc is expected
117
+ to follow the
118
+ [code of conduct](https://github.com/OpenVoxProject/openvoxserver-ca-cli/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ Dir.glob(File.join('tasks/**/*.rake')).each { |file| load file }
4
+
5
+ task default: :spec
6
+
7
+ begin
8
+ require 'github_changelog_generator/task'
9
+ require_relative 'lib/puppetserver/ca/version'
10
+
11
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
12
+ config.header = <<~HEADER.chomp
13
+ # Changelog
14
+
15
+ All notable changes to this project will be documented in this file.
16
+ HEADER
17
+ config.user = 'openvoxproject'
18
+ config.project = 'openvoxserver-ca'
19
+ config.exclude_labels = %w[dependencies duplicate question invalid wontfix wont-fix modulesync skip-changelog]
20
+ config.future_release = Puppetserver::Ca::VERSION
21
+ config.since_tag = '2.7.0'
22
+ end
23
+ rescue LoadError
24
+ task :changelog do
25
+ abort("Run `bundle install --with release` to install the `github_changelog_generator` gem.")
26
+ end
27
+ end
28
+
29
+ desc 'Prepare for a release'
30
+ task 'release:prepare' => [:changelog]
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "puppetserver/ca"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This requires everything in our Gemfile, so when functionally testing
4
+ # our debugging tools are available without having to require them
5
+ require 'bundler/setup'
6
+ Bundler.require(:default)
7
+
8
+ require 'puppetserver/ca/cli'
9
+
10
+ exit Puppetserver::Ca::Cli.run(ARGV)
@@ -0,0 +1,109 @@
1
+ require 'optparse'
2
+
3
+ require 'puppetserver/ca/action/revoke'
4
+ require 'puppetserver/ca/certificate_authority'
5
+ require 'puppetserver/ca/config/puppet'
6
+ require 'puppetserver/ca/errors'
7
+ require 'puppetserver/ca/utils/cli_parsing'
8
+ require 'puppetserver/ca/utils/file_system'
9
+
10
+ module Puppetserver
11
+ module Ca
12
+ module Action
13
+ class Clean
14
+
15
+ include Puppetserver::Ca::Utils
16
+
17
+ CERTNAME_BLOCKLIST = %w{--all --config}
18
+
19
+ SUMMARY = 'Revoke cert(s) and remove related files from CA'
20
+ BANNER = <<-BANNER
21
+ Usage:
22
+ puppetserver ca clean [--help]
23
+ puppetserver ca clean [--config] --certname NAME[,NAME]
24
+
25
+ Description:
26
+ Given one or more valid certnames, instructs the CA to revoke certificates
27
+ matching the given certnames if they exist, and then remove files pertaining
28
+ to them (keys, cert, and certificate request) over HTTPS using the local
29
+ agent's PKI
30
+
31
+ Options:
32
+ BANNER
33
+
34
+ def self.parser(parsed = {})
35
+ parsed['certnames'] = []
36
+ OptionParser.new do |o|
37
+ o.banner = BANNER
38
+ o.on('--certname NAME[,NAME]', Array,
39
+ 'One or more comma separated certnames') do |certs|
40
+ parsed['certnames'] += certs
41
+ end
42
+ o.on('--config CONF', 'Custom path to puppet.conf') do |conf|
43
+ parsed['config'] = conf
44
+ end
45
+ o.on('--help', 'Display this command-specific help output') do |help|
46
+ parsed['help'] = true
47
+ end
48
+ end
49
+ end
50
+
51
+ def initialize(logger)
52
+ @logger = logger
53
+ end
54
+
55
+ def parse(args)
56
+ results = {}
57
+ parser = self.class.parser(results)
58
+
59
+ errors = CliParsing.parse_with_errors(parser, args)
60
+
61
+ results['certnames'].each do |certname|
62
+ if CERTNAME_BLOCKLIST.include?(certname)
63
+ errors << " Cannot manage cert named `#{certname}` from " +
64
+ "the CLI, if needed use the HTTP API directly"
65
+ end
66
+ end
67
+
68
+ if results['certnames'].empty?
69
+ errors << ' At least one certname is required to clean'
70
+ end
71
+
72
+ errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
73
+
74
+ exit_code = errors_were_handled ? 1 : nil
75
+
76
+ return results, exit_code
77
+ end
78
+
79
+ def run(args)
80
+ certnames = args['certnames']
81
+ config = args['config']
82
+
83
+ if config
84
+ errors = FileSystem.validate_file_paths(config)
85
+ return 1 if Errors.handle_with_usage(@logger, errors)
86
+ end
87
+
88
+ puppet = Config::Puppet.parse(config, @logger)
89
+ return 1 if Errors.handle_with_usage(@logger, puppet.errors)
90
+
91
+ result = clean_certs(certnames, puppet.settings)
92
+ case result
93
+ when :success
94
+ return 0
95
+ when :invalid
96
+ return 24
97
+ when :not_found, :error
98
+ return 1
99
+ end
100
+ end
101
+
102
+ def clean_certs(certnames, settings)
103
+ ca = Puppetserver::Ca::CertificateAuthority.new(@logger, settings)
104
+ ca.clean_certs(certnames)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,286 @@
1
+ require 'openssl'
2
+ require 'optparse'
3
+ require 'time'
4
+ require 'puppetserver/ca/certificate_authority'
5
+ require 'puppetserver/ca/config/puppet'
6
+ require 'puppetserver/ca/errors'
7
+ require 'puppetserver/ca/utils/cli_parsing'
8
+ require 'puppetserver/ca/utils/file_system'
9
+ require 'puppetserver/ca/utils/inventory'
10
+ require 'puppetserver/ca/x509_loader'
11
+
12
+ module Puppetserver
13
+ module Ca
14
+ module Action
15
+ class Delete
16
+ include Puppetserver::Ca::Utils
17
+
18
+ CERTNAME_BLOCKLIST = %w{--config --expired --revoked --all}
19
+
20
+ SUMMARY = 'Delete signed certificate(s) from disk'
21
+ BANNER = <<-BANNER
22
+ Usage:
23
+ puppetserver ca delete [--help]
24
+ puppetserver ca delete [--config CONF] [--expired] [--revoked]
25
+ [--certname NAME[,NAME]] [--all]
26
+
27
+ Description:
28
+ Deletes signed certificates from disk. Once a certificate is
29
+ signed and delivered to a node, it no longer necessarily needs
30
+ to be stored on disk.
31
+
32
+ Options:
33
+ BANNER
34
+
35
+ def initialize(logger)
36
+ @logger = logger
37
+ end
38
+
39
+ def self.parser(parsed = {})
40
+ OptionParser.new do |opts|
41
+ opts.banner = BANNER
42
+ opts.on('--help', 'Display this command-specific help output') do |help|
43
+ parsed['help'] = true
44
+ end
45
+ opts.on('--config CONF', 'Path to puppet.conf') do |conf|
46
+ parsed['config'] = conf
47
+ end
48
+ opts.on('--expired', 'Delete expired signed certificates') do |expired|
49
+ parsed['expired'] = true
50
+ end
51
+ opts.on('--revoked', 'Delete signed certificates that have already been revoked') do |revoked|
52
+ parsed['revoked'] = true
53
+ end
54
+ opts.on('--certname NAME[,NAME]', Array,
55
+ 'One or more comma-separated certnames for which to delete signed certificates') do |certs|
56
+ parsed['certname'] = [certs].flatten
57
+ end
58
+ opts.on('--all', 'Delete all signed certificates on disk') do |all|
59
+ parsed['all'] = true
60
+ end
61
+ end
62
+ end
63
+
64
+ def parse(args)
65
+ results = {}
66
+ parser = self.class.parser(results)
67
+
68
+ errors = CliParsing.parse_with_errors(parser, args)
69
+
70
+ if results['certname']
71
+ results['certname'].each do |certname|
72
+ if CERTNAME_BLOCKLIST.include?(certname)
73
+ errors << " Cannot manage cert named `#{certname}` from "+
74
+ "the CLI. If needed, use the HTTP API directly."
75
+ end
76
+ end
77
+ end
78
+
79
+ unless results['help'] || results['expired'] || results['revoked'] || results['certname'] || results['all']
80
+ errors << ' Must pass one of the valid flags to determine which certs to delete'
81
+ end
82
+
83
+ if results['all'] && (results['expired'] || results['revoked'] || results['certname'])
84
+ errors << ' The --all flag must not be used with --expired, --revoked, or --certname'
85
+ end
86
+
87
+ errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
88
+
89
+ exit_code = errors_were_handled ? 1 : nil
90
+ return results, exit_code
91
+ end
92
+
93
+ def run(args)
94
+ config = args['config']
95
+
96
+ # Validate the config path
97
+ if config
98
+ errors = FileSystem.validate_file_paths(config)
99
+ return 1 if Errors.handle_with_usage(@logger, errors)
100
+ end
101
+
102
+ # Validate puppet config setting
103
+ puppet = Config::Puppet.parse(config, @logger)
104
+ settings = puppet.settings
105
+ return 1 if Errors.handle_with_usage(@logger, puppet.errors)
106
+
107
+ # Validate that we are offline
108
+ return 1 if HttpClient.check_server_online(settings, @logger)
109
+
110
+ # Perform the desired action, keeping track if any errors occurred
111
+ errored = false
112
+ deleted_count = 0
113
+ cadir = settings[:cadir]
114
+ inventory_file_path = File.join(cadir, 'inventory.txt')
115
+
116
+ # Because --revoke has a potentially fatal error it can throw,
117
+ # process it first.
118
+ if args['revoked']
119
+ loader = X509Loader.new(settings[:cacert], settings[:cakey], settings[:cacrl])
120
+ verified_crls = loader.crls.select { |crl| crl.verify(loader.key) }
121
+ unless verified_crls.length == 1
122
+ @logger.err("Could not identify Puppet's CRL. Aborting delete action.")
123
+ return 1
124
+ end
125
+ crl = verified_crls.first
126
+
127
+ # First, search the inventory for the revoked serial.
128
+ # If it matches the current serial for the cert, delete the cert.
129
+ # If it is an old serial for the certname, verify the file on disk
130
+ # is not the serial that was revoked, and then ignore it. If the
131
+ # file on disk does match that serial, delete it.
132
+ # If it isn't in the inventory, fall back to searching every cert on
133
+ # disk for the given serial.
134
+ inventory, err = Inventory.parse_inventory_file(inventory_file_path, @logger)
135
+ revoked_serials = crl.revoked.map { |r| r.serial.to_i }
136
+ to_delete = []
137
+ revoked_serials.each do |revoked_serial|
138
+ current_serial = inventory.find { |k,v| v[:serial] == revoked_serial }
139
+ old_serial = inventory.find { |k,v| v[:old_serials].include?(revoked_serial) }
140
+ if current_serial
141
+ @logger.debug("#{revoked_serial} is the current serial for #{current_serial.first}")
142
+ to_delete << current_serial.first
143
+ elsif old_serial
144
+ @logger.debug("#{revoked_serial} appears to be an old serial for #{old_serial.first}. Verifying cert on disk is not the revoked serial.")
145
+ begin
146
+ serial = get_cert_serial("#{cadir}/signed/#{old_serial.first}.pem")
147
+ # This should never happen unless someone has messed with
148
+ # the inventory.txt file or replaced the cert on disk with
149
+ # an old one.
150
+ to_delete << old_serial.first if serial == revoked_serial
151
+ rescue Exception => e
152
+ @logger.err("Error reading serial from certificate for #{old_serial.first} with exception #{e}")
153
+ errored = true
154
+ end
155
+ else
156
+ @logger.debug("Could not find #{revoked_serial} in inventory.txt. Searching certs on disk for this serial.")
157
+ begin
158
+ certname = find_cert_with_serial(cadir, revoked_serial)
159
+ if certname
160
+ to_delete << certname
161
+ else
162
+ @logger.err("Could not find serial #{revoked_serial} in inventory.txt or in any certificate file currently on disk.")
163
+ errored = true
164
+ end
165
+ rescue Exception => e
166
+ @logger.err("Error reading serial from certificates when trying to find certificate with serial #{revoked_serial} with exception #{e}")
167
+ errored = true
168
+ end
169
+ end
170
+ end
171
+ # Because the CRL will likely contain certs that no longer exist on disk,
172
+ # don't show an error if we can't find the file.
173
+ count, err = delete_certs(cadir, to_delete, false)
174
+ errored ||= err
175
+ deleted_count += count
176
+ end
177
+
178
+ if args['expired']
179
+ # Delete expired certs found in inventory first since this is cheaper.
180
+ # Then, look for any certs not in the inventory, check if they
181
+ # are expired, then delete those.
182
+ inventory, err = Inventory.parse_inventory_file(inventory_file_path, @logger)
183
+ errored ||= err
184
+ expired_in_inventory = inventory.select { |k,v| v[:not_after] < Time.now }.map(&:first)
185
+ # Don't print errors if the cert is not found, since the inventory
186
+ # file can contain old entries that have already been deleted.
187
+ count, err = delete_certs(cadir, expired_in_inventory, false)
188
+ deleted_count += count
189
+ errored ||= err
190
+ other_certs_to_check = find_certs_not_in_inventory(cadir, inventory.map(&:first))
191
+ count, err = delete_expired_certs(cadir, other_certs_to_check)
192
+ deleted_count += count
193
+ errored ||= err
194
+ end
195
+
196
+ if args['certname']
197
+ count, errored = delete_certs(cadir, args['certname'])
198
+ deleted_count += count
199
+ end
200
+
201
+ if args['all']
202
+ certnames = Dir.glob("#{cadir}/signed/*.pem").map{ |c| File.basename(c, '.pem') }
203
+ # Since we don't run this with any other flags, we can set these variables directly
204
+ deleted_count, errored = delete_certs(cadir, certnames)
205
+ end
206
+
207
+ plural = deleted_count == 1 ? "" : "s"
208
+ @logger.inform("#{deleted_count} certificate#{plural} deleted.")
209
+ # If encountered non-fatal errors (an invalid entry in inventory.txt, cert not existing on disk)
210
+ # return 24. Returning 1 should be for fatal errors where we could not do any part of the action.
211
+ return errored ? 24 : 0
212
+ end
213
+
214
+ def find_certs_not_in_inventory(cadir, inventory_certnames)
215
+ all_cert_files = Dir.glob("#{cadir}/signed/*.pem").map { |f| File.basename(f, '.pem') }
216
+ all_cert_files - inventory_certnames
217
+ end
218
+
219
+ def delete_certs(cadir, certnames, error_on_not_found = true)
220
+ deleted = 0
221
+ errored = false
222
+ certnames.each do |cert|
223
+ path = "#{cadir}/signed/#{cert}.pem"
224
+ if File.exist?(path)
225
+ @logger.inform("Deleting certificate at #{path}")
226
+ File.delete(path)
227
+ deleted += 1
228
+ else
229
+ if error_on_not_found
230
+ @logger.err("Could not find certificate file at #{path}")
231
+ errored = true
232
+ end
233
+ end
234
+ end
235
+ [deleted, errored]
236
+ end
237
+
238
+ def delete_expired_certs(cadir, certnames)
239
+ deleted = 0
240
+ errored = false
241
+ files = certnames.map { |c| "#{cadir}/signed/#{c}.pem" }
242
+ files.each do |f|
243
+ # Shouldn't really be possible since we look for certs on disk
244
+ # before calling this function, but just in case.
245
+ unless File.exist?(f)
246
+ @logger.err("Could not find certificate file at #{f}")
247
+ errored = true
248
+ next
249
+ end
250
+ begin
251
+ cert = OpenSSL::X509::Certificate.new(File.read(f))
252
+ rescue OpenSSL::X509::CertificateError
253
+ @logger.err("Error reading certificate at #{f}")
254
+ errored = true
255
+ next
256
+ end
257
+ if cert.not_after < Time.now
258
+ @logger.inform("Deleting certificate at #{f}")
259
+ File.delete(f)
260
+ deleted += 1
261
+ end
262
+ end
263
+ [deleted, errored]
264
+ end
265
+
266
+ def get_cert_serial(file)
267
+ cert = OpenSSL::X509::Certificate.new(File.read(file))
268
+ cert.serial.to_i
269
+ end
270
+
271
+ def find_cert_with_serial(cadir, serial)
272
+ files = Dir.glob("#{cadir}/signed/*.pem")
273
+ files.each do |f|
274
+ begin
275
+ s = get_cert_serial(f)
276
+ return File.basename(f, '.pem') if s == serial # Remove .pem
277
+ rescue Exception => e
278
+ @logger.debug("Error reading certificate at #{f} with exception #{e}. Skipping this file.")
279
+ end
280
+ end
281
+ return nil
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end