Skip to content

Commit

Permalink
Allow passing a CA key password when signing a cert
Browse files Browse the repository at this point in the history
  • Loading branch information
ehelms committed May 21, 2024
1 parent dcd89b5 commit 4dba9c5
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 41 deletions.
14 changes: 14 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

##### <a name="-openssl--certificate--x509--ensure"></a>`ensure`

Expand Down Expand Up @@ -648,6 +649,14 @@ provided.

Default value: `undef`

##### <a name="-openssl--certificate--x509--cakey--password"></a>`cakey_password`

Data type: `Optional[Variant[Sensitive, String]]`

Optional password that has encrypted the CA key.

Default value: `undef`

### <a name="openssl--config"></a>`openssl::config`

Generates an openssl.conf file using defaults
Expand Down Expand Up @@ -1258,6 +1267,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)
Expand All @@ -1284,6 +1294,10 @@ The optional ca certificate filepath

The optional ca private key filepath

##### <a name="-x509_cert--cakey--password"></a>`cakey_password`

The optional ca private key password

##### <a name="-x509_cert--csr"></a>`csr`

The optional certificate signing request path
Expand Down
8 changes: 7 additions & 1 deletion lib/puppet/provider/x509_cert/openssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,13 @@ def create
'-out', resource[:path]
]
end
options << ['-passin', "pass:#{resource[:password]}"] if resource[:password]
if resource[:cakey_password]
password = resource[:cakey_password]
elsif resource[:password]
password = resource[:password]
end

options << ['-passin', "pass:#{password}"] if password
options << ['-extensions', 'v3_req'] if resource[:req_ext] != :false
openssl options
end
Expand Down
4 changes: 4 additions & 0 deletions lib/puppet/type/x509_cert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,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
Expand Down
84 changes: 44 additions & 40 deletions manifests/certificate/x509.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand All @@ -116,37 +118,38 @@
# and set $key_mode to '0640'.
#
define openssl::certificate::x509 (
Enum['present', 'absent'] $ensure = present,
Optional[String] $country = undef,
Optional[String] $organization = undef,
Optional[String] $unit = undef,
Optional[String] $state = undef,
Optional[String] $commonname = undef,
Optional[String] $locality = undef,
Array $altnames = [],
Array $extkeyusage = [],
Optional[String] $email = undef,
Integer $days = 365,
Stdlib::Absolutepath $base_dir = '/etc/ssl/certs',
Stdlib::Absolutepath $cnf_dir = $base_dir,
Stdlib::Absolutepath $crt_dir = $base_dir,
Stdlib::Absolutepath $csr_dir = $base_dir,
Stdlib::Absolutepath $key_dir = $base_dir,
Stdlib::Absolutepath $cnf = "${cnf_dir}/${name}.cnf",
Stdlib::Absolutepath $crt = "${crt_dir}/${name}.crt",
Stdlib::Absolutepath $csr = "${csr_dir}/${name}.csr",
Stdlib::Absolutepath $key = "${key_dir}/${name}.key",
Integer $key_size = 3072,
Variant[String, Integer] $owner = 'root',
Variant[String, Integer] $group = 'root',
Variant[String, Integer] $key_owner = $owner,
Variant[String, Integer] $key_group = $group,
Stdlib::Filemode $key_mode = '0600',
Optional[String] $password = undef,
Boolean $force = true,
Boolean $encrypted = true,
Optional[Stdlib::Absolutepath] $ca = undef,
Optional[Stdlib::Absolutepath] $cakey = undef,
Enum['present', 'absent'] $ensure = present,
Optional[String] $country = undef,
Optional[String] $organization = undef,
Optional[String] $unit = undef,
Optional[String] $state = undef,
Optional[String] $commonname = undef,
Optional[String] $locality = undef,
Array $altnames = [],
Array $extkeyusage = [],
Optional[String] $email = undef,
Integer $days = 365,
Stdlib::Absolutepath $base_dir = '/etc/ssl/certs',
Stdlib::Absolutepath $cnf_dir = $base_dir,
Stdlib::Absolutepath $crt_dir = $base_dir,
Stdlib::Absolutepath $csr_dir = $base_dir,
Stdlib::Absolutepath $key_dir = $base_dir,
Stdlib::Absolutepath $cnf = "${cnf_dir}/${name}.cnf",
Stdlib::Absolutepath $crt = "${crt_dir}/${name}.crt",
Stdlib::Absolutepath $csr = "${csr_dir}/${name}.csr",
Stdlib::Absolutepath $key = "${key_dir}/${name}.key",
Integer $key_size = 3072,
Variant[String, Integer] $owner = 'root',
Variant[String, Integer] $group = 'root',
Variant[String, Integer] $key_owner = $owner,
Variant[String, Integer] $key_group = $group,
Stdlib::Filemode $key_mode = '0600',
Optional[String] $password = undef,
Boolean $force = true,
Boolean $encrypted = true,
Optional[Stdlib::Absolutepath] $ca = undef,
Optional[Stdlib::Absolutepath] $cakey = undef,
Optional[Variant[Sensitive, String]] $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.')
Expand Down Expand Up @@ -179,15 +182,16 @@
encrypted => $encrypted,
}
~> x509_cert { $crt:
ensure => $ensure,
template => $cnf,
csr => $csr,
days => $days,
password => $password,
req_ext => !empty($altnames) and !empty($extkeyusage),
force => $force,
ca => $ca,
cakey => $cakey,
ensure => $ensure,
template => $cnf,
csr => $csr,
days => $days,
password => $password,
req_ext => !empty($altnames) and !empty($extkeyusage),
force => $force,
ca => $ca,
cakey => $cakey,
cakey_password => $cakey_password,
}

# Set owner of all files
Expand Down
107 changes: 107 additions & 0 deletions spec/defines/openssl_certificate_x509_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -524,4 +524,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: '[email protected]',
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
23 changes: 23 additions & 0 deletions spec/unit/puppet/provider/x509_cert/openssl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 4dba9c5

Please sign in to comment.