puppetserver-ca 2.2.0 → 2.3.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92fe6ca44899e2b5aeec75304212ff99b5e4530cbed2e0b451ad2dd77e2bf09a
4
- data.tar.gz: c46bc437a85f0a52fde2d3b2b51bda909ad179c1997056810b397d4b27ae4867
3
+ metadata.gz: 145cb62d733040dca7ad410a86146e0a0c82c29746a2c7de392004ad98737ed5
4
+ data.tar.gz: bd0db987a4af957208395f53134c3d59ab45f3c76d2e5d4205b589b75b1b9873
5
5
  SHA512:
6
- metadata.gz: 5c7ff17130d558382fa05f5f5bcb537ce9f2bc63a13078d1014783f636fcf0b0c02e604a0d4bf5694a486f67a56dec653430cea0c31eb911b7c15c9ea4e7f59d
7
- data.tar.gz: 3ad80f74f02aac6a0dab1601cf54aad14756e0b1c0e2e0a30075d1eeafedff03b4546bc7c0cb7bc6dc6197b71abdb9f74816cd4596050b39e6c0f47ca47baf45
6
+ metadata.gz: d5ec57e3cfd1d2947521b50a2923892452f25c4ae43c0daa45e9a0ba17ebd969431933f17d71866ff8e9e15bcdeb633c754084c618ea8bd2b1891ae0438751a5
7
+ data.tar.gz: ebfc68d10303eea0d66b61a636b069986e6f4d65e1af4a222bfea5d74a45951edb2d6f01192f4d0bafdbe3392e80a356074c334212651d2489e5d2dae0e8120b
data/README.md CHANGED
@@ -55,6 +55,11 @@ To create a new keypair and certificate for a certname:
55
55
  puppetserver ca generate --certname foo.example.com
56
56
  ```
57
57
 
58
+ To remove duplicated entries from Puppet's CRL:
59
+ ```
60
+ puppetserver ca prune
61
+ ```
62
+
58
63
  To enable verbose mode:
59
64
  ```
60
65
  puppetserver ca --verbose <action>
@@ -62,6 +62,7 @@ Options:
62
62
  certnames = input['certname'] || []
63
63
  all = input['all']
64
64
  output_format = input['format'] || "text"
65
+ missing = []
65
66
 
66
67
  unless VALID_FORMAT.include?(output_format)
67
68
  Errors.handle_with_usage(@logger, ["Unknown format flag '#{output_format}'. Valid formats are '#{VALID_FORMAT.join("', '")}'."])
@@ -87,14 +88,14 @@ Options:
87
88
  filter_names = lambda { |x| true }
88
89
  end
89
90
 
90
- all_certs = get_all_certs(puppet.settings).select { |cert| filter_names.call(cert) }
91
- requested, signed, revoked = separate_certs(all_certs)
92
- missing = certnames - all_certs.map { |cert| cert['name'] }
93
-
94
91
  if (all || certnames.any?)
92
+ all_certs = get_certs_or_csrs(puppet.settings).select { |cert| filter_names.call(cert) }
93
+ requested, signed, revoked = separate_certs(all_certs)
94
+ missing = certnames - all_certs.map { |cert| cert['name'] }
95
95
  output_certs_by_state(all, output_format, requested, signed, revoked, missing)
96
96
  else
97
- output_certs_by_state(all, output_format, requested)
97
+ all_csrs = get_certs_or_csrs(puppet.settings, "requested")
98
+ output_certs_by_state(all, output_format, all_csrs)
98
99
  end
99
100
 
100
101
  return missing.any? ? 1 : 0
@@ -209,8 +210,9 @@ Options:
209
210
  return requested, signed, revoked
210
211
  end
211
212
 
212
- def get_all_certs(settings)
213
- result = Puppetserver::Ca::CertificateAuthority.new(@logger, settings).get_certificate_statuses
213
+ def get_certs_or_csrs(settings, queried_state = nil)
214
+ query = queried_state ? { :state => queried_state } : {}
215
+ result = Puppetserver::Ca::CertificateAuthority.new(@logger, settings).get_certificate_statuses(query)
214
216
 
215
217
  if result
