openssl_csr: Allow user to specify criticality of extensions (#28173)

Allow user to mark the x509v3 extensions as critical, by specifying the
$extension_critical boolean, where $extension is the name of the
extension.

Currently this module supports only 3 differents x509v3 extensions:

  * keyUsage
  * extendedKeyUsage
  * subjectAtlName

There are more to come.
This commit is contained in:
Yanis Guenane 2017-08-15 10:29:29 +02:00 committed by ansibot
parent f668f41822
commit 8ed7417ff9

View file

@ -103,12 +103,20 @@ options:
description: description:
- SAN extension to attach to the certificate signing request - SAN extension to attach to the certificate signing request
- This can either be a 'comma separated string' or a YAML list. - This can either be a 'comma separated string' or a YAML list.
subjectAltName_critical:
required: false
description:
- Should the subjectAltName extension be considered as critical
keyUsage: keyUsage:
required: false required: false
description: description:
- This defines the purpose (e.g. encipherment, signature, certificate signing) - This defines the purpose (e.g. encipherment, signature, certificate signing)
of the key contained in the certificate. of the key contained in the certificate.
- This can either be a 'comma separated string' or a YAML list. - This can either be a 'comma separated string' or a YAML list.
keyUsage_critical:
required: false
description:
- Should the keyUsage extension be considered as critical
extendedKeyUsage: extendedKeyUsage:
required: false required: false
aliases: [ 'extKeyUsage' ] aliases: [ 'extKeyUsage' ]
@ -116,6 +124,11 @@ options:
- Additional restrictions (e.g. client authentication, server authentication) - Additional restrictions (e.g. client authentication, server authentication)
on the allowed purposes for which the public key may be used. on the allowed purposes for which the public key may be used.
- This can either be a 'comma separated string' or a YAML list. - This can either be a 'comma separated string' or a YAML list.
extendedKeyUsage_critical:
required: false
aliases: [ 'extKeyUsage_critical' ]
description:
- Should the extkeyUsage extension be considered as critical
notes: notes:
- "If the certificate signing request already exists it will be checked whether subjectAltName, - "If the certificate signing request already exists it will be checked whether subjectAltName,
@ -239,8 +252,11 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
self.privatekey_passphrase = module.params['privatekey_passphrase'] self.privatekey_passphrase = module.params['privatekey_passphrase']
self.version = module.params['version'] self.version = module.params['version']
self.subjectAltName = module.params['subjectAltName'] self.subjectAltName = module.params['subjectAltName']
self.subjectAltName_critical = module.params['subjectAltName_critical']
self.keyUsage = module.params['keyUsage'] self.keyUsage = module.params['keyUsage']
self.keyUsage_critical = module.params['keyUsage_critical']
self.extendedKeyUsage = module.params['extendedKeyUsage'] self.extendedKeyUsage = module.params['extendedKeyUsage']
self.extendedKeyUsage_critical = module.params['extendedKeyUsage_critical']
self.request = None self.request = None
self.privatekey = None self.privatekey = None
@ -271,15 +287,15 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
setattr(subject, key, value) setattr(subject, key, value)
altnames = ', '.join(self.subjectAltName) altnames = ', '.join(self.subjectAltName)
extensions = [crypto.X509Extension(b"subjectAltName", False, altnames.encode('ascii'))] extensions = [crypto.X509Extension(b"subjectAltName", self.subjectAltName_critical, altnames.encode('ascii'))]
if self.keyUsage: if self.keyUsage:
usages = ', '.join(self.keyUsage) usages = ', '.join(self.keyUsage)
extensions.append(crypto.X509Extension(b"keyUsage", False, usages.encode('ascii'))) extensions.append(crypto.X509Extension(b"keyUsage", self.keyUsage_critical, usages.encode('ascii')))
if self.extendedKeyUsage: if self.extendedKeyUsage:
usages = ', '.join(self.extendedKeyUsage) usages = ', '.join(self.extendedKeyUsage)
extensions.append(crypto.X509Extension(b"extendedKeyUsage", False, usages.encode('ascii'))) extensions.append(crypto.X509Extension(b"extendedKeyUsage", self.extendedKeyUsage_critical, usages.encode('ascii')))
req.add_extensions(extensions) req.add_extensions(extensions)
@ -315,13 +331,13 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
return True return True
def _check_subjectAltName(extensions): def _check_subjectAltName(extensions):
altnames_ext = next((ext.__str__() for ext in extensions if ext.get_short_name() == b'subjectAltName'), '') altnames_ext = next((ext for ext in extensions if ext.get_short_name() == b'subjectAltName'), '')
altnames = [altname.strip() for altname in altnames_ext.split(',')] altnames = [altname.strip() for altname in str(altnames_ext).split(',')]
# apperently openssl returns 'IP address' not 'IP' as specifier when converting the subjectAltName to string # apperently openssl returns 'IP address' not 'IP' as specifier when converting the subjectAltName to string
# although it won't accept this specifier when generating the CSR. (https://github.com/openssl/openssl/issues/4004) # although it won't accept this specifier when generating the CSR. (https://github.com/openssl/openssl/issues/4004)
altnames = [name if not name.startswith('IP Address:') else "IP:" + name.split(':', 1)[1] for name in altnames] altnames = [name if not name.startswith('IP Address:') else "IP:" + name.split(':', 1)[1] for name in altnames]
if self.subjectAltName: if self.subjectAltName:
if set(altnames) != set(self.subjectAltName): if set(altnames) != set(self.subjectAltName) or altnames_ext.get_critical() != self.subjectAltName_critical:
return False return False
else: else:
if altnames: if altnames:
@ -329,22 +345,22 @@ class CertificateSigningRequest(crypto_utils.OpenSSLObject):
return True return True
def _check_keyUsage_(extensions, extName, expected, long): def _check_keyUsage_(extensions, extName, expected, critical, long):
usages_ext = [str(ext) for ext in extensions if ext.get_short_name() == extName] usages_ext = [ext for ext in extensions if ext.get_short_name() == extName]
if (not usages_ext and expected) or (usages_ext and not expected): if (not usages_ext and expected) or (usages_ext and not expected):
return False return False
elif not usages_ext and not expected: elif not usages_ext and not expected:
return True return True
else: else:
current = [usage.strip() for usage in usages_ext[0].split(',')] current = [usage.strip() for usage in str(usages_ext[0]).split(',')]
expected = [long[usage] if usage in long else usage for usage in expected] expected = [long[usage] if usage in long else usage for usage in expected]
return current == expected return current == expected and usages_ext[0].get_critical() == critical
def _check_keyUsage(extensions): def _check_keyUsage(extensions):
return _check_keyUsage_(extensions, b'keyUsage', self.keyUsage, crypto_utils.keyUsageLong) return _check_keyUsage_(extensions, b'keyUsage', self.keyUsage, self.keyUsage_critical, crypto_utils.keyUsageLong)
def _check_extenededKeyUsage(extensions): def _check_extenededKeyUsage(extensions):
return _check_keyUsage_(extensions, b'extendedKeyUsage', self.extendedKeyUsage, crypto_utils.extendedKeyUsageLong) return _check_keyUsage_(extensions, b'extendedKeyUsage', self.extendedKeyUsage, self.extendedKeyUsage_critical, crypto_utils.extendedKeyUsageLong)
def _check_extensions(csr): def _check_extensions(csr):
extensions = csr.get_extensions() extensions = csr.get_extensions()
@ -397,8 +413,11 @@ def main():
commonName=dict(aliases=['CN'], type='str'), commonName=dict(aliases=['CN'], type='str'),
emailAddress=dict(aliases=['E'], type='str'), emailAddress=dict(aliases=['E'], type='str'),
subjectAltName=dict(type='list'), subjectAltName=dict(type='list'),
subjectAltName_critical=dict(default=False, type='bool'),
keyUsage=dict(type='list'), keyUsage=dict(type='list'),
keyUsage_critical=dict(default=False, type='bool'),
extendedKeyUsage=dict(aliases=['extKeyUsage'], type='list'), extendedKeyUsage=dict(aliases=['extKeyUsage'], type='list'),
extendedKeyUsage_critical=dict(default=False, aliases=['extKeyUsage_critical'], type='bool'),
), ),
add_file_common_args=True, add_file_common_args=True,
supports_check_mode=True, supports_check_mode=True,