Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(PUP-10589) Add a generate_request option to puppet ssl #9095

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions lib/puppet/application/ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,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 @@ -188,6 +193,26 @@ def submit_request(ssl_context)
raise Puppet::Error.new(_("Failed to submit certificate request: %{message}") % { message: e.message }, e)
end

def generate_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
@cert_provider.save_private_key(Puppet[:certname], key)
end

csr = @cert_provider.create_request(Puppet[:certname], key)
@cert_provider.save_request(Puppet[:certname], csr)
Puppet.notice _("Generated certificate request for '%{name}' at %{requestdir}") % { name: Puppet[:certname], requestdir: Puppet[:requestdir] }
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
4 changes: 4 additions & 0 deletions man/man8/puppet-ssl.8
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ submit_request
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\.
.
.TP
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\.
.
.TP
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 for subsequent requests\. If there is already an existing certificate, it will be overwritten\.
.
Expand Down
70 changes: 70 additions & 0 deletions spec/unit/application/ssl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,76 @@ 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_behaves_like 'an ssl action'

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

expects_command_to_pass(%r{Generated certificate request for '#{name}' at #{requestdir}})
end

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

expects_command_to_pass(%r{Generated certificate request for '#{name}' at #{requestdir}})
end

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

expects_command_to_pass(%r{Generated certificate request for '#{name}' at #{requestdir}})
end

it 'saves the CSR locally' do
expects_command_to_pass(%r{Generated certificate request for '#{name}' at #{requestdir}})

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

it 'detects when a CSR with the same public key has already been submitted' do
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)

expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})

stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)

expects_command_to_pass
end

it 'downloads the certificate when autosigning is enabled' do
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)

expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})

expect(Puppet::FileSystem).to be_exist(Puppet[:hostcert])
expect(Puppet::FileSystem).to_not be_exist(csr_path)
end

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

stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)

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