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