Skip to content

Commit 809d465

Browse files
authored
Merge pull request #9111 from tvpartytonight/maint_7x_mergeup
(maint) 7.x merge up into main
2 parents 90720bd + 63230e6 commit 809d465

File tree

6 files changed

+104
-10
lines changed

6 files changed

+104
-10
lines changed

lib/puppet/application/ssl.rb

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ def help
6060
the CSR. Otherwise a new key pair will be generated. If a CSR has already
6161
been submitted with the given `certname`, then the operation will fail.
6262
63+
* generate_request:
64+
Generate a certificate signing request (CSR). If
65+
a private and public key pair already exist, they will be used to generate
66+
the CSR. Otherwise a new key pair will be generated.
67+
6368
* download_cert:
6469
Download a certificate for this host. If the current private key matches
6570
the downloaded certificate, then the certificate will be saved and used
@@ -137,6 +142,8 @@ def main
137142
unless cert
138143
raise Puppet::Error, _("The certificate for '%{name}' has not yet been signed") % { name: certname }
139144
end
145+
when 'generate_request'
146+
generate_request(certname)
140147
when 'verify'
141148
verify(certname)
142149
when 'clean'
@@ -163,13 +170,7 @@ def show(certname)
163170
def submit_request(ssl_context)
164171
key = @cert_provider.load_private_key(Puppet[:certname])
165172
unless key
166-
if Puppet[:key_type] == 'ec'
167-
Puppet.info _("Creating a new EC SSL key for %{name} using curve %{curve}") % { name: Puppet[:certname], curve: Puppet[:named_curve] }
168-
key = OpenSSL::PKey::EC.generate(Puppet[:named_curve])
169-
else
170-
Puppet.info _("Creating a new SSL key for %{name}") % { name: Puppet[:certname] }
171-
key = OpenSSL::PKey::RSA.new(Puppet[:keylength].to_i)
172-
end
173+
key = create_key(Puppet[:certname])
173174
@cert_provider.save_private_key(Puppet[:certname], key)
174175
end
175176

@@ -188,6 +189,20 @@ def submit_request(ssl_context)
188189
raise Puppet::Error.new(_("Failed to submit certificate request: %{message}") % { message: e.message }, e)
189190
end
190191

192+
def generate_request(certname)
193+
key = @cert_provider.load_private_key(certname)
194+
unless key
195+
key = create_key(certname)
196+
@cert_provider.save_private_key(certname, key)
197+
end
198+
199+
csr = @cert_provider.create_request(certname, key)
200+
@cert_provider.save_request(certname, csr)
201+
Puppet.notice _("Generated certificate request in '%{path}'") % { path: @cert_provider.to_path(Puppet[:requestdir], certname) }
202+
rescue => e
203+
raise Puppet::Error.new(_("Failed to generate certificate request: %{message}") % { message: e.message }, e)
204+
end
205+
191206
def download_cert(ssl_context)
192207
key = @cert_provider.load_private_key(Puppet[:certname])
193208

@@ -286,4 +301,14 @@ def fingerprint(cert)
286301
def create_route(ssl_context)
287302
@session.route_to(:ca, ssl_context: ssl_context)
288303
end
304+
305+
def create_key(certname)
306+
if Puppet[:key_type] == 'ec'
307+
Puppet.info _("Creating a new EC SSL key for %{name} using curve %{curve}") % { name: certname, curve: Puppet[:named_curve] }
308+
OpenSSL::PKey::EC.generate(Puppet[:named_curve])
309+
else
310+
Puppet.info _("Creating a new SSL key for %{name}") % { name: certname }
311+
OpenSSL::PKey::RSA.new(Puppet[:keylength].to_i)
312+
end
313+
end
289314
end

lib/puppet/x509/cert_provider.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,13 +376,17 @@ def load_request_from_pem(pem)
376376
OpenSSL::X509::Request.new(pem)
377377
end
378378

