puppetserver-ca 2.6.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|