216
218
  return JSON.parse(result.body)
@@ -0,0 +1,136 @@
1
+ require 'optparse'
2
+ require 'openssl'
3
+ require 'puppetserver/ca/errors'
4
+ require 'puppetserver/ca/utils/cli_parsing'
5
+ require 'puppetserver/ca/utils/file_system'
6
+ require 'puppetserver/ca/utils/config'
7
+ require 'puppetserver/ca/x509_loader'
8
+
9
+ module Puppetserver
10
+ module Ca
11
+ module Action
12
+ class Prune
13
+ include Puppetserver::Ca::Utils
14
+
15
+ SUMMARY = "Prune the local CRL on disk to remove any duplicated certificates"
16
+ BANNER = <<-BANNER
17
+ Usage:
18
+ puppetserver ca prune [--help]
19
+ puppetserver ca prune [--config]
20
+
21
+ Description:
22
+ Prune the list of revoked certificates of any duplication within it. This command
23
+ will only prune the CRL issued by Puppet's CA cert.
24
+
25
+ Options:
26
+ BANNER
27
+
28
+ def initialize(logger)
29
+ @logger = logger
30
+ end
31
+
32
+ def run(inputs)
33
+ config_path = inputs['config']
34
+ exit_code = 0
35
+
36
+ # Validate the config path.
37
+ if config_path
38
+ errors = FileSystem.validate_file_paths(config_path)
39
+ return 1 if Errors.handle_with_usage(@logger, errors)
40
+ end
41
+
42
+ # Validate puppet config setting.
43
+ puppet = Config::Puppet.new(config_path)
44
+ puppet.load(logger: @logger)
45
+ return 1 if Errors.handle_with_usage(@logger, puppet.errors)
46
+
47
+ # Validate that we are offline
48
+ return 1 if HttpClient.check_server_online(puppet.settings, @logger)
49
+
50
+ # Getting the CRL(s)
51
+ loader = X509Loader.new(puppet.settings[:cacert], puppet.settings[:cakey], puppet.settings[:cacrl])
52
+
53
+ verified_crls = loader.crls.select { |crl| crl.verify(loader.key) }
54
+
55
+ if verified_crls.length == 1
56
+ puppet_crl = verified_crls.first
57
+ @logger.inform("Total number of certificates found in Puppet's CRL is: #{puppet_crl.revoked.length}.")
58
+ number_of_removed_duplicates = prune_CRL(puppet_crl)
59
+
60
+ if number_of_removed_duplicates > 0
61
+ update_pruned_CRL(puppet_crl, loader.key)
62
+ FileSystem.write_file(puppet.settings[:cacrl], loader.crls, 0644)
63
+ @logger.inform("Removed #{number_of_removed_duplicates} duplicated certs from Puppet's CRL.")
64
+ else
65
+ @logger.inform("No duplicate revocations found in the CRL.")
66
+ end
67
+ else
68
+ @logger.err("Could not identify Puppet's CRL. Aborting prune action.")
69
+ exit_code = 1
70
+ end
71
+
72
+ return exit_code
73
+ end
74
+
75
+ def prune_CRL(crl)
76
+ number_of_removed_duplicates = 0
77
+
78
+ existed_serial_number = Set.new()
79
+ revoked_list = crl.revoked
80
+ @logger.debug("Pruning duplicate entries in CRL for issuer " \
81
+ "#{crl.issuer.to_s(OpenSSL::X509::Name::RFC2253)}") if @logger.debug?
82
+
83
+ revoked_list.delete_if do |revoked|
84
+ if existed_serial_number.add?(revoked.serial)
85
+ false
86
+ else
87
+ number_of_removed_duplicates += 1
88
+ @logger.debug("Removing duplicate of #{revoked.serial}, " \
89
+ "revoked on #{revoked.time}\n") if @logger.debug?
90
+ true
91
+ end
92
+ end
93
+ crl.revoked=(revoked_list)
94
+
95
+ return number_of_removed_duplicates
96
+ end
97
+
98
+ def update_pruned_CRL(crl, pkey)
99
+ number_ext, other_ext = crl.extensions.partition{ |ext| ext.oid == "crlNumber" }
100
+ number_ext.each do |crl_number|
101
+ updated_crl_number = OpenSSL::BN.new(crl_number.value) + OpenSSL::BN.new(1)
102
+ crl_number.value=(OpenSSL::ASN1::Integer(updated_crl_number))
103
+ end
104
+ crl.extensions=(number_ext + other_ext)
105
+ crl.sign(pkey, OpenSSL::Digest::SHA256.new)
106
+ end
107
+
108
+ def self.parser(parsed = {})
109
+ OptionParser.new do |opts|
110
+ opts.banner = BANNER
111
+ opts.on('--help', 'Display this command-specific help output') do |help|
112
+ parsed['help'] = true
113
+ end
114
+ opts.on('--config CONF', 'Path to the puppet.conf file on disk') do |conf|
115
+ parsed['config'] = conf
116
+ end
117
+ end
118
+ end
119
+
120
+ def parse(args)
121
+ results = {}
122
+ parser = self.class.parser(results)
123
+ errors = CliParsing.parse_with_errors(parser, args)
124
+ errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
125
+
126
+ if errors_were_handled
127
+ exit_code = 1
128
+ else
129
+ exit_code = nil
130
+ end
131
+ return results, exit_code
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -41,8 +41,8 @@ module Puppetserver
41
41
  end
