Skip to content

Commit

Permalink
Merge pull request #9107 from joshcooper/nickg_generate_request
Browse files Browse the repository at this point in the history
(PUP-10589) Add a generate_request option to puppet ssl
  • Loading branch information
cthorn42 committed Sep 15, 2023
2 parents 0a3ced0 + ebef1ef commit f334eb9
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 9 deletions.
39 changes: 32 additions & 7 deletions lib/puppet/application/ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ def help
the CSR. Otherwise a new key pair will be generated. If a CSR has already
been submitted with the given `certname`, then the operation will fail.
* generate_request:
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.
* download_cert:
Download a certificate for this host. If the current private key matches
the downloaded certificate, then the certificate will be saved and used
Expand Down Expand Up @@ -136,6 +141,8 @@ def main
unless cert
raise Puppet::Error, _("The certificate for '%{name}' has not yet been signed") % { name: certname }
end
when 'generate_request'
generate_request(certname)
when 'verify'
verify(certname)
when 'clean'
Expand All @@ -162,13 +169,7 @@ def show(certname)
def submit_request(ssl_context)
key = @cert_provider.load_private_key(Puppet[:certname])
unless key
if Puppet[:key_type] == 'ec'
Puppet.info _("Creating a new EC SSL key for %{name} using curve %{curve}") % { name: Puppet[:certname], curve: Puppet[:named_curve] }
key = OpenSSL::PKey::EC.generate(Puppet[:named_curve])
else
Puppet.info _("Creating a new SSL key for %{name}") % { name: Puppet[:certname] }
key = OpenSSL::PKey::RSA.new(Puppet[:keylength].to_i)
end
key = create_key(Puppet[:certname])
@cert_provider.save_private_key(Puppet[:certname], key)
end

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

def generate_request(certname)
key = @cert_provider.load_private_key(certname)
unless key
key = create_key(certname)
@cert_provider.save_private_key(certname, key)
end

csr = @cert_provider.create_request(certname, key)
@cert_provider.save_request(certname, csr)
Puppet.notice _("Generated certificate request in '%{path}'") % { path: @cert_provider.to_path(Puppet[:requestdir], certname) }
rescue => e
raise Puppet::Error.new(_("Failed to generate certificate request: %{message}") % { message: e.message }, e)
end

def download_cert(ssl_context)
key = @cert_provider.load_private_key(Puppet[:certname])

Expand Down Expand Up @@ -285,4 +300,14 @@ def fingerprint(cert)
def create_route(ssl_context)
@session.route_to(:ca, ssl_context: ssl_context)
end

def create_key(certname)
if Puppet[:key_type] == 'ec'
Puppet.info _("Creating a new EC SSL key for %{name} using curve %{curve}") % { name: certname, curve: Puppet[:named_curve] }
OpenSSL::PKey::EC.generate(Puppet[:named_curve])
else
Puppet.info _("Creating a new SSL key for %{name}") % { name: certname }
OpenSSL::PKey::RSA.new(Puppet[:keylength].to_i)
end
end
end
8 changes: 6 additions & 2 deletions lib/puppet/x509/cert_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -346,13 +346,17 @@ def load_request_from_pem(pem)
OpenSSL::X509::Request.new(pem)
end

private

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

private

def permissions_for_setting(name)
setting = Puppet.settings.setting(name)
perm = { mode: setting.mode.to_i(8) }
Expand Down
44 changes: 44 additions & 0 deletions spec/unit/application/ssl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,50 @@ def expects_command_to_fail(message)
end
end

context 'when generating a CSR' do
let(:csr_path) { Puppet[:hostcsr] }
let(:requestdir) { Puppet[:requestdir] }

before do
ssl.command_line.args << 'generate_request'
end

it 'generates an RSA private key' do
File.unlink(Puppet[:hostprivkey])

expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})
end

it 'generates an EC private key' do
Puppet[:key_type] = 'ec'
File.unlink(Puppet[:hostprivkey])

expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})
end

it 'registers OIDs' do
expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)

expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})
end

it 'saves the CSR locally' do
expects_command_to_pass(%r{Generated certificate request in '#{csr_path}'})

expect(Puppet::FileSystem).to be_exist(csr_path)
end

it 'accepts dns alt names' do
Puppet[:dns_alt_names] = 'majortom'

expects_command_to_pass

csr = Puppet::SSL::CertificateRequest.new(name)
csr.read(csr_path)
expect(csr.subject_alt_names).to include('DNS:majortom')
end
end

context 'when downloading a certificate' do
before do
ssl.command_line.args << 'download_cert'
Expand Down

0 comments on commit f334eb9

Please sign in to comment.