puppetserver-ca 2.6.0 → 2.7.0
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/.github/dependabot.yml +11 -0
- data/CODEOWNERS +1 -1
- data/README.md +2 -7
- data/lib/puppetserver/ca/action/setup.rb +7 -1
- data/lib/puppetserver/ca/action/sign.rb +24 -8
- data/lib/puppetserver/ca/certificate_authority.rb +124 -4
- data/lib/puppetserver/ca/config/puppet.rb +10 -2
- data/lib/puppetserver/ca/utils/http_client.rb +14 -1
- data/lib/puppetserver/ca/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3405b0e0be4f947fcec85824880f6b62fb2630d1993e130e1782960e58b5a80
|
4
|
+
data.tar.gz: 92ae8e4b074c7d12609a79beb4c886a95f3299e44b2b4ff602f8b6acf7b002c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82fce6d5d561700df09dc132723a600f2943ca86d19b7a2cbe56b1c25c4ebce170cb4a973ef6a07d0271057587cccbd2185c1a392dba9217b6dd6a8c617ce3df
|
7
|
+
data.tar.gz: 130b8dc609b09c2ab7722459f62febcce52452ea419fa69f4ecb70d059b28428962574206a6bc5600f620f541c4205f86fe9e2eb7381a5bee6bb2e9fa24941b4
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
3
|
+
# Please see the documentation for all configuration options:
|
4
|
+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
5
|
+
|
6
|
+
version: 2
|
7
|
+
updates:
|
8
|
+
- package-ecosystem: "" # See documentation for possible values
|
9
|
+
directory: "/" # Location of package manifests
|
10
|
+
schedule:
|
11
|
+
interval: "weekly"
|
data/CODEOWNERS
CHANGED
data/README.md
CHANGED
@@ -101,16 +101,11 @@ To test your changes on a VM:
|
|
101
101
|
```
|
102
102
|
1. To confirm that installation was successful, run `puppetserver ca --help`
|
103
103
|
|
104
|
-
### Releasing
|
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.
|
106
|
-
|
107
104
|
## Contributing & Support
|
108
105
|
|
109
|
-
Bug reports and feature requests are welcome
|
110
|
-
https://tickets.puppetlabs.com/projects/SERVER/issues.
|
106
|
+
Bug reports and feature requests are welcome via GitHub issues.
|
111
107
|
|
112
|
-
For interactive questions feel free to post to #puppet or #puppet-dev on
|
113
|
-
Freenode, or the Puppet Community Slack channel.
|
108
|
+
For interactive questions feel free to post to #puppet or #puppet-dev on the Puppet Community Slack channel.
|
114
109
|
|
115
110
|
Contributions are welcome at https://github.com/puppetlabs/puppetserver-ca-cli/pulls.
|
116
111
|
Contributors should both be sure to read the
|
@@ -19,7 +19,7 @@ module Puppetserver
|
|
19
19
|
Usage:
|
20
20
|
puppetserver ca setup [--help]
|
21
21
|
puppetserver ca setup [--config PATH] [--subject-alt-names NAME[,NAME]]
|
22
|
-
[--certname NAME] [--ca-name NAME]
|
22
|
+
[--certname NAME] [--ca-name NAME] [--root-ca-name NAME]
|
23
23
|
|
24
24
|
Description:
|
25
25
|
Setup a root and intermediate signing CA for Puppet Server
|
@@ -51,6 +51,7 @@ BANNER
|
|
51
51
|
settings_overrides = {}
|
52
52
|
settings_overrides[:certname] = input['certname'] unless input['certname'].empty?
|
53
53
|
settings_overrides[:ca_name] = input['ca-name'] unless input['ca-name'].empty?
|
54
|
+
settings_overrides[:root_ca_name] = input['root-ca-name'] unless input['root-ca-name'].empty?
|
54
55
|
# Since puppet expects the key to be called 'dns_alt_names', we need to use that here
|
55
56
|
# to ensure that the overriding works correctly.
|
56
57
|
settings_overrides[:dns_alt_names] = input['subject-alt-names'] unless input['subject-alt-names'].empty?
|
@@ -153,6 +154,7 @@ ERR
|
|
153
154
|
def self.parser(parsed = {})
|
154
155
|
parsed['subject-alt-names'] = ''
|
155
156
|
parsed['ca-name'] = ''
|
157
|
+
parsed['root-ca-name'] = ''
|
156
158
|
parsed['certname'] = ''
|
157
159
|
OptionParser.new do |opts|
|
158
160
|
opts.banner = BANNER
|
@@ -170,6 +172,10 @@ ERR
|
|
170
172
|
'Common name to use for the CA signing cert') do |name|
|
171
173
|
parsed['ca-name'] = name
|
172
174
|
end
|
175
|
+
opts.on('--root-ca-name NAME',
|
176
|
+
'Common name to use for the self-signed Root CA cert') do |name|
|
177
|
+
parsed['root-ca-name'] = name
|
178
|
+
end
|
173
179
|
opts.on('--certname NAME',
|
174
180
|
'Common name to use for the server cert') do |name|
|
175
181
|
parsed['certname'] = name
|
@@ -66,17 +66,33 @@ Options:
|
|
66
66
|
return 1 if Errors.handle_with_usage(@logger, puppet.errors)
|
67
67
|
|
68
68
|
ca = Puppetserver::Ca::CertificateAuthority.new(@logger, puppet.settings)
|
69
|
+
bulk_sign = ca.server_has_bulk_signing_endpoints
|
70
|
+
|
71
|
+
# Bulk sign endpoints don't allow setting TTL, so
|
72
|
+
# use single signing endpoint if TTL is specified.
|
73
|
+
success = false
|
74
|
+
if input['ttl'] || !bulk_sign
|
75
|
+
if input['all']
|
76
|
+
requested_certnames = get_all_pending_certs(ca)
|
77
|
+
return 1 if requested_certnames.nil?
|
78
|
+
return 24 if requested_certnames.empty?
|
79
|
+
else
|
80
|
+
requested_certnames = input['certname']
|
81
|
+
end
|
69
82
|
|
70
|
-
|
71
|
-
|
72
|
-
return 1 if requested_certnames.nil?
|
73
|
-
return 24 if requested_certnames.empty?
|
83
|
+
success = ca.sign_certs(requested_certnames, input['ttl'])
|
84
|
+
return success ? 0 : 1
|
74
85
|
else
|
75
|
-
|
86
|
+
result = input['all'] ? ca.sign_all : ca.sign_bulk(input['certname'])
|
87
|
+
case result
|
88
|
+
when :success
|
89
|
+
return 0
|
90
|
+
when :no_requests
|
91
|
+
return 24
|
92
|
+
else
|
93
|
+
return 1
|
94
|
+
end
|
76
95
|
end
|
77
|
-
|
78
|
-
success = ca.sign_certs(requested_certnames, input['ttl'])
|
79
|
-
return success ? 0 : 1
|
80
96
|
end
|
81
97
|
|
82
98
|
def get_all_pending_certs(ca)
|
@@ -19,7 +19,6 @@ module Puppetserver
|
|
19
19
|
}
|
20
20
|
|
21
21
|
REVOKE_BODY = JSON.dump({ desired_state: 'revoked' })
|
22
|
-
SIGN_BODY = JSON.dump({ desired_state: 'signed' })
|
23
22
|
|
24
23
|
def initialize(logger, settings)
|
25
24
|
@logger = logger
|
@@ -28,6 +27,15 @@ module Puppetserver
|
|
28
27
|
@ca_port = settings[:ca_port]
|
29
28
|
end
|
30
29
|
|
30
|
+
def server_has_bulk_signing_endpoints
|
31
|
+
url = HttpClient::URL.new('https', @ca_server, @ca_port, 'status', 'v1', 'services')
|
32
|
+
result = @client.with_connection(url) do |connection|
|
33
|
+
connection.get(url)
|
34
|
+
end
|
35
|
+
version = process_results(:server_version, nil, result)
|
36
|
+
return version >= Gem::Version.new('8.4.0')
|
37
|
+
end
|
38
|
+
|
31
39
|
def worst_result(previous_result, current_result)
|
32
40
|
%i{success invalid not_found error}.each do |state|
|
33
41
|
if previous_result == state
|
@@ -61,13 +69,26 @@ module Puppetserver
|
|
61
69
|
end
|
62
70
|
end
|
63
71
|
|
72
|
+
def sign_all
|
73
|
+
return post(resource_type: 'sign',
|
74
|
+
resource_name: 'all',
|
75
|
+
body: '{}',
|
76
|
+
type: :sign_all)
|
77
|
+
end
|
78
|
+
|
79
|
+
def sign_bulk(certnames)
|
80
|
+
return post(resource_type: 'sign',
|
81
|
+
body: "{\"certnames\":#{certnames}}",
|
82
|
+
type: :sign_bulk
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
64
86
|
def sign_certs(certnames, ttl=nil)
|
65
87
|
results = []
|
66
88
|
if ttl
|
67
89
|
lifetime = process_ttl_input(ttl)
|
68
90
|
return false if lifetime.nil?
|
69
|
-
body = JSON.dump({ desired_state: 'signed',
|
70
|
-
cert_ttl: lifetime})
|
91
|
+
body = JSON.dump({ desired_state: 'signed', cert_ttl: lifetime})
|
71
92
|
results = put(certnames,
|
72
93
|
resource_type: 'certificate_status',
|
73
94
|
body: body,
|
@@ -75,7 +96,7 @@ module Puppetserver
|
|
75
96
|
else
|
76
97
|
results = put(certnames,
|
77
98
|
resource_type: 'certificate_status',
|
78
|
-
body:
|
99
|
+
body: JSON.dump({ desired_state: 'signed' }),
|
79
100
|
type: :sign)
|
80
101
|
end
|
81
102
|
|
@@ -119,6 +140,48 @@ module Puppetserver
|
|
119
140
|
end
|
120
141
|
end
|
121
142
|
|
143
|
+
# Make an HTTP POST request to CA
|
144
|
+
# @param endpoint [String] the endpoint to post to for the url
|
145
|
+
# @param body [JSON/String] body of the post request
|
146
|
+
# @param type [Symbol] type of error processing to perform on result
|
147
|
+
# @return [Boolean] whether all requests were successful
|
148
|
+
def post(resource_type:, resource_name: nil, body:, type:, headers: {})
|
149
|
+
url = make_ca_url(resource_type, resource_name)
|
150
|
+
results = @client.with_connection(url) do |connection|
|
151
|
+
result = connection.post(body, url, headers)
|
152
|
+
process_results(type, nil, result)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Handle the result data from the /sign and /sign/all endpoints
|
157
|
+
def process_bulk_sign_result_data(result)
|
158
|
+
data = JSON.parse(result.body)
|
159
|
+
signed = data.dig('signed') || []
|
160
|
+
no_csr = data.dig('no-csr') || []
|
161
|
+
signing_errors = data.dig('signing-errors') || []
|
162
|
+
|
163
|
+
if !signed.empty?
|
164
|
+
@logger.inform "Successfully signed the following certificate requests:"
|
165
|
+
signed.each { |s| @logger.inform " #{s}" }
|
166
|
+
end
|
167
|
+
|
168
|
+
@logger.err 'Error:' if !no_csr.empty? || !signing_errors.empty?
|
169
|
+
if !no_csr.empty?
|
170
|
+
@logger.err ' No certificate request found for the following nodes when attempting to sign:'
|
171
|
+
no_csr.each { |s| @logger.err " #{s}" }
|
172
|
+
end
|
173
|
+
if !signing_errors.empty?
|
174
|
+
@logger.err ' Error encountered when attempting to sign the certificate request for the following nodes:'
|
175
|
+
signing_errors.each { |s| @logger.err " #{s}" }
|
176
|
+
end
|
177
|
+
if no_csr.empty? && signing_errors.empty?
|
178
|
+
@logger.err 'No waiting certificate requests to sign.' if signed.empty?
|
179
|
+
return signed.empty? ? :no_requests : :success
|
180
|
+
else
|
181
|
+
return :error
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
122
185
|
# logs the action and returns true/false for success
|
123
186
|
def process_results(type, certname, result)
|
124
187
|
case type
|
@@ -138,6 +201,50 @@ module Puppetserver
|
|
138
201
|
@logger.err " body: #{result.body.to_s}" if result.body
|
139
202
|
return :error
|
140
203
|
end
|
204
|
+
when :sign_all
|
205
|
+
if result.code == '200'
|
206
|
+
if !result.body
|
207
|
+
@logger.err 'Error:'
|
208
|
+
@logger.err ' Response from /sign/all endpoint did not include a body. Unable to verify certificate requests were signed.'
|
209
|
+
return :error
|
210
|
+
end
|
211
|
+
begin
|
212
|
+
return process_bulk_sign_result_data(result)
|
213
|
+
rescue JSON::ParserError
|
214
|
+
@logger.err 'Error:'
|
215
|
+
@logger.err ' Unable to parse the response from the /sign/all endpoint.'
|
216
|
+
@logger.err " body #{result.body.to_s}"
|
217
|
+
return :error
|
218
|
+
end
|
219
|
+
else
|
220
|
+
@logger.err 'Error:'
|
221
|
+
@logger.err ' When attempting to sign all certificate requests, received:'
|
222
|
+
@logger.err " code: #{result.code}"
|
223
|
+
@logger.err " body: #{result.body.to_s}" if result.body
|
224
|
+
return :error
|
225
|
+
end
|
226
|
+
when :sign_bulk
|
227
|
+
if result.code == '200'
|
228
|
+
if !result.body
|
229
|
+
@logger.err 'Error:'
|
230
|
+
@logger.err ' Response from /sign endpoint did not include a body. Unable to verify certificate requests were signed.'
|
231
|
+
return :error
|
232
|
+
end
|
233
|
+
begin
|
234
|
+
return process_bulk_sign_result_data(result)
|
235
|
+
rescue JSON::ParserError
|
236
|
+
@logger.err 'Error:'
|
237
|
+
@logger.err ' Unable to parse the response from the /sign endpoint.'
|
238
|
+
@logger.err " body #{result.body.to_s}"
|
239
|
+
return :error
|
240
|
+
end
|
241
|
+
else
|
242
|
+
@logger.err 'Error:'
|
243
|
+
@logger.err ' When attempting to sign certificate requests, received:'
|
244
|
+
@logger.err " code: #{result.code}"
|
245
|
+
@logger.err " body: #{result.body.to_s}" if result.body
|
246
|
+
return :error
|
247
|
+
end
|
141
248
|
when :revoke
|
142
249
|
case result.code
|
143
250
|
when '200', '204'
|
@@ -170,6 +277,19 @@ module Puppetserver
|
|
170
277
|
@logger.err " body: #{result.body.to_s}" if result.body
|
171
278
|
return :error
|
172
279
|
end
|
280
|
+
when :server_version
|
281
|
+
if result.code == '200' && result.body
|
282
|
+
begin
|
283
|
+
data = JSON.parse(result.body)
|
284
|
+
version_str = data.dig('ca','service_version')
|
285
|
+
return Gem::Version.new(version_str.match('^\d+\.\d+\.\d+')[0])
|
286
|
+
rescue JSON::ParserError, NoMethodError
|
287
|
+
# If we get bad JSON, version_str is nil, or the matcher doesn't match,
|
288
|
+
# fall through to returning a version of 0.
|
289
|
+
end
|
290
|
+
end
|
291
|
+
@logger.debug 'Could not detect server version. Defaulting to legacy signing endpoints.'
|
292
|
+
return Gem::Version.new(0)
|
173
293
|
end
|
174
294
|
end
|
175
295
|
|
@@ -194,6 +194,9 @@ module Puppetserver
|
|
194
194
|
# class keys/section names but nearly any character values (excluding
|
195
195
|
# leading whitespace) up to one of whitespace, opening curly brace, or
|
196
196
|
# hash sign (Our concern being to capture filesystem path values).
|
197
|
+
#
|
198
|
+
# ca_root and root_ca_name values may include whitespace
|
199
|
+
#
|
197
200
|
# Put values without a section into :main.
|
198
201
|
#
|
199
202
|
# Return Hash of Symbol section names with Symbol setting keys and
|
@@ -205,10 +208,15 @@ module Puppetserver
|
|
205
208
|
case line
|
206
209
|
when /^\s*\[(\w+)\].*/
|
207
210
|
current_section = $1.to_sym
|
208
|
-
when /^\s*(\w+)\s*=\s*([
|
211
|
+
when /^\s*(\w+)\s*=\s*(.+?)\s*(?=[{#]|$)/
|
209
212
|
# Using a Hash with a default key breaks RSpec expectations.
|
210
213
|
res[current_section] ||= {}
|
211
|
-
res[current_section][$1.to_sym] =
|
214
|
+
res[current_section][$1.to_sym] =
|
215
|
+
if [:ca_name, :root_ca_name].include?($1.to_sym)
|
216
|
+
$2
|
217
|
+
else
|
218
|
+
$2.split(' ')[0]
|
219
|
+
end
|
212
220
|
end
|
213
221
|
end
|
214
222
|
|
@@ -130,6 +130,19 @@ module Puppetserver
|
|
130
130
|
Result.new(result.code, result.body)
|
131
131
|
end
|
132
132
|
|
133
|
+
def post(body, url_override = nil, header_overrides = {})
|
134
|
+
url = url_override || @url
|
135
|
+
headers = @default_headers.merge(header_overrides)
|
136
|
+
|
137
|
+
@logger.debug("Making a POST request at #{url.full_url}")
|
138
|
+
|
139
|
+
request = Net::HTTP::Post.new(url.to_uri, headers)
|
140
|
+
request.body = body
|
141
|
+
result = @conn.request(request)
|
142
|
+
|
143
|
+
Result.new(result.code, result.body)
|
144
|
+
end
|
145
|
+
|
133
146
|
def delete(url_override = nil, header_overrides = {})
|
134
147
|
url = url_override || @url
|
135
148
|
headers = @default_headers.merge(header_overrides)
|
@@ -151,7 +164,7 @@ module Puppetserver
|
|
151
164
|
:resource_type, :resource_name, :query) do
|
152
165
|
def full_url
|
153
166
|
url = protocol + '://' + host + ':' + port + '/' +
|
154
|
-
[endpoint, version, resource_type, resource_name].join('/')
|
167
|
+
[endpoint, version, resource_type, resource_name].compact.join('/')
|
155
168
|
|
156
169
|
url = url + "?" + URI.encode_www_form(query) unless query.nil? || query.empty?
|
157
170
|
return url
|
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.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: facter
|
@@ -80,6 +80,7 @@ executables:
|
|
80
80
|
extensions: []
|
81
81
|
extra_rdoc_files: []
|
82
82
|
files:
|
83
|
+
- ".github/dependabot.yml"
|
83
84
|
- ".github/workflows/mend.yaml"
|
84
85
|
- ".gitignore"
|
85
86
|
- ".rspec"
|
@@ -143,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
144
|
- !ruby/object:Gem::Version
|
144
145
|
version: '0'
|
145
146
|
requirements: []
|
146
|
-
rubygems_version: 3.4.
|
147
|
+
rubygems_version: 3.4.20
|
147
148
|
signing_key:
|
148
149
|
specification_version: 4
|
149
150
|
summary: A simple CLI tool for interacting with Puppet Server's Certificate Authority
|