diff --git a/REFERENCE.md b/REFERENCE.md index 0632859..fa4a9b7 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -369,6 +369,7 @@ The following parameters are available in the `openssl::certificate::x509` defin * [`encrypted`](#-openssl--certificate--x509--encrypted) * [`ca`](#-openssl--certificate--x509--ca) * [`cakey`](#-openssl--certificate--x509--cakey) +* [`cakey_password`](#-openssl--certificate--x509--cakey_password) ##### `ensure` @@ -648,6 +649,14 @@ provided. Default value: `undef` +##### `cakey_password` + +Data type: `Optional[Variant[Sensitive[String[1]], String[1]]]` + +Optional password that has encrypted the CA key. + +Default value: `undef` + ### `openssl::config` Generates an openssl.conf file using defaults @@ -1312,6 +1321,7 @@ The following parameters are available in the `x509_cert` type. * [`authentication`](#-x509_cert--authentication) * [`ca`](#-x509_cert--ca) * [`cakey`](#-x509_cert--cakey) +* [`cakey_password`](#-x509_cert--cakey_password) * [`csr`](#-x509_cert--csr) * [`days`](#-x509_cert--days) * [`force`](#-x509_cert--force) @@ -1338,6 +1348,10 @@ The optional ca certificate filepath The optional ca private key filepath +##### `cakey_password` + +The optional CA key password + ##### `csr` The optional certificate signing request path diff --git a/lib/puppet/provider/x509_cert/openssl.rb b/lib/puppet/provider/x509_cert/openssl.rb index e5262a7..997d7da 100644 --- a/lib/puppet/provider/x509_cert/openssl.rb +++ b/lib/puppet/provider/x509_cert/openssl.rb @@ -99,7 +99,10 @@ def create '-out', resource[:path] ] end - options << ['-passin', "pass:#{resource[:password]}"] if resource[:password] + + password = resource[:cakey_password] || resource[:password] + + options << ['-passin', "pass:#{password}"] if password options << ['-extensions', 'v3_req'] if resource[:req_ext] != :false openssl options end diff --git a/lib/puppet/type/x509_cert.rb b/lib/puppet/type/x509_cert.rb index be58061..68ea9b1 100644 --- a/lib/puppet/type/x509_cert.rb +++ b/lib/puppet/type/x509_cert.rb @@ -78,6 +78,10 @@ desc 'The optional ca private key filepath' end + newparam(:cakey_password) do + desc 'The optional CA key password' + end + autorequire(:file) do self[:template] end diff --git a/manifests/certificate/x509.pp b/manifests/certificate/x509.pp index 2aae7c4..3d8a1f1 100644 --- a/manifests/certificate/x509.pp +++ b/manifests/certificate/x509.pp @@ -93,6 +93,8 @@ # @param cakey # Path to CA private key for signing. Undef mean no CAkey will be # provided. +# @param cakey_password +# Optional password that has encrypted the CA key. # # @example basic usage # @@ -147,6 +149,7 @@ Boolean $encrypted = true, Optional[Stdlib::Absolutepath] $ca = undef, Optional[Stdlib::Absolutepath] $cakey = undef, + Optional[Variant[Sensitive[String[1]], String[1]]] $cakey_password = undef, ) { unless $country or $organization or $unit or $state or $commonname { fail('At least one of $country, $organization, $unit, $state or $commonname is required.') @@ -179,15 +182,16 @@ encrypted => $encrypted, } ~> x509_cert { $crt: - ensure => $ensure, - template => $cnf, - csr => $csr, - days => $days, - password => $password, - req_ext => !empty($altnames) or !empty($extkeyusage), - force => $force, - ca => $ca, - cakey => $cakey, + ensure => $ensure, + template => $cnf, + csr => $csr, + days => $days, + password => $password, + req_ext => !empty($altnames) or !empty($extkeyusage), + force => $force, + ca => $ca, + cakey => $cakey, + cakey_password => $cakey_password, } # Set owner of all files diff --git a/metadata.json b/metadata.json index 50a472f..a8afb9a 100644 --- a/metadata.json +++ b/metadata.json @@ -36,6 +36,20 @@ "9" ] }, + { + "operatingsystem": "OracleLinux", + "operatingsystemrelease": [ + "8", + "9" + ] + }, + { + "operatingsystem": "Rocky", + "operatingsystemrelease": [ + "8", + "9" + ] + }, { "operatingsystem": "AlmaLinux", "operatingsystemrelease": [ diff --git a/spec/defines/openssl_certificate_x509_spec.rb b/spec/defines/openssl_certificate_x509_spec.rb index c3b42d3..e5fe21b 100644 --- a/spec/defines/openssl_certificate_x509_spec.rb +++ b/spec/defines/openssl_certificate_x509_spec.rb @@ -604,4 +604,111 @@ ) } end + + context 'when passing CA properties' do + let(:params) do + { + country: 'com', + organization: 'bar', + commonname: 'baz', + state: 'FR', + locality: 'here', + unit: 'braz', + altnames: ['a.com', 'b.com', 'c.com'], + extkeyusage: %w[serverAuth clientAuth], + email: 'contact@foo.com', + days: 4567, + key_size: 4096, + owner: 'www-data', + ca: '/etc/pki/ca.crt', + cakey: '/etc/pki/ca.key', + cakey_password: '5r$}^', + force: false, + base_dir: '/tmp/foobar', + } + end + + it { + is_expected.to contain_file('/tmp/foobar/foo.cnf').with( + ensure: 'present', + owner: 'www-data' + ).with_content( + %r{countryName\s+=\s+com} + ).with_content( + %r{stateOrProvinceName\s+=\s+FR} + ).with_content( + %r{localityName\s+=\s+here} + ).with_content( + %r{organizationName\s+=\s+bar} + ).with_content( + %r{organizationalUnitName\s+=\s+braz} + ).with_content( + %r{commonName\s+=\s+baz} + ).with_content( + %r{emailAddress\s+=\s+contact@foo\.com} + ).with_content( + %r{extendedKeyUsage\s+=\s+serverAuth,\s+clientAuth} + ).with_content( + %r{subjectAltName\s+=\s+@alt_names} + ).with_content( + %r{DNS\.0\s+=\s+a\.com} + ).with_content( + %r{DNS\.1\s+=\s+b\.com} + ).with_content( + %r{DNS\.2\s+=\s+c\.com} + ) + } + + it { + is_expected.to contain_ssl_pkey('/tmp/foobar/foo.key').with( + ensure: 'present', + password: nil, + size: 4096 + ) + } + + it { + is_expected.to contain_x509_cert('/tmp/foobar/foo.crt').with( + ensure: 'present', + template: '/tmp/foobar/foo.cnf', + csr: '/tmp/foobar/foo.csr', + days: 4567, + ca: '/etc/pki/ca.crt', + cakey: '/etc/pki/ca.key', + cakey_password: '5r$}^', + force: false + ) + } + + it { + is_expected.to contain_x509_request('/tmp/foobar/foo.csr').with( + ensure: 'present', + template: '/tmp/foobar/foo.cnf', + private_key: '/tmp/foobar/foo.key', + password: nil, + force: false + ) + } + + it { + is_expected.to contain_file('/tmp/foobar/foo.key').with( + ensure: 'present', + owner: 'www-data' + ) + } + + it { + is_expected.to contain_file('/tmp/foobar/foo.crt').with( + ensure: 'present', + owner: 'www-data' + ) + } + + it { + is_expected.to contain_file('/tmp/foobar/foo.csr').with( + ensure: 'present', + owner: 'www-data' + ) + } + end end diff --git a/spec/unit/puppet/provider/x509_cert/openssl_spec.rb b/spec/unit/puppet/provider/x509_cert/openssl_spec.rb index 88b42de..8a11059 100644 --- a/spec/unit/puppet/provider/x509_cert/openssl_spec.rb +++ b/spec/unit/puppet/provider/x509_cert/openssl_spec.rb @@ -79,6 +79,29 @@ end end + context 'when using a CA for signing with a password' 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' + resource[:cakey_password] = '5i;6%' + expect(provider_class).to receive(:openssl).with([ + 'x509', + '-req', + '-days', 3650, + '-in', '/tmp/foo.csr', + '-out', '/tmp/foo.crt', + ['-extfile', '/tmp/foo.cnf'], + ['-CAcreateserial'], + ['-CA', '/tmp/foo-ca.crt'], + ['-CAkey', '/tmp/foo-ca.key'], + ['-passin', 'pass:5i;6%'], + ['-extensions', 'v3_req'] + ]) + 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