diff --git a/REFERENCE.md b/REFERENCE.md index d7a340ef..8dcb94a6 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -368,6 +368,8 @@ The following parameters are available in the `openssl::certificate::x509` defin * [`csr`](#-openssl--certificate--x509--csr) * [`key`](#-openssl--certificate--x509--key) * [`encrypted`](#-openssl--certificate--x509--encrypted) +* [`ca`](#-openssl--certificate--x509--ca) +* [`cakey`](#-openssl--certificate--x509--cakey) ##### `ensure` @@ -631,6 +633,24 @@ Defaults to true (key is encrypted) Default value: `true` +##### `ca` + +Data type: `Optional[Stdlib::Absolutepath]` + +Path to CA certificate for signing. Undef means no CA will be +provided for signing the certificate. + +Default value: `undef` + +##### `cakey` + +Data type: `Optional[Stdlib::Absolutepath]` + +Path to CA private key for signing. Undef mean no CAkey will be +provided. + +Default value: `undef` + ### `openssl::config` Generates an openssl.conf file using defaults @@ -1233,6 +1253,9 @@ Default value: `present` The following parameters are available in the `x509_cert` type. * [`authentication`](#-x509_cert--authentication) +* [`ca`](#-x509_cert--ca) +* [`cakey`](#-x509_cert--cakey) +* [`csr`](#-x509_cert--csr) * [`days`](#-x509_cert--days) * [`force`](#-x509_cert--force) * [`password`](#-x509_cert--password) @@ -1250,6 +1273,18 @@ The authentication algorithm: 'rsa', 'dsa or ec' Default value: `rsa` +##### `ca` + +The optional ca certificate filepath + +##### `cakey` + +The optional ca private key filepath + +##### `csr` + +The optional certificate signing request path + ##### `days` Valid values: `%r{\d+}` diff --git a/lib/puppet/provider/x509_cert/openssl.rb b/lib/puppet/provider/x509_cert/openssl.rb index 1bf3f8c5..f1c40f16 100644 --- a/lib/puppet/provider/x509_cert/openssl.rb +++ b/lib/puppet/provider/x509_cert/openssl.rb @@ -67,14 +67,29 @@ def exists? end def create - options = [ - 'req', - '-config', resource[:template], - '-new', '-x509', - '-days', resource[:days], - '-key', resource[:private_key], - '-out', resource[:path] - ] + if resource[:csr] + options = [ + 'x509', + '-req', + '-days', resource[:days], + '-in', resource[:csr], + '-out', resource[:path] + ] + if resource[:ca] + options << ['-CAcreateserial'] + options << ['-CA', resource[:ca]] + options << ['-CAkey', resource[:cakey]] + end + else + options = [ + 'req', + '-config', resource[:template], + '-new', '-x509', + '-days', resource[:days], + '-key', resource[:private_key], + '-out', resource[:path] + ] + end options << ['-passin', "pass:#{resource[:password]}"] if resource[:password] options << ['-extensions', 'req_ext'] if resource[:req_ext] != :false openssl options diff --git a/lib/puppet/type/x509_cert.rb b/lib/puppet/type/x509_cert.rb index 32bb216c..0edbffc3 100644 --- a/lib/puppet/type/x509_cert.rb +++ b/lib/puppet/type/x509_cert.rb @@ -66,6 +66,18 @@ defaultto :rsa end + newparam(:csr) do + desc 'The optional certificate signing request path' + end + + newparam(:ca) do + desc 'The optional ca certificate filepath' + end + + newparam(:cakey) do + desc 'The optional ca private key filepath' + end + autorequire(:file) do self[:template] end diff --git a/manifests/certificate/x509.pp b/manifests/certificate/x509.pp index 82ef2978..44b5d276 100644 --- a/manifests/certificate/x509.pp +++ b/manifests/certificate/x509.pp @@ -89,6 +89,12 @@ # specifying the -nodes option during the CSR generation. Turning # off encryption is needed by some applications, such as OpenLDAP. # Defaults to true (key is encrypted) +# @param ca +# Path to CA certificate for signing. Undef means no CA will be +# provided for signing the certificate. +# @param cakey +# Path to CA private key for signing. Undef mean no CAkey will be +# provided. # # @example basic usage # @@ -142,6 +148,8 @@ Boolean $force = true, String $cnf_tpl = 'openssl/cert.cnf.erb', Boolean $encrypted = true, + Optional[Stdlib::Absolutepath] $ca = undef, + Optional[Stdlib::Absolutepath] $cakey = undef, ) { $_key_owner = pick($key_owner, $owner) $_key_group = pick($key_group, $group) @@ -182,6 +190,9 @@ req_ext => $req_ext, force => $force, require => File[$_cnf], + ca => $ca, + cakey => $cakey, + csr => $csr, } x509_request { $_csr: diff --git a/spec/unit/puppet/provider/x509_cert/openssl_spec.rb b/spec/unit/puppet/provider/x509_cert/openssl_spec.rb index 40fd8585..7cc759c7 100644 --- a/spec/unit/puppet/provider/x509_cert/openssl_spec.rb +++ b/spec/unit/puppet/provider/x509_cert/openssl_spec.rb @@ -27,7 +27,10 @@ it 'creates a certificate with the proper options' do expect(provider_class).to receive(:openssl).with([ - 'req', '-config', '/tmp/foo.cnf', '-new', '-x509', + 'req', + '-config', '/tmp/foo.cnf', + '-new', + '-x509', '-days', 3650, '-key', '/tmp/foo.key', '-out', '/tmp/foo.crt', @@ -40,7 +43,10 @@ it 'creates a certificate with the proper options' do resource[:password] = '2x6${' expect(provider_class).to receive(:openssl).with([ - 'req', '-config', '/tmp/foo.cnf', '-new', '-x509', + 'req', + '-config', '/tmp/foo.cnf', + '-new', + '-x509', '-days', 3650, '-key', '/tmp/foo.key', '-out', '/tmp/foo.crt', @@ -52,6 +58,26 @@ end end + context 'when using a CA for signing' do + it 'creates a certificate with the proper options' do + resource[:csr] = '/tmp/foo.csr' + resource[:ca] = '/tmp/foo-ca.crt' + resource[:cakey] = '/tmp/foo-ca.key' + expect(provider_class).to receive(:openssl).with([ + 'x509', + '-req', + '-days', 3650, + '-in', '/tmp/foo.csr', + '-out', '/tmp/foo.crt', + ['-CAcreateserial'], + ['-CA', '/tmp/foo-ca.crt'], + ['-CAkey', '/tmp/foo-ca.key'], + ['-extensions', 'req_ext'] + ]) + resource.provider.create + end + end + context 'when forcing key' do it 'exists? should return true if certificate exists and is synced' do resource[:force] = true diff --git a/spec/unit/puppet/type/x509_cert_spec.rb b/spec/unit/puppet/type/x509_cert_spec.rb index ad484a0f..649e3353 100644 --- a/spec/unit/puppet/type/x509_cert_spec.rb +++ b/spec/unit/puppet/type/x509_cert_spec.rb @@ -79,4 +79,9 @@ resource[:authentication] = :foo end.to raise_error(Puppet::Error, %r{Invalid value :foo}) end + + it 'accepts a valid csr parameter' do + resource[:csr] = '/tmp/foo.csr' + expect(resource[:csr]).to eq('/tmp/foo.csr') + end end