379-
private
380-
379+
# Return the path to the cert related object (key, CSR, cert, etc).
380+
#
381+
# @param base [String] base directory
382+
# @param name [String] the name associated with the cert related object
381383
def to_path(base, name)
382384
raise _("Certname %{name} must not contain unprintable or non-ASCII characters") % { name: name.inspect } unless name =~ VALID_CERTNAME
383385
File.join(base, "#{name.downcase}.pem")
384386
end
385387

388+
private
389+
386390
def permissions_for_setting(name)
387391
setting = Puppet.settings.setting(name)
388392
perm = { mode: setting.mode.to_i(8) }

man/man8/puppet-ssl.8

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ submit_request
4242
Generate a certificate signing request (CSR) and submit it to the CA\. If a private and public key pair already exist, they will be used to generate the CSR\. Otherwise a new key pair will be generated\. If a CSR has already been submitted with the given \fBcertname\fR, then the operation will fail\.
4343
.
4444
.TP
45+
generate_request
46+
Generate a certificate signing request (CSR)\. If a private and public key pair already exist, they will be used to generate the CSR\. Otherwise a new key pair will be generated\.
47+
.
48+
.TP
4549
download_cert
4650
Download a certificate for this host\. If the current private key matches the downloaded certificate, then the certificate will be saved and used for subsequent requests\. If there is already an existing certificate, it will be overwritten\.
4751
.

spec/unit/application/ssl_spec.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,50 @@ def expects_command_to_fail(message)
171171
end
172172
end
173173

174+
context 'when generating a CSR' do
175+
let(:csr_path) { Puppet[:hostcsr] }
176+
let(:requestdir) { Puppet[:requestdir] }
177+
178+
before do
179+
ssl.command_line.args << 'generate_request'
180+
end
181+
182+
it 'generates an RSA private key' do
183+
File.unlink(Puppet[:hostprivkey])
184+
185+
expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})
186+
end
187+
188+
it 'generates an EC private key' do
189+
Puppet[:key_type] = 'ec'
190+
File.unlink(Puppet[:hostprivkey])
191+
192+
expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})
193+
end
194+
195+
it 'registers OIDs' do
196+
expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)
197+
198+
expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})
199+
end
200+
201+
it 'saves the CSR locally' do
202+
expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})
203+
204+
expect(Puppet::FileSystem).to be_exist(csr_path)
205+
end
206+
207+
it 'accepts dns alt names' do
208+
Puppet[:dns_alt_names] = 'majortom'
209+
210+
expects_command_to_pass
211+
212+
csr = Puppet::SSL::CertificateRequest.new(name)
213+
csr.read(csr_path)
214+
expect(csr.subject_alt_names).to include('DNS:majortom')
215+
end
216+
end
217+
174218
context 'when downloading a certificate' do
175219
before do
176220
ssl.command_line.args << 'download_cert'
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
require 'spec_helper'
2+
3+
describe Puppet::SSL::CertificateSigner do
4+
include PuppetSpec::Files
5+
6+
let(:wrong_key) { OpenSSL::PKey::RSA.new(512) }
7+
let(:client_cert) { cert_fixture('signed.pem') }
8+
9+
# jruby-openssl >= 0.13.0 (JRuby >= 9.3.5.0) raises an error when signing a
10+
# certificate when there is a discrepancy between the certificate and key.
11+
it 'raises if client cert signature is invalid', if: Puppet::Util::Platform.jruby? && RUBY_VERSION.to_f >= 2.6 do
12+
expect {
13+
client_cert.sign(wrong_key, OpenSSL::Digest::SHA256.new)
14+
}.to raise_error(OpenSSL::X509::CertificateError,
15+
'invalid public key data')
16+
end
17+
end

spec/unit/ssl/ssl_provider_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@
338338
end
339339
end
340340

341-
it 'raises if intermediate CA signature is invalid' do
341+
it 'raises if intermediate CA signature is invalid', unless: Puppet::Util::Platform.jruby? && RUBY_VERSION.to_f >= 2.6 do
342342
int = global_cacerts.last
343343
int.public_key = wrong_key.public_key if Puppet::Util::Platform.jruby?
344344
int.sign(wrong_key, OpenSSL::Digest::SHA256.new)

0 commit comments

Comments
 (0)