puppetserver-ca 2.0.2 → 2.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: dd192c17f1178829548e7c39aff278a87fcc6bc699d7a1a357fcf84a53af7190
4
- data.tar.gz: a05ef2372661026a0bb3c539d8584e25ec6b4b0b9e0d61752de4335e23a95804
3
+ metadata.gz: 9443656cd200fac958b84f3b7444a402146344105b25022eef46dbe1d24201a1
4
+ data.tar.gz: aa4c2fbcae1976a25346c6cfe16d792099b6ccc722f24770535e58367bc4128d
5
5
  SHA512:
6
- metadata.gz: d1c0059436ac51099bf0f73f6ad72a0989ccbf59e3e934b8f213ffd05c0c89ed4c5a84ce9b32b0a0e76977ab46b9e17f0474e890bc9b7136cfde5f8224f2b83c
7
- data.tar.gz: 6fe6cd33aa3712d26444d0288184273c8184e5172bdbcb894516f450a316a0b41981428a6f07b1135f984a915fb09b9d5eb7656982d742afaf34fec5ad86c650
6
+ metadata.gz: 34e5662cbb5d7f92bf9f1b3f91f69d37149986c4ba998e42655cf5440349a242116f04c3abfc50244140f4de681fdffed77fa9c56128286bfe3e96d49ebc2939
7
+ data.tar.gz: 24740e26cceae352f4def897433bc4971b78c8d51c94e0692b19868a10a73dec29c266c1f9f09281b46299dfd9f795cba00c57b00726556529faf0ef820bd664
data/README.md CHANGED
@@ -55,20 +55,30 @@ 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
+
63
+ To enable verbose mode:
64
+ ```
65
+ puppetserver ca --verbose <action>
66
+ ```
67
+
58
68
  For more details, see the help output:
59
69
  ```
60
70
  puppetserver ca --help
61
71
  ```
62
72
 
63
73
  This code in this project is licensed under the Apache Software License v2,
