openssl_csr: added support for the OCSP Must Staple extension (#35082)

* Added support for the OCSP Must Staple extension.

* Trying to clean up magic constants a bit.
This commit is contained in:
Felix Fontein 2018-02-08 13:03:28 +01:00 committed by John R Barker
parent 273a3d1d51
commit d1f19125a5
3 changed files with 93 additions and 5 deletions

View file

@ -22,7 +22,8 @@ short_description: Generate OpenSSL Certificate Signing Request (CSR)
description:
- "This module allows one to (re)generate OpenSSL certificate signing requests.
It uses the pyOpenSSL python library to interact with openssl. This module supports
the subjectAltName as well as the keyUsage and extendedKeyUsage extensions."
the subjectAltName, keyUsage, extendedKeyUsage, basicConstraints and OCSP Must Staple
extensions."
requirements:
- "python-pyOpenSSL >= 0.15"
options:
@ -148,12 +149,29 @@ options:
description:
- Should the basicConstraints extension be considered as critical
version_added: 2.5
ocsp_must_staple:
required: false
aliases: ['ocspMustStaple']
description:
- Indicates that the certificate should contain the OCSP Must Staple
extension (U(https://tools.ietf.org/html/rfc7633)).
version_added: 2.5
ocsp_must_staple_critical:
required: false
aliases: [ 'ocspMustStaple_critical' ]
description:
- Should the OCSP Must Staple extension be considered as critical
- "Warning: according to the RFC, this extension should not be marked
as critical, as old clients not knowing about OCSP Must Staple
are required to reject such certificates
(see U(https://tools.ietf.org/html/rfc7633#section-4))."
version_added: 2.5
extends_documentation_fragment: files
notes:
- "If the certificate signing request already exists it will be checked whether subjectAltName,
keyUsage and extendedKeyUsage only contain the requested values and if the request was signed
by the given private key"
keyUsage, extendedKeyUsage and basicConstraints only contain the requested values, whether
OCSP Must Staple is as requested, and if the request was signed by the given private key."
'''
@ -204,6 +222,13 @@ EXAMPLES = '''
- keyAgreement
extended_key_usage:
- clientAuth
# Generate an OpenSSL Certificate Signing Request with OCSP Must Staple
- openssl_csr:
path: /etc/ssl/csr/www.ansible.com.csr
privatekey_path: /etc/ssl/private/ansible.com.pem
common_name: www.ansible.com
ocsp_must_staple: true
'''
@ -243,6 +268,12 @@ basicConstraints:
returned: changed or success
type: list
sample: ['CA:TRUE', 'pathLenConstraint:0']
ocsp_must_staple:
description: Indicates whether the certificate has the OCSP
Must Staple feature enabled
returned: changed or success
type: bool
sample: false
'''
import os
@ -258,6 +289,14 @@ except ImportError:
pyopenssl_found = False
else:
pyopenssl_found = True
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
# OpenSSL 1.1.0 or newer
MUST_STAPLE_NAME = b"tlsfeature"
MUST_STAPLE_VALUE = b"status_request"
else:
# OpenSSL 1.0.x or older
MUST_STAPLE_NAME = b"1.3.6.1.5.5.7.1.24"
MUST_STAPLE_VALUE = b"DER:30:03:02:01:05"
class CertificateSigningRequestError(crypto_utils.OpenSSLObjectError):
@ -285,6 +324,8 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
self.extendedKeyUsage_critical = module.params['extendedKeyUsage_critical']
self.basicConstraints = module.params['basicConstraints']
self.basicConstraints_critical = module.params['basicConstraints_critical']
self.ocspMustStaple = module.params['ocspMustStaple']
self.ocspMustStaple_critical = module.params['ocspMustStaple_critical']
self.request = None
self.privatekey = None
@ -338,6 +379,9 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
usages = ', '.join(self.basicConstraints)
extensions.append(crypto.X509Extension(b"basicConstraints", self.basicConstraints_critical, usages.encode('ascii')))
if self.ocspMustStaple:
extensions.append(crypto.X509Extension(MUST_STAPLE_NAME, self.ocspMustStaple_critical, MUST_STAPLE_VALUE))
if extensions:
req.add_extensions(extensions)
@ -407,10 +451,21 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
def _check_basicConstraints(extensions):
return _check_keyUsage_(extensions, b'basicConstraints', self.basicConstraints, self.basicConstraints_critical)
def _check_ocspMustStaple(extensions):
oms_ext = [ext for ext in extensions if ext.get_short_name() == MUST_STAPLE_NAME and str(ext) == MUST_STAPLE_VALUE]
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER < 0x10100000:
# Older versions of libssl don't know about OCSP Must Staple
oms_ext.extend([ext for ext in extensions if ext.get_short_name() == b'UNDEF' and ext.get_data() == b'\x30\x03\x02\x01\x05'])
if self.ocspMustStaple:
return len(oms_ext) > 0 and oms_ext[0].get_critical() == self.ocspMustStaple_critical
else:
return len(oms_ext) == 0
def _check_extensions(csr):
extensions = csr.get_extensions()
return (_check_subjectAltName(extensions) and _check_keyUsage(extensions) and
_check_extenededKeyUsage(extensions) and _check_basicConstraints(extensions))
_check_extenededKeyUsage(extensions) and _check_basicConstraints(extensions) and
_check_ocspMustStaple(extensions))
def _check_signature(csr):
try:
@ -436,6 +491,7 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
'keyUsage': self.keyUsage,
'extendedKeyUsage': self.extendedKeyUsage,
'basicConstraints': self.basicConstraints,
'ocspMustStaple': self.ocspMustStaple,
'changed': self.changed
}
@ -468,6 +524,8 @@ def main():
extendedKeyUsage_critical=dict(aliases=['extKeyUsage_critical', 'extended_key_usage_critical'], default=False, type='bool'),
basicConstraints=dict(aliases=['basic_constraints'], type='list'),
basicConstraints_critical=dict(aliases=['basic_constraints_critical'], default=False, type='bool'),
ocspMustStaple=dict(aliases=['ocsp_must_staple'], default=False, type='bool'),
ocspMustStaple_critical=dict(aliases=['ocsp_must_staple_critical'], default=False, type='bool'),
),
add_file_common_args=True,
supports_check_mode=True,

View file

@ -51,6 +51,21 @@
privatekey_path: '{{ output_dir }}/privatekey.pem'
commonName: www.ansible.com
- name: Generate CSR with OCSP Must Staple
openssl_csr:
path: '{{ output_dir }}/csr_ocsp.csr'
privatekey_path: '{{ output_dir }}/privatekey.pem'
subject_alt_name: "DNS:www.ansible.com"
ocsp_must_staple: true
- name: Generate CSR with OCSP Must Staple (test idempotency)
openssl_csr:
path: '{{ output_dir }}/csr_ocsp.csr'
privatekey_path: '{{ output_dir }}/privatekey.pem'
subject_alt_name: "DNS:www.ansible.com"
ocsp_must_staple: true
register: csr_ocsp_idempotency
- import_tasks: ../tests/validate.yml
when: pyopenssl_version.stdout is version('0.15', '>=')

View file

@ -19,7 +19,7 @@
- name: Validate CSR_KU_XKU (assert idempotency)
assert:
that:
- csr_ku_xku.changed == False
- csr_ku_xku is not changed
- name: Validate old_API CSR (test - Common Name)
shell: "openssl req -noout -subject -in {{ output_dir }}/csr_oldapi.csr -nameopt oneline,-space_eq"
@ -34,3 +34,18 @@
that:
- csr_oldapi_cn.stdout.split('=')[-1] == 'www.ansible.com'
- csr_oldapi_modulus.stdout == privatekey_modulus.stdout
- name: Validate OCSP Must Staple CSR (test - everything)
shell: "openssl req -noout -in {{ output_dir }}/csr_ocsp.csr -text"
register: csr_ocsp
- name: Validate OCSP Must Staple CSR (assert)
assert:
that:
- "(csr_ocsp.stdout is search('\\s+TLS Feature:\\s*\\n\\s+status_request\\s+')) or
(csr_ocsp.stdout is search('\\s+1.3.6.1.5.5.7.1.24:\\s*\\n\\s+0\\.\\.\\.\\.\\s+'))"
- name: Validate OCSP Must Staple CSR (assert idempotency)
assert:
that:
- csr_ocsp_idempotency is not changed