puppetserver-ca 2.2.0 → 2.3.3

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: 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