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 +4 -4
- data/README.md +5 -0
- data/lib/puppetserver/ca/action/list.rb +9 -7
- data/lib/puppetserver/ca/action/prune.rb +136 -0
- data/lib/puppetserver/ca/certificate_authority.rb +8 -8
- data/lib/puppetserver/ca/cli.rb +8 -6
- data/lib/puppetserver/ca/logger.rb +5 -1
- data/lib/puppetserver/ca/utils/http_client.rb +7 -4
- data/lib/puppetserver/ca/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 145cb62d733040dca7ad410a86146e0a0c82c29746a2c7de392004ad98737ed5
|
4
|
+
data.tar.gz: bd0db987a4af957208395f53134c3d59ab45f3c76d2e5d4205b589b75b1b9873
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
213
|
-
|
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 "
|
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 "
|
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
|
data/lib/puppetserver/ca/cli.rb
CHANGED
@@ -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
|
-
|
29
|
+
ADMIN_ACTIONS = {
|
29
30
|
'import' => Action::Import,
|
30
31
|
'setup' => Action::Setup,
|
31
|
-
'enable'
|
32
|
-
'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 =
|
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
|
-
"
|
51
|
-
|
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
|
|
@@ -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
|
-
|
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
|
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.
|
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-
|
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
|