42
42
 
43
43
  # Returns a URI-like wrapper around CA specific urls
44
- def make_ca_url(resource_type = nil, certname = nil)
45
- HttpClient::URL.new('https', @ca_server, @ca_port, 'puppet-ca', 'v1', resource_type, certname)
44
+ def make_ca_url(resource_type = nil, certname = nil, query = {})
45
+ HttpClient::URL.new('https', @ca_server, @ca_port, 'puppet-ca', 'v1', resource_type, certname, query)
46
46
  end
47
47
 
48
48
  def process_ttl_input(ttl)
@@ -141,7 +141,7 @@ module Puppetserver
141
141
  when :revoke
142
142
  case result.code
143
143
  when '200', '204'
144
- @logger.inform "Revoked certificate for #{certname}"
144
+ @logger.inform "Certificate for #{certname} has been revoked"
145
145
  return :success
146
146
  when '404'
147
147
  @logger.err 'Error:'
@@ -215,7 +215,7 @@ module Puppetserver
215
215
  def check_revocation(certname, result)
216
216
  case result.code
217
217
  when '200', '204'
218
- @logger.inform "Revoked certificate for #{certname}"
218
+ @logger.inform "Certificate for #{certname} has been revoked"
219
219
  return :success
220
220
  when '409'
221
221
  return :invalid
@@ -250,8 +250,8 @@ module Puppetserver
250
250
  end
251
251
 
252
252
  # Returns nil for errors, else the result of the GET request
253
- def get_certificate_statuses
254
- result = get('certificate_statuses', 'any_key')
253
+ def get_certificate_statuses(query = {})
254
+ result = get('certificate_statuses', 'any_key', query)
255
255
 
256
256
  unless result.code == '200'
257
257
  @logger.err 'Error:'
@@ -287,8 +287,8 @@ module Puppetserver
287
287
  # @param resource_type [String] the resource type of url
288
288
  # @param resource_name [String] the resource name of url
289
289
  # @return [Struct] an instance of the Result struct with :code, :body
290
- def get(resource_type, resource_name)
291
- url = make_ca_url(resource_type, resource_name)
290
+ def get(resource_type, resource_name, query = {})
291
+ url = make_ca_url(resource_type, resource_name, query)
292
292
  @client.with_connection(url) do |connection|
293
293
  connection.get(url)
294
294
  end
@@ -8,6 +8,7 @@ require 'puppetserver/ca/action/list'
8
8
  require 'puppetserver/ca/action/revoke'
9
9
  require 'puppetserver/ca/action/setup'
10
10
  require 'puppetserver/ca/action/sign'
11
+ require 'puppetserver/ca/action/prune'
11
12
  require 'puppetserver/ca/action/migrate'
12
13
  require 'puppetserver/ca/errors'
13
14
  require 'puppetserver/ca/logger'