64
- please see the included [License](https://github.com/puppetlabs/puppetserver-ca-cli/blob/master/LICENSE.md)
74
+ please see the included [License](https://github.com/puppetlabs/puppetserver-ca-cli/blob/main/LICENSE.md)
65
75
  for more details.
66
76
 
67
77
 
68
78
  ## Development
69
79
 
70
80
  After checking out the repo, run `bin/setup` to install dependencies. Then,
71
- run `rake spec` to run the tests. You can also run `bin/console` for an
81
+ run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an
72
82
  interactive prompt that will allow you to experiment.
73
83
 
74
84
  To install this gem onto your local machine, run `bundle exec rake install`.
@@ -92,8 +102,7 @@ To test your changes on a VM:
92
102
  1. To confirm that installation was successful, run `puppetserver ca --help`
93
103
 
94
104
  ### Releasing
95
- To release a new version, run the [release pipeline](https://jenkins-master-prod-1.delivery.puppetlabs.net/job/platform_puppetserver-ca_init-multijob_main/), which will bump the version, tag, build, and release the gem.
96
-
105
+ To release a new version, run the [release pipeline](https://jenkins-platform.delivery.puppetlabs.net/job/platform_puppetserver-ca_init-multijob_main/), which will bump the version, tag, build, and release the gem.
97
106
 
98
107
  ## Contributing & Support
99
108
 
@@ -105,9 +114,9 @@ Freenode, or the Puppet Community Slack channel.
105
114
 
106
115
  Contributions are welcome at https://github.com/puppetlabs/puppetserver-ca-cli/pulls.
107
116
  Contributors should both be sure to read the
108
- [contributing document](https://github.com/puppetlabs/puppetserver-ca-cli/blob/master/CONTRIBUTING.md)
117
+ [contributing document](https://github.com/puppetlabs/puppetserver-ca-cli/blob/main/CONTRIBUTING.md)
109
118
  and sign the [contributor license agreement](https://cla.puppet.com/).
110
119
 
111
120
  Everyone interacting with the project’s codebase, issue tracker, etc is expected
112
121
  to follow the
113
- [code of conduct](https://github.com/puppetlabs/puppetserver-ca-cli/blob/master/CODE_OF_CONDUCT.md).
122
+ [code of conduct](https://github.com/puppetlabs/puppetserver-ca-cli/blob/main/CODE_OF_CONDUCT.md).
@@ -14,7 +14,7 @@ module Puppetserver
14
14
 
15
15
  include Puppetserver::Ca::Utils
16
16
 
17
- CERTNAME_BLACKLIST = %w{--all --config}
17
+ CERTNAME_BLOCKLIST = %w{--all --config}
18
18
 
19
19
  SUMMARY = 'Revoke cert(s) and remove related files from CA'
20
20
  BANNER = <<-BANNER
@@ -59,7 +59,7 @@ BANNER
59
59
  errors = CliParsing.parse_with_errors(parser, args)
60
60
 
61
61
  results['certnames'].each do |certname|
62
- if CERTNAME_BLACKLIST.include?(certname)
62
+ if CERTNAME_BLOCKLIST.include?(certname)
63
63
  errors << " Cannot manage cert named `#{certname}` from " +
64
64
  "the CLI, if needed use the HTTP API directly"
65
65
  end
@@ -18,7 +18,7 @@ module Puppetserver
18
18
 
19
19
  # Only allow printing ascii characters, excluding /
20
20
  VALID_CERTNAME = /\A[ -.0-~]+\Z/
21
- CERTNAME_BLACKLIST = %w{--all --config}
21
+ CERTNAME_BLOCKLIST = %w{--all --config}
22
22
 
23
23
  SUMMARY = "Generate a new certificate signed by the CA"
24
24
  BANNER = <<-BANNER
@@ -35,7 +35,7 @@ Description:
35
35
  If the `--ca-client` flag is passed, the cert will be generated
36
36
  offline, without using Puppet Server's signing code, and will add
37
37
  a special extension authorizing it to talk to the CA API. This can
38
- be used for regenerating the master's host cert, or for manually
38
+ be used for regenerating the server's host cert, or for manually
39
39
  setting up other nodes to be CA clients. Do not distribute certs
40
40
  generated this way to any node that you do not intend to have
41
41
  administrative access to the CA (e.g. the ability to sign a cert).
@@ -91,7 +91,7 @@ BANNER
91
91
  errors << ' At least one certname is required to generate'
92
92
  else
93
93
  results['certnames'].each do |certname|
94
- if CERTNAME_BLACKLIST.include?(certname)
94
+ if CERTNAME_BLOCKLIST.include?(certname)
95
95
  errors << " Cannot manage cert named `#{certname}` from " +
96
96
  "the CLI, if needed use the HTTP API directly"
97
97
  end
@@ -15,7 +15,7 @@ module Puppetserver
15
15
  class Import
16
16
  include Puppetserver::Ca::Utils
17
17
 
18
- SUMMARY = "Import an external CA chain and generate master PKI"
18
+ SUMMARY = "Import an external CA chain and generate server PKI"
19
19
  BANNER = <<-BANNER
20
20
  Usage:
21
21
  puppetserver ca import [--help]
@@ -73,7 +73,7 @@ BANNER
73
73
  def import(loader, settings, signing_digest)
74
74
  ca = Puppetserver::Ca::LocalCertificateAuthority.new(signing_digest, settings)
75
75
  ca.initialize_ssl_components(loader)
76
- master_key, master_cert = ca.create_master_cert
76
+ server_key, server_cert = ca.create_server_cert
77
77
  return ca.errors if ca.errors.any?
78
78
 
79
79
  FileSystem.ensure_dirs([settings[:ssldir],
@@ -89,25 +89,25 @@ BANNER
89
89
  [settings[:cadir] + '/infra_crl.pem', loader.crls],
90
90
  [settings[:localcacert], loader.certs],
91
91
  [settings[:hostcrl], loader.crls],
92
- [settings[:hostpubkey], master_key.public_key],
93
- [settings[:hostcert], master_cert],
94
- [settings[:cert_inventory], ca.inventory_entry(master_cert)],
92
+ [settings[:hostpubkey], server_key.public_key],
93
+ [settings[:hostcert], server_cert],
94
+ [settings[:cert_inventory], ca.inventory_entry(server_cert)],
95
95
  [settings[:capub], loader.key.public_key],
96
96
  [settings[:cadir] + '/infra_inventory.txt', ''],
97
97
  [settings[:cadir] + '/infra_serials', ''],
98
98
  [settings[:serial], "002"],
99
- [File.join(settings[:signeddir], "#{settings[:certname]}.pem"), master_cert]
99
+ [File.join(settings[:signeddir], "#{settings[:certname]}.pem"), server_cert]
100
100
  ]
101
101
 
102
102
  private_files = [
103
- [settings[:hostprivkey], master_key],
103
+ [settings[:hostprivkey], server_key],
104
104
  [settings[:cakey], loader.key],
105
105
  ]
106
106
 
107
107
  files_to_check = public_files + private_files
108
- # We don't want to error if master's keys exist. Certain workflows
108
+ # We don't want to error if server's keys exist. Certain workflows
109
109
  # allow the agent to have already be installed with keys and then
110
- # upgraded to be a master. The host class will honor keys, if both
110
+ # upgraded to be a server. The host class will honor keys, if both
111
111
  # public and private exist, and error if only one exists - as is
112
112
  # previous behavior.
113
113
  files_to_check = files_to_check.map(&:first) - [settings[:hostpubkey], settings[:hostprivkey]]
@@ -181,11 +181,11 @@ ERR
181
181
  parsed['crl-chain'] = chain
182
182
  end
183
183
  opts.on('--certname NAME',
184
- 'Common name to use for the master cert') do |name|
184
+ 'Common name to use for the server cert') do |name|
185
185
  parsed['certname'] = name
186
186
  end
187
187
  opts.on('--subject-alt-names NAME[,NAME]',
188
- 'Subject alternative names for the master cert') do |sans|
188
+ 'Subject alternative names for the server cert') do |sans|
189
189
  parsed['subject-alt-names'] = sans
190
190
  end
191
191
  end
@@ -30,6 +30,7 @@ Options:
30
30
  BANNER
31
31
 
32
32
  BODY = JSON.dump({desired_state: 'signed'})
33
+ VALID_FORMAT = ['text', 'json']
33
34
 
34
35
  def initialize(logger)
35
36
  @logger = logger
@@ -47,6 +48,9 @@ Options:
47
48
  opts.on('--all', 'List all certificates') do |a|
48
49
  parsed['all'] = true
49
50
  end
51
+ opts.on('--format FORMAT', "Valid formats are: 'text' (default), 'json'") do |f|
52
+ parsed['format'] = f
53
+ end
50
54
  opts.on('--certname NAME[,NAME]', Array, 'List the specified cert(s)') do |cert|
51
55
  parsed['certname'] = cert
52
56
  end
@@ -57,9 +61,15 @@ Options:
57
61
  config = input['config']
58
62
  certnames = input['certname'] || []
59
63
  all = input['all']
64
+ output_format = input['format'] || "text"
65
+
66
+ unless VALID_FORMAT.include?(output_format)
67
+ Errors.handle_with_usage(@logger, ["Unknown format flag '#{output_format}'. Valid formats are '#{VALID_FORMAT.join("', '")}'."])
68
+ return 1
69
+ end
60
70
 
61
71
  if all && certnames.any?
62
- Errors.handle_with_usage(@logger, ['Cannot combine use of --all and --certname'])
72
+ Errors.handle_with_usage(@logger, ['Cannot combine use of --all and --certname.'])
63
73
  return 1
64
74
  end
65
75
 
@@ -71,24 +81,60 @@ Options:
71
81
  puppet = Config::Puppet.parse(config, @logger)
72
82
  return 1 if Errors.handle_with_usage(@logger, puppet.errors)
73
83
 
74
- filter_names = certnames.any? \
75
- ? lambda { |x| certnames.include?(x['name']) }
76
- : lambda { |x| true }
84
+ if certnames.any?
85
+ filter_names = lambda { |x| certnames.include?(x['name']) }
86
+ else
87
+ filter_names = lambda { |x| true }
88
+ end
77
89
 
78
90
  all_certs = get_all_certs(puppet.settings).select { |cert| filter_names.call(cert) }
79
91
  requested, signed, revoked = separate_certs(all_certs)
80
92
  missing = certnames - all_certs.map { |cert| cert['name'] }
81
93
 
82
- (all || certnames.any?) \
83
- ? output_certs_by_state(requested, signed, revoked, missing)
84
- : output_certs_by_state(requested)
94
+ if (all || certnames.any?)
95
+ output_certs_by_state(all, output_format, requested, signed, revoked, missing)
96
+ else
97
+ output_certs_by_state(all, output_format, requested)
98
+ end
99
+
100
+ return missing.any? ? 1 : 0
101
+ end
85
102
 
86
- return missing.any? \
87
- ? 1
88
- : 0
103
+ def output_certs_by_state(all, output_format, requested, signed = [], revoked = [], missing = [])
104
+ if output_format == 'json'
105
+ output_certs_json_format(all, requested, signed, revoked, missing)
106
+ else
107
+ output_certs_text_format(requested, signed, revoked, missing)
108
+ end
89
109
  end
90
110
 
91
- def output_certs_by_state(requested, signed = [], revoked = [], missing = [])
111
+ def output_certs_json_format(all, requested, signed, revoked, missing)
112
+ grouped_cert = {}
113
+
114
+ if all
115
+ grouped_cert = { "requested" => requested,
116
+ "signed" => signed,
117
+ "revoked" => revoked }.to_json
118
+ @logger.inform(grouped_cert)
119
+ else
120
+ grouped_cert["requested"] = requested unless requested.empty?
121
+ grouped_cert["signed"] = signed unless signed.empty?
122
+ grouped_cert["revoked"] = revoked unless revoked.empty?
123
+ grouped_cert["missing"] = missing unless missing.empty?
124
+
125
+ # If neither the '--all' flag or the '--certname' flag was passed in
126
+ # and the requested cert array is empty, we output a JSON object
127
+ # with an empty 'requested' key. Otherwise, we display
128
+ # any of the classes that are currently in grouped_cert
129
+ if grouped_cert.empty?
130
+ @logger.inform({ "requested" => requested }.to_json)
131
+ else
132
+ @logger.inform(grouped_cert.to_json)
133
+ end
134
+ end
135
+ end
136
+
137
+ def output_certs_text_format(requested, signed, revoked, missing)
92
138
  if revoked.empty? && signed.empty? && requested.empty? && missing.empty?
93
139
  @logger.inform "No certificates to list"
94
140
  return
@@ -165,7 +211,12 @@ Options:
165
211
 
166
212
  def get_all_certs(settings)
167
213
  result = Puppetserver::Ca::CertificateAuthority.new(@logger, settings).get_certificate_statuses
168
- result ? JSON.parse(result.body) : []
214
+
215
+ if result
216
+ return JSON.parse(result.body)
217
+ else
218
+ return []
219
+ end
169
220
  end
170
221
 
171
222
  def parse(args)
@@ -176,8 +227,11 @@ Options:
176
227
 
177
228
  errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
178
229
 
179
- exit_code = errors_were_handled ? 1 : nil
180
-
230
+ if errors_were_handled
231
+ exit_code = 1
232
+ else
233
+ exit_code = nil
234
+ end
181
235
  return results, exit_code
182
236
  end
183
237
  end
@@ -18,8 +18,9 @@ Usage:
18
18
 
19
19
  Description:
20
20
  Migrate an existing CA directory to #{PUPPETSERVER_CA_DIR}. This is for
21
- upgrading from Puppet Platform 6.x to Puppet 7. Use the currently configured
22
- puppet.conf file in your installation, or supply one using the `--config` flag.
21
+ upgrading from Puppet Platform 6.x to Puppet 7. Uses the default puppet.conf
22
+ in your installation, or use a different config by supplying the `--config` flag.
23
+
23
24
  Options:
24
25
  BANNER
25
26
 
@@ -0,0 +1,131 @@
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
+
35
+ # Validate the config path.
36
+ if config_path
37
+ errors = FileSystem.validate_file_paths(config_path)
38
+ return 1 if Errors.handle_with_usage(@logger, errors)
39
+ end
40
+
41
+ # Validate puppet config setting.
42
+ puppet = Config::Puppet.new(config_path)
43
+ puppet.load(logger: @logger)
44
+ return 1 if Errors.handle_with_usage(@logger, puppet.errors)
45
+
46
+ # Validate that we are offline
47
+ return 1 if HttpClient.check_server_online(puppet.settings, @logger)
48
+
49
+ # Getting the CRL(s)
50
+ loader = X509Loader.new(puppet.settings[:cacert], puppet.settings[:cakey], puppet.settings[:cacrl])
51
+
52
+ puppet_crl = loader.crls.select { |crl| crl.verify(loader.key) }
53
+ number_of_removed_duplicates = prune_CRLs(puppet_crl)
54
+
55
+ if number_of_removed_duplicates > 0
56
+ update_pruned_CRL(puppet_crl, loader.key)
57
+ FileSystem.write_file(puppet.settings[:cacrl], loader.crls, 0644)
58
+ @logger.inform("Removed #{number_of_removed_duplicates} duplicated certs from Puppet's CRL.")
59
+ else
60
+ @logger.inform("No duplicate revocations found in the CRL.")
61
+ end
62
+
63
+ return 0
64
+ end
65
+
66
+ def prune_CRLs(crl_list)
67
+ number_of_removed_duplicates = 0
68
+
69
+ crl_list.each do |crl|
70
+ existed_serial_number = Set.new()
71
+ revoked_list = crl.revoked
72
+ @logger.debug("Pruning duplicate entries in CRL for issuer " \
73
+ "#{crl.issuer.to_s(OpenSSL::X509::Name::RFC2253)}") if @logger.debug?
74
+
75
+ revoked_list.delete_if do |revoked|
76
+ if existed_serial_number.add?(revoked.serial)
77
+ false
78
+ else
79
+ number_of_removed_duplicates += 1
80
+ @logger.debug("Removing duplicate of #{revoked.serial}, " \
81
+ "revoked on #{revoked.time}\n") if @logger.debug?
82
+ true
83
+ end
84
+ end
85
+ crl.revoked=(revoked_list)
86
+ end
87
+
88
+ return number_of_removed_duplicates
89
+ end
90
+
91
+ def update_pruned_CRL(crl_list, pkey)
92
+ crl_list.each do |crl|
93
+ number_ext, other_ext = crl.extensions.partition{ |ext| ext.oid == "crlNumber" }
94
+ number_ext.each do |crl_number|
95
+ updated_crl_number = OpenSSL::BN.new(crl_number.value) + OpenSSL::BN.new(1)
96
+ crl_number.value=(OpenSSL::ASN1::Integer(updated_crl_number))
97
+ end
98
+ crl.extensions=(number_ext + other_ext)
99
+ crl.sign(pkey, OpenSSL::Digest::SHA256.new)
100
+ end
101
+ end
102
+
103
+ def self.parser(parsed = {})
104
+ OptionParser.new do |opts|
105
+ opts.banner = BANNER
106
+ opts.on('--help', 'Display this command-specific help output') do |help|
107
+ parsed['help'] = true
108
+ end
109
+ opts.on('--config CONF', 'Path to the puppet.conf file on disk') do |conf|
110
+ parsed['config'] = conf
111
+ end
112
+ end
113
+ end
114
+
115
+ def parse(args)
116
+ results = {}
117
+ parser = self.class.parser(results)
118
+ errors = CliParsing.parse_with_errors(parser, args)
119
+ errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
120
+
121
+ if errors_were_handled
122
+ exit_code = 1
123
+ else
124
+ exit_code = nil
125
+ end
126
+ return results, exit_code
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -12,7 +12,7 @@ module Puppetserver
12
12
 
13
13
  include Puppetserver::Ca::Utils
14
14
 
15
- CERTNAME_BLACKLIST = %w{--all --config}
15
+ CERTNAME_BLOCKLIST = %w{--all --config}
16
16
 
17
17
  SUMMARY = 'Revoke certificate(s)'
18
18
  BANNER = <<-BANNER
@@ -55,7 +55,7 @@ BANNER
55
55
  errors = CliParsing.parse_with_errors(parser, args)
56
56
 
57
57
  results['certnames'].each do |certname|
58
- if CERTNAME_BLACKLIST.include?(certname)
58
+ if CERTNAME_BLOCKLIST.include?(certname)
59
59
  errors << " Cannot manage cert named `#{certname}` from " +
60
60
  "the CLI, if needed use the HTTP API directly"
61
61
  end
@@ -24,10 +24,10 @@ Usage:
24
24
  Description:
25
25
  Setup a root and intermediate signing CA for Puppet Server
26
26
  and store generated CA keys, certs, crls, and associated
27
- master related files on disk.
27
+ server related files on disk.
28
28
 
29
29
  The `--subject-alt-names` flag can be used to add SANs to the
30
- certificate generated for the Puppet master. Multiple names can be
30
+ certificate generated for the Puppet server. Multiple names can be
31
31
  listed as a comma separated string. These can be either DNS names or
32
32
  IP addresses, differentiated by prefixes: `DNS:foo.bar.com,IP:123.456.789`.
33
33
  Names with no prefix will be treated as DNS names.
@@ -77,7 +77,7 @@ BANNER
77
77
 
78
78
  root_key, root_cert, root_crl = ca.create_root_cert
79
79
  ca.create_intermediate_cert(root_key, root_cert)
80
- master_key, master_cert = ca.create_master_cert
80
+ server_key, server_cert = ca.create_server_cert
81
81
  return ca.errors if ca.errors.any?
82
82
 
83
83
  FileSystem.ensure_dirs([settings[:ssldir],
@@ -91,28 +91,28 @@ BANNER
91
91
  [settings[:cacert], [ca.cert, root_cert]],
92
92
  [settings[:cacrl], [ca.crl, root_crl]],
93
93
  [settings[:cadir] + '/infra_crl.pem', [ca.crl, root_crl]],
94
- [settings[:hostcert], master_cert],
94
+ [settings[:hostcert], server_cert],
95
95
  [settings[:localcacert], [ca.cert, root_cert]],
96
96
  [settings[:hostcrl], [ca.crl, root_crl]],
97
- [settings[:hostpubkey], master_key.public_key],
97
+ [settings[:hostpubkey], server_key.public_key],
98
98
  [settings[:capub], ca.key.public_key],
99
- [settings[:cert_inventory], ca.inventory_entry(master_cert)],
99
+ [settings[:cert_inventory], ca.inventory_entry(server_cert)],
100
100
  [settings[:cadir] + '/infra_inventory.txt', ''],
101
101
  [settings[:cadir] + '/infra_serials', ''],
102
102
  [settings[:serial], "002"],
103
- [File.join(settings[:signeddir], "#{settings[:certname]}.pem"), master_cert],
103
+ [File.join(settings[:signeddir], "#{settings[:certname]}.pem"), server_cert],
104
104
  ]
105
105
 
106
106
  private_files = [
107
- [settings[:hostprivkey], master_key],
107
+ [settings[:hostprivkey], server_key],
108
108
  [settings[:rootkey], root_key],
109
109
  [settings[:cakey], ca.key],
110
110
  ]
111
111
 
112
112
  files_to_check = public_files + private_files
113
- # We don't want to error if master's keys exist. Certain workflows
113
+ # We don't want to error if server's keys exist. Certain workflows
114
114
  # allow the agent to have already be installed with keys and then
115
- # upgraded to be a master. The host class will honor keys, if both
115
+ # upgraded to be a server. The host class will honor keys, if both
116
116
  # public and private exist, and error if only one exists - as is
117
117
  # previous behavior.
118
118
  files_to_check = files_to_check.map(&:first) - [settings[:hostpubkey], settings[:hostprivkey]]
@@ -163,7 +163,7 @@ ERR
163
163
  parsed['config'] = conf
164
164
  end
165
165
  opts.on('--subject-alt-names NAME[,NAME]',
166
- 'Subject alternative names for the master cert') do |sans|
166
+ 'Subject alternative names for the server cert') do |sans|
167
167
  parsed['subject-alt-names'] = sans
168
168
  end
169
169
  opts.on('--ca-name NAME',
@@ -171,7 +171,7 @@ ERR
171
171
  parsed['ca-name'] = name
172
172
  end
173
173
  opts.on('--certname NAME',
174
- 'Common name to use for the master cert') do |name|
174
+ 'Common name to use for the server cert') do |name|
175
175
  parsed['certname'] = name
176
176
  end
177
177
  end
@@ -23,7 +23,7 @@ module Puppetserver
23
23
 
24
24
  def initialize(logger, settings)
25
25
  @logger = logger
26
- @client = HttpClient.new(settings)
26
+ @client = HttpClient.new(@logger, settings)
27
27
  @ca_server = settings[:ca_server]
28
28
  @ca_port = settings[:ca_port]
29
29
  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
 
@@ -64,8 +66,10 @@ BANNER
64
66
 
65
67
 
66
68
  def self.run(cli_args = ARGV, out = STDOUT, err = STDERR)
67
- logger = Puppetserver::Ca::Logger.new(:info, out, err)
68
69
  parser, general_options, unparsed = parse_general_inputs(cli_args)
70
+ level = general_options.delete('verbose') ? :debug : :info
71
+
72
+ logger = Puppetserver::Ca::Logger.new(level, out, err)
69
73
 
70
74
  if general_options['version']
71
75
  logger.inform Puppetserver::Ca::VERSION
@@ -121,6 +125,9 @@ BANNER
121
125
  opts.on('--version', 'Display the version') do |v|
122
126
  parsed['version'] = true
123
127
  end
128
+ opts.on('--verbose', 'Display low-level information') do |verbose|
129
+ parsed['verbose'] = true
130
+ end
124
131
 
125
132
  opts.separator ACTION_OPTIONS
126
133
  opts.separator "\nSee `puppetserver ca <action> --help` for detailed info"
@@ -69,6 +69,9 @@ module Puppetserver
69
69
 
70
70
  overrides = results[:agent].merge(results[:main]).merge(results[:master]).merge(results[:server])
71
71
  overrides.merge!(cli_overrides)
72
+ if overrides[:masterport]
73
+ overrides[:serverport] ||= overrides.delete(:masterport)
74
+ end
72
75
 
73
76
  @settings = resolve_settings(overrides, logger, ca_dir_warn: ca_dir_warn).freeze
74
77
  end
@@ -103,7 +106,7 @@ module Puppetserver
103
106
  [:certdir, '$ssldir/certs'],
104
107
  [:certname, default_certname],
105
108
  [:server, 'puppet'],
106
- [:masterport, '8140'],
109
+ [:serverport, '8140'],
107
110
  [:privatekeydir, '$ssldir/private_keys'],
108
111
  [:publickeydir, '$ssldir/public_keys'],
109
112
  ]
@@ -121,7 +124,7 @@ module Puppetserver
121
124
  :serial => '$cadir/serial',
122
125
  :cert_inventory => '$cadir/inventory.txt',
123
126
  :ca_server => '$server',
124
- :ca_port => '$masterport',
127
+ :ca_port => '$serverport',
125
128
  :localcacert => '$certdir/ca.pem',
126
129
  :hostcrl => '$ssldir/crl.pem',
127
130
  :hostcert => '$certdir/$certname.pem',
@@ -287,7 +290,7 @@ module Puppetserver
287
290
  end
288
291
 
289
292
  if settings.dig(:server_list, 0, 1) &&
290
- settings[:ca_port] == '$masterport'
293
+ settings[:ca_port] == '$serverport'
291
294
 
292
295
  settings[:ca_port] = settings.dig(:server_list, 0, 1)
293
296
  end
@@ -58,10 +58,10 @@ module Puppetserver
58
58
  @errors = []
59
59
  end
60
60
 
61
- # If both the private and public keys exist for a master then we want
61
+ # If both the private and public keys exist for a server then we want
62
62
  # to honor them here, if only one key exists we want to surface an error,
63
63
  # and if neither exist we generate a new key. This logic is necessary for
64
- # proper bootstrapping for certain master workflows.
64
+ # proper bootstrapping for certain server workflows.
65
65
  def create_private_key(keylength, private_path = '', public_path = '')
66
66
  if File.exists?(private_path) && File.exists?(public_path)
67
67
  return OpenSSL::PKey.read(File.read(private_path))
@@ -20,7 +20,7 @@ module Puppetserver
20
20
 
21
21
  CLI_AUTH_EXT_OID = "1.3.6.1.4.1.34380.1.3.39"
22
22
 
23
- MASTER_EXTENSIONS = [
23
+ SERVER_EXTENSIONS = [
24
24
  ["basicConstraints", "CA:FALSE", true],
25
25
  ["nsComment", "Puppet Server Internal Certificate", false],
26
26
  ["authorityKeyIdentifier", "keyid:always", false],
@@ -132,23 +132,23 @@ module Puppetserver
132
132
  time.strftime('%Y-%m-%dT%H:%M:%S%Z')
133
133
  end
134
134
 
135
- def create_master_cert
136
- master_cert = nil
137
- master_key = @host.create_private_key(@settings[:keylength],
135
+ def create_server_cert
136
+ server_cert = nil
137
+ server_key = @host.create_private_key(@settings[:keylength],
138
138
  @settings[:hostprivkey],
139
139
  @settings[:hostpubkey])
140
- if master_key
141
- master_csr = @host.create_csr(name: @settings[:certname], key: master_key)
140
+ if server_key
141
+ server_csr = @host.create_csr(name: @settings[:certname], key: server_key)
142
142
  if @settings[:subject_alt_names].empty?
143
143
  alt_names = "DNS:puppet, DNS:#{@settings[:certname]}"
144
144
  else
145
145
  alt_names = @settings[:subject_alt_names]
146
146
  end
147
147
 
148
- master_cert = sign_authorized_cert(master_csr, alt_names)
148
+ server_cert = sign_authorized_cert(server_csr, alt_names)
149
149
  end
150
150
 
151
- return master_key, master_cert
151
+ return server_key, server_cert
152
152
  end
153
153
 
154
154
  def sign_authorized_cert(csr, alt_names = '')
@@ -176,7 +176,7 @@ module Puppetserver
176
176
  end
177
177
 
178
178
  def add_authorized_extensions(cert, ef)
179
- MASTER_EXTENSIONS.each do |ext|
179
+ SERVER_EXTENSIONS.each do |ext|
180
180
  extension = ef.create_extension(*ext)
181
181
  cert.add_extension(extension)
182
182
  end
@@ -13,8 +13,16 @@ module Puppetserver
13
13
  @err = err
14
14
  end
15
15
 
16
+ def level
17
+ @level
18
+ end
19
+
20
+ def debug?
21
+ return @level >= LEVELS[:debug]
22
+ end
23
+
16
24
  def debug(text)
17
- if @level >= LEVELS[:debug]
25
+ if debug?
18
26
  @out.puts(text)
19
27
  end
20
28
  end
@@ -19,7 +19,8 @@ module Puppetserver
19
19
 
20
20
  # Not all connections require a client cert to be present.
21
21
  # For example, when querying the status endpoint.
22
- def initialize(settings, with_client_cert: true)
22
+ def initialize(logger, settings, with_client_cert: true)
23
+ @logger = logger
23
24
  @store = make_store(settings[:localcacert],
24
25
  settings[:certificate_revocation],
25
26
  settings[:hostcrl])
@@ -50,7 +51,7 @@ module Puppetserver
50
51
  # The Connection object should have HTTP verbs defined on it that take
51
52
  # a body (and optional overrides). Returns whatever the block given returned.
52
53
  def with_connection(url, &block)
53
- request = ->(conn) { block.call(Connection.new(conn, url)) }
54
+ request = ->(conn) { block.call(Connection.new(conn, url, @logger)) }
54
55
 
55
56
  begin
56
57
  Net::HTTP.start(url.host, url.port,
@@ -85,29 +86,35 @@ module Puppetserver
85
86
  # and defines methods named after HTTP verbs that are called on the
86
87
  # saved connection, returning a Result.
87
88
  class Connection
88
- def initialize(net_http_connection, url_struct)
89
+ def initialize(net_http_connection, url_struct, logger)
89
90
  @conn = net_http_connection
90
91
  @url = url_struct
92
+ @logger = logger
91
93
  end
92
94
 
93
95
  def get(url_overide = nil, headers = {})
94
96
  url = url_overide || @url
95
97
  headers = DEFAULT_HEADERS.merge(headers)
96
98
 
99
+ @logger.debug("Making a GET request at #{url.full_url}")
100
+
97
101
  request = Net::HTTP::Get.new(url.to_uri, headers)
98
102
  result = @conn.request(request)
99
-
100
103
  Result.new(result.code, result.body)
104
+
101
105
  end
102
106
 
103
107
  def put(body, url_override = nil, headers = {})
104
108
  url = url_override || @url
105
109
  headers = DEFAULT_HEADERS.merge(headers)
106
110
 
111
+ @logger.debug("Making a PUT request at #{url.full_url}")
112
+
107
113
  request = Net::HTTP::Put.new(url.to_uri, headers)
108
114
  request.body = body
109
115
  result = @conn.request(request)
110
116
 
117
+
111
118
  Result.new(result.code, result.body)
112
119
  end
113
120
 
@@ -115,6 +122,8 @@ module Puppetserver
115
122
  url = url_override || @url
116
123
  headers = DEFAULT_HEADERS.merge(headers)
117
124
 
125
+ @logger.debug("Making a DELETE request at #{url.full_url}")
126
+
118
127
  result = @conn.request(Net::HTTP::Delete.new(url.to_uri, headers))
119
128
 
120
129
  Result.new(result.code, result.body)
@@ -166,12 +175,12 @@ module Puppetserver
166
175
  def self.check_server_online(settings, logger)
167
176
  status_url = URL.new('https', settings[:ca_server], settings[:ca_port], 'status', 'v1', 'simple', 'ca')
168
177
  begin
169
- # Generating certs offline is necessary if the master cert has been destroyed
178
+ # Generating certs offline is necessary if the server cert has been destroyed
170
179
  # or compromised. Since querying the status endpoint does not require a client cert, and
171
180
  # we commonly won't have one, don't require one for creating the connection.
172
181
  # Additionally, we want to ensure the server is stopped before migrating the CA dir to
173
182
  # avoid issues with writing to the CA dir and moving it.
174
- self.new(settings, with_client_cert: false).with_connection(status_url) do |conn|
183
+ self.new(logger, settings, with_client_cert: false).with_connection(status_url) do |conn|
175
184
  result = conn.get
176
185
  if result.body == "running"
177
186
  logger.err "Puppetserver service is running. Please stop it before attempting to run this command."
@@ -1,5 +1,5 @@
1
1
  module Puppetserver
2
2
  module Ca
3
- VERSION = "2.0.2"
3
+ VERSION = "2.3.1"
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.0.2
4
+ version: 2.3.1
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-03-25 00:00:00.000000000 Z
11
+ date: 2021-07-28 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