@@ -25,11 +26,12 @@ Manage the Private Key Infrastructure for
25
26
  Puppet Server's built-in Certificate Authority
26
27
  BANNER
27
28
 
28
- INIT_ACTIONS = {
29
+ ADMIN_ACTIONS = {
29
30
  'import' => Action::Import,
30
31
  'setup' => Action::Setup,
31
- 'enable' => Action::Enable,
32
- 'migrate' => Action::Migrate,
32
+ 'enable' => Action::Enable,
33
+ 'migrate' => Action::Migrate,
34
+ 'prune' => Action::Prune
33
35
  }
34
36
 
35
37
  MAINT_ACTIONS = {
@@ -40,15 +42,15 @@ BANNER
40
42
  'sign' => Action::Sign
41
43
  }
42
44
 
43
- VALID_ACTIONS = INIT_ACTIONS.merge(MAINT_ACTIONS).sort.to_h
45
+ VALID_ACTIONS = ADMIN_ACTIONS.merge(MAINT_ACTIONS).sort.to_h
44
46
 
45
47
  ACTION_LIST = "\nAvailable Actions:\n\n" +
46
48
  " Certificate Actions (requires a running Puppet Server):\n\n" +
47
49
  MAINT_ACTIONS.map do |action, cls|
48
50
  " #{action}\t#{cls::SUMMARY}"
49
51
  end.join("\n") + "\n\n" +
50
- " Initialization Actions (requires Puppet Server to be stopped):\n\n" +
51
- INIT_ACTIONS.map do |action, cls|
52
+ " Administrative Actions (requires Puppet Server to be stopped):\n\n" +
53
+ ADMIN_ACTIONS.map do |action, cls|
52
54
  " #{action}\t#{cls::SUMMARY}"
53
55
  end.join("\n")
54
56
 
@@ -17,8 +17,12 @@ module Puppetserver
17
17
  @level
18
18
  end
19
19
 
20
+ def debug?
21
+ return @level >= LEVELS[:debug]
22
+ end
23
+
20
24
  def debug(text)
21
- if @level >= LEVELS[:debug]
25
+ if debug?
22
26
  @out.puts(text)
23
27
  end
24
28
  end
@@ -1,5 +1,6 @@
1
1
  require 'net/https'
2
2
  require 'openssl'
3
+ require 'uri'
3
4
 
4
5
  require 'puppetserver/ca/errors'
5
6
 
@@ -114,7 +115,6 @@ module Puppetserver
114
115
  request.body = body
115
116
  result = @conn.request(request)
116
117
 
117
-
118
118
  Result.new(result.code, result.body)
119
119
  end
120
120
 
@@ -136,10 +136,13 @@ module Puppetserver
136
136
  # Like URI, but not... maybe of suspicious value
137
137
  URL = Struct.new(:protocol, :host, :port,
138
138
  :endpoint, :version,
139
- :resource_type, :resource_name) do
139
+ :resource_type, :resource_name, :query) do
140
140
  def full_url
141
- protocol + '://' + host + ':' + port + '/' +
142
- [endpoint, version, resource_type, resource_name].join('/')
141
+ url = protocol + '://' + host + ':' + port + '/' +
142
+ [endpoint, version, resource_type, resource_name].join('/')
143
+
144
+ url = url + "?" + URI.encode_www_form(query) unless query.nil? || query.empty?
145
+ return url
143
146
  end
144
147
 
145
148
  def to_uri
@@ -1,5 +1,5 @@
1
1
  module Puppetserver
2
2
  module Ca
3
- VERSION = "2.2.0"
3
+ VERSION = "2.3.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppetserver-ca
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-06 00:00:00.000000000 Z
11
+ date: 2021-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: facter
@@ -100,6 +100,7 @@ files:
100
100
  - lib/puppetserver/ca/action/import.rb
101
101
  - lib/puppetserver/ca/action/list.rb
102
102
  - lib/puppetserver/ca/action/migrate.rb
103
+ - lib/puppetserver/ca/action/prune.rb
103
104
  - lib/puppetserver/ca/action/revoke.rb
104
105
  - lib/puppetserver/ca/action/setup.rb
105
106
  - lib/puppetserver/ca/action/sign.rb