Remove assertonly (#289)
* Remove assertonly backend. * Remove assertonly tests. * The expired test is basically a test of assertonly. * Replace assertonly verification by _info + assert.pull/307/head
parent
c68bfedbaa
commit
5f1efb6f7e
|
@ -0,0 +1,2 @@
|
|||
removed_features:
|
||||
- "x509_certificate - remove ``assertonly`` provider (https://github.com/ansible-collections/community.crypto/pull/289)."
|
|
@ -16,7 +16,7 @@ description:
|
|||
- This module allows one to (re)generate OpenSSL certificates.
|
||||
- It uses the cryptography python library to interact with OpenSSL.
|
||||
requirements:
|
||||
- cryptography >= 1.6 (if using C(selfsigned), C(ownca) or C(assertonly) provider)
|
||||
- cryptography >= 1.6 (if using C(selfsigned) or C(ownca) provider)
|
||||
options:
|
||||
force:
|
||||
description:
|
||||
|
@ -113,201 +113,6 @@ options:
|
|||
default: https://acme-v02.api.letsencrypt.org/directory
|
||||
'''
|
||||
|
||||
BACKEND_ASSERTONLY_DOCUMENTATION = r'''
|
||||
description:
|
||||
- The C(assertonly) provider is intended for use cases where one is only interested in
|
||||
checking properties of a supplied certificate. Please note that this provider has been
|
||||
deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0. See the examples on how
|
||||
to emulate C(assertonly) usage with M(community.crypto.x509_certificate_info),
|
||||
M(community.crypto.openssl_csr_info), M(community.crypto.openssl_privatekey_info) and
|
||||
M(ansible.builtin.assert). This also allows more flexible checks than
|
||||
the ones offered by the C(assertonly) provider.
|
||||
- Many properties that can be specified in this module are for validation of an
|
||||
existing or newly generated certificate. The proper place to specify them, if you
|
||||
want to receive a certificate with these properties is a CSR (Certificate Signing Request).
|
||||
options:
|
||||
csr_path:
|
||||
description:
|
||||
- This is not required for the C(assertonly) provider.
|
||||
|
||||
csr_content:
|
||||
description:
|
||||
- This is not required for the C(assertonly) provider.
|
||||
|
||||
signature_algorithms:
|
||||
description:
|
||||
- A list of algorithms that you would accept the certificate to be signed with
|
||||
(e.g. ['sha256WithRSAEncryption', 'sha512WithRSAEncryption']).
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: list
|
||||
elements: str
|
||||
|
||||
issuer:
|
||||
description:
|
||||
- The key/value pairs that must be present in the issuer name field of the certificate.
|
||||
- If you need to specify more than one value with the same key, use a list as value.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: dict
|
||||
|
||||
issuer_strict:
|
||||
description:
|
||||
- If set to C(yes), the I(issuer) field must contain only these values.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: bool
|
||||
default: no
|
||||
|
||||
subject:
|
||||
description:
|
||||
- The key/value pairs that must be present in the subject name field of the certificate.
|
||||
- If you need to specify more than one value with the same key, use a list as value.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: dict
|
||||
|
||||
subject_strict:
|
||||
description:
|
||||
- If set to C(yes), the I(subject) field must contain only these values.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: bool
|
||||
default: no
|
||||
|
||||
has_expired:
|
||||
description:
|
||||
- Checks if the certificate is expired/not expired at the time the module is executed.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: bool
|
||||
default: no
|
||||
|
||||
version:
|
||||
description:
|
||||
- The version of the certificate.
|
||||
- Nowadays it should almost always be 3.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: int
|
||||
|
||||
valid_at:
|
||||
description:
|
||||
- The certificate must be valid at this point in time.
|
||||
- The timestamp is formatted as an ASN.1 TIME.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: str
|
||||
|
||||
invalid_at:
|
||||
description:
|
||||
- The certificate must be invalid at this point in time.
|
||||
- The timestamp is formatted as an ASN.1 TIME.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: str
|
||||
|
||||
not_before:
|
||||
description:
|
||||
- The certificate must start to become valid at this point in time.
|
||||
- The timestamp is formatted as an ASN.1 TIME.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: str
|
||||
aliases: [ notBefore ]
|
||||
|
||||
not_after:
|
||||
description:
|
||||
- The certificate must expire at this point in time.
|
||||
- The timestamp is formatted as an ASN.1 TIME.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: str
|
||||
aliases: [ notAfter ]
|
||||
|
||||
valid_in:
|
||||
description:
|
||||
- The certificate must still be valid at this relative time offset from now.
|
||||
- Valid format is C([+-]timespec | number_of_seconds) where timespec can be an integer
|
||||
+ C([w | d | h | m | s]) (e.g. C(+32w1d2h).
|
||||
- Note that if using this parameter, this module is NOT idempotent.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: str
|
||||
|
||||
key_usage:
|
||||
description:
|
||||
- The I(key_usage) extension field must contain all these values.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: list
|
||||
elements: str
|
||||
aliases: [ keyUsage ]
|
||||
|
||||
key_usage_strict:
|
||||
description:
|
||||
- If set to C(yes), the I(key_usage) extension field must contain only these values.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: bool
|
||||
default: no
|
||||
aliases: [ keyUsage_strict ]
|
||||
|
||||
extended_key_usage:
|
||||
description:
|
||||
- The I(extended_key_usage) extension field must contain all these values.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: list
|
||||
elements: str
|
||||
aliases: [ extendedKeyUsage ]
|
||||
|
||||
extended_key_usage_strict:
|
||||
description:
|
||||
- If set to C(yes), the I(extended_key_usage) extension field must contain only these values.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: bool
|
||||
default: no
|
||||
aliases: [ extendedKeyUsage_strict ]
|
||||
|
||||
subject_alt_name:
|
||||
description:
|
||||
- The I(subject_alt_name) extension field must contain these values.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: list
|
||||
elements: str
|
||||
aliases: [ subjectAltName ]
|
||||
|
||||
subject_alt_name_strict:
|
||||
description:
|
||||
- If set to C(yes), the I(subject_alt_name) extension field must contain only these values.
|
||||
- This is only used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: bool
|
||||
default: no
|
||||
aliases: [ subjectAltName_strict ]
|
||||
'''
|
||||
|
||||
BACKEND_ENTRUST_DOCUMENTATION = r'''
|
||||
options:
|
||||
entrust_cert_type:
|
||||
|
|
|
@ -1,500 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2016-2017, Yanis Guenane <yanis+ansible@guenane.org>
|
||||
# Copyright: (c) 2017, Markus Teufelberger <mteufelberger+ansible@mgit.at>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
import abc
|
||||
import datetime
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_native, to_bytes, to_text
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.support import (
|
||||
parse_name_field,
|
||||
get_relative_time_option,
|
||||
)
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_support import (
|
||||
cryptography_compare_public_keys,
|
||||
cryptography_get_name,
|
||||
cryptography_name_to_oid,
|
||||
cryptography_parse_key_usage_params,
|
||||
)
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.certificate import (
|
||||
CertificateBackend,
|
||||
CertificateProvider,
|
||||
)
|
||||
|
||||
try:
|
||||
import OpenSSL
|
||||
from OpenSSL import crypto
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
import cryptography
|
||||
from cryptography import x509
|
||||
from cryptography.x509 import NameAttribute, Name
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def compare_sets(subset, superset, equality=False):
|
||||
if equality:
|
||||
return set(subset) == set(superset)
|
||||
else:
|
||||
return all(x in superset for x in subset)
|
||||
|
||||
|
||||
def compare_dicts(subset, superset, equality=False):
|
||||
if equality:
|
||||
return subset == superset
|
||||
else:
|
||||
return all(superset.get(x) == v for x, v in subset.items())
|
||||
|
||||
|
||||
NO_EXTENSION = 'no extension'
|
||||
|
||||
|
||||
class AssertOnlyCertificateBackend(CertificateBackend):
|
||||
def __init__(self, module, backend):
|
||||
super(AssertOnlyCertificateBackend, self).__init__(module, backend)
|
||||
|
||||
self.signature_algorithms = module.params['signature_algorithms']
|
||||
if module.params['subject']:
|
||||
self.subject = parse_name_field(module.params['subject'])
|
||||
else:
|
||||
self.subject = []
|
||||
self.subject_strict = module.params['subject_strict']
|
||||
if module.params['issuer']:
|
||||
self.issuer = parse_name_field(module.params['issuer'])
|
||||
else:
|
||||
self.issuer = []
|
||||
self.issuer_strict = module.params['issuer_strict']
|
||||
self.has_expired = module.params['has_expired']
|
||||
self.version = module.params['version']
|
||||
self.key_usage = module.params['key_usage']
|
||||
self.key_usage_strict = module.params['key_usage_strict']
|
||||
self.extended_key_usage = module.params['extended_key_usage']
|
||||
self.extended_key_usage_strict = module.params['extended_key_usage_strict']
|
||||
self.subject_alt_name = module.params['subject_alt_name']
|
||||
self.subject_alt_name_strict = module.params['subject_alt_name_strict']
|
||||
self.not_before = module.params['not_before']
|
||||
self.not_after = module.params['not_after']
|
||||
self.valid_at = module.params['valid_at']
|
||||
self.invalid_at = module.params['invalid_at']
|
||||
self.valid_in = module.params['valid_in']
|
||||
if self.valid_in and not self.valid_in.startswith("+") and not self.valid_in.startswith("-"):
|
||||
try:
|
||||
int(self.valid_in)
|
||||
except ValueError:
|
||||
module.fail_json(msg='The supplied value for "valid_in" (%s) is not an integer or a valid timespec' % self.valid_in)
|
||||
self.valid_in = "+" + self.valid_in + "s"
|
||||
|
||||
# Load objects
|
||||
self._ensure_private_key_loaded()
|
||||
self._ensure_csr_loaded()
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_privatekey(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_csr_signature(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_csr_subject(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_csr_extensions(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_signature_algorithms(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_subject(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_issuer(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_has_expired(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_version(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_key_usage(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_extended_key_usage(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_subject_alt_name(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_not_before(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_not_after(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_valid_at(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_invalid_at(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _validate_valid_in(self):
|
||||
pass
|
||||
|
||||
def assertonly(self):
|
||||
messages = []
|
||||
if self.privatekey_path is not None or self.privatekey_content is not None:
|
||||
if not self._validate_privatekey():
|
||||
messages.append(
|
||||
'Certificate and private key %s do not match' %
|
||||
(self.privatekey_path or '(provided in module options)')
|
||||
)
|
||||
|
||||
if self.csr_path is not None or self.csr_content is not None:
|
||||
if not self._validate_csr_signature():
|
||||
messages.append(
|
||||
'Certificate and CSR %s do not match: private key mismatch' %
|
||||
(self.csr_path or '(provided in module options)')
|
||||
)
|
||||
if not self._validate_csr_subject():
|
||||
messages.append(
|
||||
'Certificate and CSR %s do not match: subject mismatch' %
|
||||
(self.csr_path or '(provided in module options)')
|
||||
)
|
||||
if not self._validate_csr_extensions():
|
||||
messages.append(
|
||||
'Certificate and CSR %s do not match: extensions mismatch' %
|
||||
(self.csr_path or '(provided in module options)')
|
||||
)
|
||||
|
||||
if self.signature_algorithms is not None:
|
||||
wrong_alg = self._validate_signature_algorithms()
|
||||
if wrong_alg:
|
||||
messages.append(
|
||||
'Invalid signature algorithm (got %s, expected one of %s)' %
|
||||
(wrong_alg, self.signature_algorithms)
|
||||
)
|
||||
|
||||
if self.subject is not None:
|
||||
failure = self._validate_subject()
|
||||
if failure:
|
||||
dummy, cert_subject = failure
|
||||
messages.append(
|
||||
'Invalid subject component (got %s, expected all of %s to be present)' %
|
||||
(cert_subject, self.subject)
|
||||
)
|
||||
|
||||
if self.issuer is not None:
|
||||
failure = self._validate_issuer()
|
||||
if failure:
|
||||
dummy, cert_issuer = failure
|
||||
messages.append(
|
||||
'Invalid issuer component (got %s, expected all of %s to be present)' % (cert_issuer, self.issuer)
|
||||
)
|
||||
|
||||
if self.has_expired is not None:
|
||||
cert_expired = self._validate_has_expired()
|
||||
if cert_expired != self.has_expired:
|
||||
messages.append(
|
||||
'Certificate expiration check failed (certificate expiration is %s, expected %s)' %
|
||||
(cert_expired, self.has_expired)
|
||||
)
|
||||
|
||||
if self.version is not None:
|
||||
cert_version = self._validate_version()
|
||||
if cert_version != self.version:
|
||||
messages.append(
|
||||
'Invalid certificate version number (got %s, expected %s)' %
|
||||
(cert_version, self.version)
|
||||
)
|
||||
|
||||
if self.key_usage is not None:
|
||||
failure = self._validate_key_usage()
|
||||
if failure == NO_EXTENSION:
|
||||
messages.append('Found no keyUsage extension')
|
||||
elif failure:
|
||||
dummy, cert_key_usage = failure
|
||||
messages.append(
|
||||
'Invalid keyUsage components (got %s, expected all of %s to be present)' %
|
||||
(cert_key_usage, self.key_usage)
|
||||
)
|
||||
|
||||
if self.extended_key_usage is not None:
|
||||
failure = self._validate_extended_key_usage()
|
||||
if failure == NO_EXTENSION:
|
||||
messages.append('Found no extendedKeyUsage extension')
|
||||
elif failure:
|
||||
dummy, ext_cert_key_usage = failure
|
||||
messages.append(
|
||||
'Invalid extendedKeyUsage component (got %s, expected all of %s to be present)' % (ext_cert_key_usage, self.extended_key_usage)
|
||||
)
|
||||
|
||||
if self.subject_alt_name is not None:
|
||||
failure = self._validate_subject_alt_name()
|
||||
if failure == NO_EXTENSION:
|
||||
messages.append('Found no subjectAltName extension')
|
||||
elif failure:
|
||||
dummy, cert_san = failure
|
||||
messages.append(
|
||||
'Invalid subjectAltName component (got %s, expected all of %s to be present)' %
|
||||
(cert_san, self.subject_alt_name)
|
||||
)
|
||||
|
||||
if self.not_before is not None:
|
||||
cert_not_valid_before = self._validate_not_before()
|
||||
if cert_not_valid_before != get_relative_time_option(self.not_before, 'not_before', backend=self.backend):
|
||||
messages.append(
|
||||
'Invalid not_before component (got %s, expected %s to be present)' %
|
||||
(cert_not_valid_before, self.not_before)
|
||||
)
|
||||
|
||||
if self.not_after is not None:
|
||||
cert_not_valid_after = self._validate_not_after()
|
||||
if cert_not_valid_after != get_relative_time_option(self.not_after, 'not_after', backend=self.backend):
|
||||
messages.append(
|
||||
'Invalid not_after component (got %s, expected %s to be present)' %
|
||||
(cert_not_valid_after, self.not_after)
|
||||
)
|
||||
|
||||
if self.valid_at is not None:
|
||||
not_before, valid_at, not_after = self._validate_valid_at()
|
||||
if not (not_before <= valid_at <= not_after):
|
||||
messages.append(
|
||||
'Certificate is not valid for the specified date (%s) - not_before: %s - not_after: %s' %
|
||||
(self.valid_at, not_before, not_after)
|
||||
)
|
||||
|
||||
if self.invalid_at is not None:
|
||||
not_before, invalid_at, not_after = self._validate_invalid_at()
|
||||
if not_before <= invalid_at <= not_after:
|
||||
messages.append(
|
||||
'Certificate is not invalid for the specified date (%s) - not_before: %s - not_after: %s' %
|
||||
(self.invalid_at, not_before, not_after)
|
||||
)
|
||||
|
||||
if self.valid_in is not None:
|
||||
not_before, valid_in, not_after = self._validate_valid_in()
|
||||
if not not_before <= valid_in <= not_after:
|
||||
messages.append(
|
||||
'Certificate is not valid in %s from now (that would be %s) - not_before: %s - not_after: %s' %
|
||||
(self.valid_in, valid_in, not_before, not_after)
|
||||
)
|
||||
return messages
|
||||
|
||||
def needs_regeneration(self):
|
||||
self._ensure_existing_certificate_loaded()
|
||||
if self.existing_certificate is None:
|
||||
self.messages = ['Certificate not provided']
|
||||
else:
|
||||
self.messages = self.assertonly()
|
||||
|
||||
return len(self.messages) != 0
|
||||
|
||||
def generate_certificate(self):
|
||||
self.module.fail_json(msg=' | '.join(self.messages))
|
||||
|
||||
def get_certificate_data(self):
|
||||
return self.existing_certificate_bytes
|
||||
|
||||
|
||||
class AssertOnlyCertificateBackendCryptography(AssertOnlyCertificateBackend):
|
||||
"""Validate the supplied cert, using the cryptography backend"""
|
||||
def __init__(self, module):
|
||||
super(AssertOnlyCertificateBackendCryptography, self).__init__(module, 'cryptography')
|
||||
|
||||
def _validate_privatekey(self):
|
||||
return cryptography_compare_public_keys(self.existing_certificate.public_key(), self.privatekey.public_key())
|
||||
|
||||
def _validate_csr_signature(self):
|
||||
if not self.csr.is_signature_valid:
|
||||
return False
|
||||
return cryptography_compare_public_keys(self.csr.public_key(), self.existing_certificate.public_key())
|
||||
|
||||
def _validate_csr_subject(self):
|
||||
return self.csr.subject == self.existing_certificate.subject
|
||||
|
||||
def _validate_csr_extensions(self):
|
||||
cert_exts = self.existing_certificate.extensions
|
||||
csr_exts = self.csr.extensions
|
||||
if len(cert_exts) != len(csr_exts):
|
||||
return False
|
||||
for cert_ext in cert_exts:
|
||||
try:
|
||||
csr_ext = csr_exts.get_extension_for_oid(cert_ext.oid)
|
||||
if cert_ext != csr_ext:
|
||||
return False
|
||||
except cryptography.x509.ExtensionNotFound as dummy:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _validate_signature_algorithms(self):
|
||||
if self.existing_certificate.signature_algorithm_oid._name not in self.signature_algorithms:
|
||||
return self.existing_certificate.signature_algorithm_oid._name
|
||||
|
||||
def _validate_subject(self):
|
||||
expected_subject = Name([NameAttribute(oid=cryptography_name_to_oid(sub[0]), value=to_text(sub[1]))
|
||||
for sub in self.subject])
|
||||
cert_subject = self.existing_certificate.subject
|
||||
if not compare_sets(expected_subject, cert_subject, self.subject_strict):
|
||||
return expected_subject, cert_subject
|
||||
|
||||
def _validate_issuer(self):
|
||||
expected_issuer = Name([NameAttribute(oid=cryptography_name_to_oid(iss[0]), value=to_text(iss[1]))
|
||||
for iss in self.issuer])
|
||||
cert_issuer = self.existing_certificate.issuer
|
||||
if not compare_sets(expected_issuer, cert_issuer, self.issuer_strict):
|
||||
return self.issuer, cert_issuer
|
||||
|
||||
def _validate_has_expired(self):
|
||||
cert_not_after = self.existing_certificate.not_valid_after
|
||||
cert_expired = cert_not_after < datetime.datetime.utcnow()
|
||||
return cert_expired
|
||||
|
||||
def _validate_version(self):
|
||||
if self.existing_certificate.version == x509.Version.v1:
|
||||
return 1
|
||||
if self.existing_certificate.version == x509.Version.v3:
|
||||
return 3
|
||||
return "unknown"
|
||||
|
||||
def _validate_key_usage(self):
|
||||
try:
|
||||
current_key_usage = self.existing_certificate.extensions.get_extension_for_class(x509.KeyUsage).value
|
||||
test_key_usage = dict(
|
||||
digital_signature=current_key_usage.digital_signature,
|
||||
content_commitment=current_key_usage.content_commitment,
|
||||
key_encipherment=current_key_usage.key_encipherment,
|
||||
data_encipherment=current_key_usage.data_encipherment,
|
||||
key_agreement=current_key_usage.key_agreement,
|
||||
key_cert_sign=current_key_usage.key_cert_sign,
|
||||
crl_sign=current_key_usage.crl_sign,
|
||||
encipher_only=False,
|
||||
decipher_only=False
|
||||
)
|
||||
if test_key_usage['key_agreement']:
|
||||
test_key_usage.update(dict(
|
||||
encipher_only=current_key_usage.encipher_only,
|
||||
decipher_only=current_key_usage.decipher_only
|
||||
))
|
||||
|
||||
key_usages = cryptography_parse_key_usage_params(self.key_usage)
|
||||
if not compare_dicts(key_usages, test_key_usage, self.key_usage_strict):
|
||||
return self.key_usage, [k for k, v in test_key_usage.items() if v is True]
|
||||
|
||||
except cryptography.x509.ExtensionNotFound:
|
||||
# This is only bad if the user specified a non-empty list
|
||||
if self.key_usage:
|
||||
return NO_EXTENSION
|
||||
|
||||
def _validate_extended_key_usage(self):
|
||||
try:
|
||||
current_ext_keyusage = self.existing_certificate.extensions.get_extension_for_class(x509.ExtendedKeyUsage).value
|
||||
usages = [cryptography_name_to_oid(usage) for usage in self.extended_key_usage]
|
||||
expected_ext_keyusage = x509.ExtendedKeyUsage(usages)
|
||||
if not compare_sets(expected_ext_keyusage, current_ext_keyusage, self.extended_key_usage_strict):
|
||||
return [eku.value for eku in expected_ext_keyusage], [eku.value for eku in current_ext_keyusage]
|
||||
|
||||
except cryptography.x509.ExtensionNotFound:
|
||||
# This is only bad if the user specified a non-empty list
|
||||
if self.extended_key_usage:
|
||||
return NO_EXTENSION
|
||||
|
||||
def _validate_subject_alt_name(self):
|
||||
try:
|
||||
current_san = self.existing_certificate.extensions.get_extension_for_class(x509.SubjectAlternativeName).value
|
||||
expected_san = [cryptography_get_name(san) for san in self.subject_alt_name]
|
||||
if not compare_sets(expected_san, current_san, self.subject_alt_name_strict):
|
||||
return self.subject_alt_name, current_san
|
||||
except cryptography.x509.ExtensionNotFound:
|
||||
# This is only bad if the user specified a non-empty list
|
||||
if self.subject_alt_name:
|
||||
return NO_EXTENSION
|
||||
|
||||
def _validate_not_before(self):
|
||||
return self.existing_certificate.not_valid_before
|
||||
|
||||
def _validate_not_after(self):
|
||||
return self.existing_certificate.not_valid_after
|
||||
|
||||
def _validate_valid_at(self):
|
||||
rt = get_relative_time_option(self.valid_at, 'valid_at', backend=self.backend)
|
||||
return self.existing_certificate.not_valid_before, rt, self.existing_certificate.not_valid_after
|
||||
|
||||
def _validate_invalid_at(self):
|
||||
rt = get_relative_time_option(self.invalid_at, 'invalid_at', backend=self.backend)
|
||||
return self.existing_certificate.not_valid_before, rt, self.existing_certificate.not_valid_after
|
||||
|
||||
def _validate_valid_in(self):
|
||||
valid_in_date = get_relative_time_option(self.valid_in, "valid_in", backend=self.backend)
|
||||
return self.existing_certificate.not_valid_before, valid_in_date, self.existing_certificate.not_valid_after
|
||||
|
||||
|
||||
class AssertOnlyCertificateProvider(CertificateProvider):
|
||||
def validate_module_args(self, module):
|
||||
module.deprecate("The 'assertonly' provider is deprecated; please see the examples of "
|
||||
"the 'x509_certificate' module on how to replace it with other modules",
|
||||
version='2.0.0', collection_name='community.crypto')
|
||||
|
||||
def needs_version_two_certs(self, module):
|
||||
return False
|
||||
|
||||
def create_backend(self, module, backend):
|
||||
if backend == 'cryptography':
|
||||
return AssertOnlyCertificateBackendCryptography(module)
|
||||
|
||||
|
||||
def add_assertonly_provider_to_argument_spec(argument_spec):
|
||||
argument_spec.argument_spec['provider']['choices'].append('assertonly')
|
||||
argument_spec.argument_spec.update(dict(
|
||||
signature_algorithms=dict(type='list', elements='str', removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
subject=dict(type='dict', removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
subject_strict=dict(type='bool', default=False, removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
issuer=dict(type='dict', removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
issuer_strict=dict(type='bool', default=False, removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
has_expired=dict(type='bool', default=False, removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
version=dict(type='int', removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
key_usage=dict(type='list', elements='str', aliases=['keyUsage'],
|
||||
removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
key_usage_strict=dict(type='bool', default=False, aliases=['keyUsage_strict'],
|
||||
removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
extended_key_usage=dict(type='list', elements='str', aliases=['extendedKeyUsage'],
|
||||
removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
extended_key_usage_strict=dict(type='bool', default=False, aliases=['extendedKeyUsage_strict'],
|
||||
removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
subject_alt_name=dict(type='list', elements='str', aliases=['subjectAltName'],
|
||||
removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
subject_alt_name_strict=dict(type='bool', default=False, aliases=['subjectAltName_strict'],
|
||||
removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
not_before=dict(type='str', aliases=['notBefore'], removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
not_after=dict(type='str', aliases=['notAfter'], removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
valid_at=dict(type='str', removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
invalid_at=dict(type='str', removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
valid_in=dict(type='str', removed_in_version='2.0.0', removed_from_collection='community.crypto'),
|
||||
))
|
|
@ -14,7 +14,7 @@ DOCUMENTATION = r'''
|
|||
module: x509_certificate
|
||||
short_description: Generate and/or check OpenSSL certificates
|
||||
description:
|
||||
- It implements a notion of provider (ie. C(selfsigned), C(ownca), C(acme), C(assertonly), C(entrust))
|
||||
- It implements a notion of provider (one of C(selfsigned), C(ownca), C(acme), and C(entrust))
|
||||
for your certificate.
|
||||
- "Please note that the module regenerates existing certificate if it does not match the module's
|
||||
options, or if it seems to be corrupt. If you are concerned that this could overwrite
|
||||
|
@ -47,8 +47,6 @@ options:
|
|||
provider:
|
||||
description:
|
||||
- Name of the provider to use to generate/retrieve the OpenSSL certificate.
|
||||
- The C(assertonly) provider will not generate files and fail if the certificate file is missing.
|
||||
- The C(assertonly) provider has been deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0.
|
||||
Please see the examples on how to emulate it with
|
||||
M(community.crypto.x509_certificate_info), M(community.crypto.openssl_csr_info),
|
||||
M(community.crypto.openssl_privatekey_info) and M(ansible.builtin.assert).
|
||||
|
@ -56,7 +54,7 @@ options:
|
|||
L(Entrust Certificate Services,https://www.entrustdatacard.com/products/categories/ssl-certificates) (ECS) API."
|
||||
- Required if I(state) is C(present).
|
||||
type: str
|
||||
choices: [ acme, assertonly, entrust, ownca, selfsigned ]
|
||||
choices: [ acme, entrust, ownca, selfsigned ]
|
||||
|
||||
return_content:
|
||||
description:
|
||||
|
@ -69,9 +67,6 @@ options:
|
|||
description:
|
||||
- Create a backup file including a timestamp so you can get the original
|
||||
certificate back if you overwrote it with a new one by accident.
|
||||
- This is not used by the C(assertonly) provider.
|
||||
- This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in community.crypto 2.0.0.
|
||||
For alternatives, see the example on replacing C(assertonly).
|
||||
type: bool
|
||||
default: no
|
||||
|
||||
|
@ -96,7 +91,6 @@ extends_documentation_fragment:
|
|||
- ansible.builtin.files
|
||||
- community.crypto.module_certificate
|
||||
- community.crypto.module_certificate.backend_acme_documentation
|
||||
- community.crypto.module_certificate.backend_assertonly_documentation
|
||||
- community.crypto.module_certificate.backend_entrust_documentation
|
||||
- community.crypto.module_certificate.backend_ownca_documentation
|
||||
- community.crypto.module_certificate.backend_selfsigned_documentation
|
||||
|
@ -150,40 +144,9 @@ EXAMPLES = r'''
|
|||
entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-key.crt
|
||||
entrust_api_specification_path: /etc/ssl/entrust/api-docs/cms-api-2.1.0.yaml
|
||||
|
||||
# The following example shows one assertonly usage using all existing options for
|
||||
# assertonly, and shows how to emulate the behavior with the x509_certificate_info,
|
||||
# openssl_csr_info, openssl_privatekey_info and assert modules:
|
||||
- name: Usage of assertonly with all existing options
|
||||
community.crypto.x509_certificate:
|
||||
provider: assertonly
|
||||
path: /etc/ssl/crt/ansible.com.crt
|
||||
csr_path: /etc/ssl/csr/ansible.com.csr
|
||||
privatekey_path: /etc/ssl/csr/ansible.com.key
|
||||
signature_algorithms:
|
||||
- sha256WithRSAEncryption
|
||||
- sha512WithRSAEncryption
|
||||
subject:
|
||||
commonName: ansible.com
|
||||
subject_strict: yes
|
||||
issuer:
|
||||
commonName: ansible.com
|
||||
issuer_strict: yes
|
||||
has_expired: no
|
||||
version: 3
|
||||
key_usage:
|
||||
- Data Encipherment
|
||||
key_usage_strict: yes
|
||||
extended_key_usage:
|
||||
- DVCS
|
||||
extended_key_usage_strict: yes
|
||||
subject_alt_name:
|
||||
- dns:ansible.com
|
||||
subject_alt_name_strict: yes
|
||||
not_before: 20190331202428Z
|
||||
not_after: 20190413202428Z
|
||||
valid_at: "+1d10h"
|
||||
invalid_at: 20200331202428Z
|
||||
valid_in: 10 # in ten seconds
|
||||
# The following example shows how to emulate the behavior of the removed
|
||||
# "assertonly" provider with the x509_certificate_info, openssl_csr_info,
|
||||
# openssl_privatekey_info and assert modules:
|
||||
|
||||
- name: Get certificate information
|
||||
community.crypto.x509_certificate_info:
|
||||
|
@ -208,9 +171,9 @@ EXAMPLES = r'''
|
|||
|
||||
- assert:
|
||||
that:
|
||||
# When private key is specified for assertonly, this will be checked:
|
||||
# When private key was specified for assertonly, this was checked:
|
||||
- result.public_key == result_privatekey.public_key
|
||||
# When CSR is specified for assertonly, this will be checked:
|
||||
# When CSR was specified for assertonly, this was checked:
|
||||
- result.public_key == result_csr.public_key
|
||||
- result.subject_ordered == result_csr.subject_ordered
|
||||
- result.extensions_by_oid == result_csr.extensions_by_oid
|
||||
|
@ -242,103 +205,6 @@ EXAMPLES = r'''
|
|||
- "result.valid_at.one_day_ten_hours" # for valid_at
|
||||
- "not result.valid_at.fixed_timestamp" # for invalid_at
|
||||
- "result.valid_at.ten_seconds" # for valid_in
|
||||
|
||||
# Examples for some checks one could use the assertonly provider for:
|
||||
# (Please note that assertonly has been deprecated!)
|
||||
|
||||
# How to use the assertonly provider to implement and trigger your own custom certificate generation workflow:
|
||||
- name: Check if a certificate is currently still valid, ignoring failures
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
has_expired: no
|
||||
ignore_errors: yes
|
||||
register: validity_check
|
||||
|
||||
- name: Run custom task(s) to get a new, valid certificate in case the initial check failed
|
||||
command: superspecialSSL recreate /etc/ssl/crt/example.com.crt
|
||||
when: validity_check.failed
|
||||
|
||||
- name: Check the new certificate again for validity with the same parameters, this time failing the play if it is still invalid
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
has_expired: no
|
||||
when: validity_check.failed
|
||||
|
||||
# Some other checks that assertonly could be used for:
|
||||
- name: Verify that an existing certificate was issued by the Let's Encrypt CA and is currently still valid
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
issuer:
|
||||
O: Let's Encrypt
|
||||
has_expired: no
|
||||
|
||||
- name: Ensure that a certificate uses a modern signature algorithm (no SHA1, MD5 or DSA)
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
signature_algorithms:
|
||||
- sha224WithRSAEncryption
|
||||
- sha256WithRSAEncryption
|
||||
- sha384WithRSAEncryption
|
||||
- sha512WithRSAEncryption
|
||||
- sha224WithECDSAEncryption
|
||||
- sha256WithECDSAEncryption
|
||||
- sha384WithECDSAEncryption
|
||||
- sha512WithECDSAEncryption
|
||||
|
||||
- name: Ensure that the existing certificate belongs to the specified private key
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
privatekey_path: /etc/ssl/private/example.com.pem
|
||||
provider: assertonly
|
||||
|
||||
- name: Ensure that the existing certificate is still valid at the winter solstice 2017
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
valid_at: 20171221162800Z
|
||||
|
||||
- name: Ensure that the existing certificate is still valid 2 weeks (1209600 seconds) from now
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
valid_in: 1209600
|
||||
|
||||
- name: Ensure that the existing certificate is only used for digital signatures and encrypting other keys
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
key_usage:
|
||||
- digitalSignature
|
||||
- keyEncipherment
|
||||
key_usage_strict: true
|
||||
|
||||
- name: Ensure that the existing certificate can be used for client authentication
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
extended_key_usage:
|
||||
- clientAuth
|
||||
|
||||
- name: Ensure that the existing certificate can only be used for client authentication and time stamping
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
extended_key_usage:
|
||||
- clientAuth
|
||||
- 1.3.6.1.5.5.7.3.8
|
||||
extended_key_usage_strict: true
|
||||
|
||||
- name: Ensure that the existing certificate has a certain domain in its subjectAltName
|
||||
community.crypto.x509_certificate:
|
||||
path: /etc/ssl/crt/example.com.crt
|
||||
provider: assertonly
|
||||
subject_alt_name:
|
||||
- www.example.com
|
||||
- test.example.com
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
|
@ -374,11 +240,6 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.module_bac
|
|||
add_acme_provider_to_argument_spec,
|
||||
)
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.certificate_assertonly import (
|
||||
AssertOnlyCertificateProvider,
|
||||
add_assertonly_provider_to_argument_spec,
|
||||
)
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.certificate_entrust import (
|
||||
EntrustCertificateProvider,
|
||||
add_entrust_provider_to_argument_spec,
|
||||
|
@ -495,7 +356,6 @@ class GenericCertificate(OpenSSLObject):
|
|||
def main():
|
||||
argument_spec = get_certificate_argument_spec()
|
||||
add_acme_provider_to_argument_spec(argument_spec)
|
||||
add_assertonly_provider_to_argument_spec(argument_spec)
|
||||
add_entrust_provider_to_argument_spec(argument_spec)
|
||||
add_ownca_provider_to_argument_spec(argument_spec)
|
||||
add_selfsigned_provider_to_argument_spec(argument_spec)
|
||||
|
@ -537,7 +397,6 @@ def main():
|
|||
provider = module.params['provider']
|
||||
provider_map = {
|
||||
'acme': AcmeCertificateProvider,
|
||||
'assertonly': AssertOnlyCertificateProvider,
|
||||
'entrust': EntrustCertificateProvider,
|
||||
'ownca': OwnCACertificateProvider,
|
||||
'selfsigned': SelfSignedCertificateProvider,
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
---
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Generate privatekey
|
||||
openssl_privatekey:
|
||||
path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
size: '{{ default_rsa_key_size_certifiates }}'
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Generate privatekey with password
|
||||
openssl_privatekey:
|
||||
path: '{{ remote_tmp_dir }}/privatekeypw.pem'
|
||||
passphrase: hunter2
|
||||
cipher: auto
|
||||
select_crypto_backend: cryptography
|
||||
size: '{{ default_rsa_key_size_certifiates }}'
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Generate CSR (no extensions)
|
||||
openssl_csr:
|
||||
path: '{{ remote_tmp_dir }}/csr_noext.csr'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
subject:
|
||||
commonName: www.example.com
|
||||
useCommonNameForSAN: no
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Generate CSR (with SANs)
|
||||
openssl_csr:
|
||||
path: '{{ remote_tmp_dir }}/csr_sans.csr'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
subject:
|
||||
commonName: www.example.com
|
||||
subject_alt_name:
|
||||
- "DNS:ansible.com"
|
||||
- "IP:127.0.0.1"
|
||||
- "IP:::1"
|
||||
useCommonNameForSAN: no
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Generate selfsigned certificate (no extensions)
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_noext.pem'
|
||||
csr_path: '{{ remote_tmp_dir }}/csr_noext.csr'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
provider: selfsigned
|
||||
selfsigned_digest: sha256
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Generate selfsigned certificate (with SANs)
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_sans.pem'
|
||||
csr_path: '{{ remote_tmp_dir }}/csr_sans.csr'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
provider: selfsigned
|
||||
selfsigned_digest: sha256
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Assert that subject_alt_name is there (should fail)
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_noext.pem'
|
||||
provider: assertonly
|
||||
subject_alt_name:
|
||||
- "DNS:example.com"
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
ignore_errors: yes
|
||||
register: extension_missing_san
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Assert that subject_alt_name is there
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_sans.pem'
|
||||
provider: assertonly
|
||||
subject_alt_name:
|
||||
- "DNS:ansible.com"
|
||||
- "IP:127.0.0.1"
|
||||
- "IP:::1"
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: extension_san
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Assert that subject_alt_name is there (strict)
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_sans.pem'
|
||||
provider: assertonly
|
||||
subject_alt_name:
|
||||
- "DNS:ansible.com"
|
||||
- "IP:127.0.0.1"
|
||||
- "IP:::1"
|
||||
subject_alt_name_strict: yes
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: extension_san_strict
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Assert that key_usage is there (should fail)
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_noext.pem'
|
||||
provider: assertonly
|
||||
key_usage:
|
||||
- digitalSignature
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
ignore_errors: yes
|
||||
register: extension_missing_ku
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Assert that extended_key_usage is there (should fail)
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_noext.pem'
|
||||
provider: assertonly
|
||||
extended_key_usage:
|
||||
- biometricInfo
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
ignore_errors: yes
|
||||
register: extension_missing_eku
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- extension_missing_san is failed
|
||||
- "'Found no subjectAltName extension' in extension_missing_san.msg"
|
||||
- extension_san is succeeded
|
||||
- extension_san_strict is succeeded
|
||||
- extension_missing_ku is failed
|
||||
- "'Found no keyUsage extension' in extension_missing_ku.msg"
|
||||
- extension_missing_eku is failed
|
||||
- "'Found no extendedKeyUsage extension' in extension_missing_eku.msg"
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Check wrong key fail
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_noext.pem'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekeypw.pem'
|
||||
privatekey_passphrase: hunter2
|
||||
provider: assertonly
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
ignore_errors: yes
|
||||
register: private_key_error
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Check private key passphrase fail 1
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_noext.pem'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
privatekey_passphrase: hunter2
|
||||
provider: assertonly
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
ignore_errors: yes
|
||||
register: passphrase_error_1
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Check private key passphrase fail 2
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_noext.pem'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekeypw.pem'
|
||||
privatekey_passphrase: wrong_password
|
||||
provider: assertonly
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
ignore_errors: yes
|
||||
register: passphrase_error_2
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) - Check private key passphrase fail 3
|
||||
x509_certificate:
|
||||
path: '{{ remote_tmp_dir }}/cert_noext.pem'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekeypw.pem'
|
||||
provider: assertonly
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
ignore_errors: yes
|
||||
register: passphrase_error_3
|
||||
|
||||
- name: (Assertonly, {{select_crypto_backend}}) -
|
||||
assert:
|
||||
that:
|
||||
- private_key_error is failed
|
||||
- "'Certificate and private key ' in private_key_error.msg and ' do not match' in private_key_error.msg"
|
||||
- passphrase_error_1 is failed
|
||||
- "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_1.msg"
|
||||
- passphrase_error_2 is failed
|
||||
- "'assphrase' in passphrase_error_2.msg or 'assword' in passphrase_error_2.msg or 'serializ' in passphrase_error_2.msg"
|
||||
- passphrase_error_3 is failed
|
||||
- "'assphrase' in passphrase_error_3.msg or 'assword' in passphrase_error_3.msg or 'serializ' in passphrase_error_3.msg"
|
|
@ -1,37 +0,0 @@
|
|||
---
|
||||
- name: (Expired, {{select_crypto_backend}}) Generate privatekey
|
||||
openssl_privatekey:
|
||||
path: '{{ remote_tmp_dir }}/has_expired_privatekey.pem'
|
||||
size: '{{ default_rsa_key_size_certifiates }}'
|
||||
|
||||
- name: (Expired, {{select_crypto_backend}}) Generate CSR
|
||||
openssl_csr:
|
||||
path: '{{ remote_tmp_dir }}/has_expired_csr.csr'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/has_expired_privatekey.pem'
|
||||
subject:
|
||||
commonName: www.example.com
|
||||
|
||||
- name: (Expired, {{select_crypto_backend}}) Generate expired selfsigned certificate
|
||||
# Cryptography won't allow creating expired certificates; so we create it with 'command'
|
||||
command: "{{ openssl_binary }} x509 -req -days -1 -in {{ remote_tmp_dir }}/has_expired_csr.csr -signkey {{ remote_tmp_dir }}/has_expired_privatekey.pem -out {{ remote_tmp_dir }}/has_expired_cert.pem"
|
||||
|
||||
- name: "(Expired) Check task fails because cert is expired (has_expired: false)"
|
||||
x509_certificate:
|
||||
provider: assertonly
|
||||
path: "{{ remote_tmp_dir }}/has_expired_cert.pem"
|
||||
has_expired: false
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
ignore_errors: true
|
||||
register: expired_cert_check
|
||||
|
||||
- name: (Expired, {{select_crypto_backend}}) Ensure previous task failed
|
||||
assert:
|
||||
that: expired_cert_check is failed
|
||||
|
||||
- name: "(Expired) Check expired cert check is ignored (has_expired: true)"
|
||||
x509_certificate:
|
||||
provider: assertonly
|
||||
path: "{{ remote_tmp_dir }}/has_expired_cert.pem"
|
||||
has_expired: true
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: expired_cert_skip
|
|
@ -1,8 +1,6 @@
|
|||
---
|
||||
- debug:
|
||||
msg: "Executing tests with backend {{ select_crypto_backend }}"
|
||||
- import_tasks: assertonly.yml
|
||||
- import_tasks: expired.yml
|
||||
- import_tasks: selfsigned.yml
|
||||
- import_tasks: ownca.yml
|
||||
- import_tasks: removal.yml
|
||||
|
|
|
@ -110,21 +110,27 @@
|
|||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
check_mode: yes
|
||||
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate
|
||||
x509_certificate:
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Get certificate information
|
||||
community.crypto.x509_certificate_info:
|
||||
path: '{{ remote_tmp_dir }}/ownca_cert.pem'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
provider: assertonly
|
||||
has_expired: False
|
||||
version: 3
|
||||
signature_algorithms:
|
||||
- sha256WithRSAEncryption
|
||||
- sha256WithECDSAEncryption
|
||||
subject:
|
||||
commonName: www.example.com
|
||||
issuer:
|
||||
commonName: Example CA
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: result
|
||||
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Get private key information
|
||||
community.crypto.openssl_privatekey_info:
|
||||
path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: result_privatekey
|
||||
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate
|
||||
assert:
|
||||
that:
|
||||
- result.public_key == result_privatekey.public_key
|
||||
- "result.signature_algorithm == 'sha256WithRSAEncryption' or result.signature_algorithm == 'sha256WithECDSAEncryption'"
|
||||
- "result.subject.commonName == 'www.example.com'"
|
||||
- "result.issuer.commonName == 'Example CA'"
|
||||
- not result.expired
|
||||
- result.version == 3
|
||||
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca v2 certificate
|
||||
x509_certificate:
|
||||
|
@ -151,33 +157,35 @@
|
|||
ownca_digest: sha256
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate2
|
||||
x509_certificate:
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Get certificate information
|
||||
community.crypto.x509_certificate_info:
|
||||
path: '{{ remote_tmp_dir }}/ownca_cert2.pem'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekey2.pem'
|
||||
provider: assertonly
|
||||
has_expired: False
|
||||
version: 3
|
||||
signature_algorithms:
|
||||
- sha256WithRSAEncryption
|
||||
- sha256WithECDSAEncryption
|
||||
subject:
|
||||
commonName: www.example.com
|
||||
C: US
|
||||
ST: California
|
||||
L: Los Angeles
|
||||
O: ACME Inc.
|
||||
OU:
|
||||
- Roadrunner pest control
|
||||
- Pyrotechnics
|
||||
keyUsage:
|
||||
- digitalSignature
|
||||
extendedKeyUsage:
|
||||
- ipsecUser
|
||||
- biometricInfo
|
||||
issuer:
|
||||
commonName: Example CA
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: result
|
||||
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Get private key information
|
||||
community.crypto.openssl_privatekey_info:
|
||||
path: '{{ remote_tmp_dir }}/privatekey2.pem'
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: result_privatekey
|
||||
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate2
|
||||
assert:
|
||||
that:
|
||||
- result.public_key == result_privatekey.public_key
|
||||
- "result.signature_algorithm == 'sha256WithRSAEncryption' or result.signature_algorithm == 'sha256WithECDSAEncryption'"
|
||||
- "result.subject.commonName == 'www.example.com'"
|
||||
- "result.subject.countryName == 'US'"
|
||||
- "result.subject.localityName == 'Los Angeles'" # L
|
||||
- "result.subject.organizationName == 'ACME Inc.'"
|
||||
- "['organizationalUnitName', 'Pyrotechnics'] in result.subject_ordered"
|
||||
- "['organizationalUnitName', 'Roadrunner pest control'] in result.subject_ordered"
|
||||
- "result.issuer.commonName == 'Example CA'"
|
||||
- not result.expired
|
||||
- result.version == 3
|
||||
- "'Digital Signature' in result.key_usage"
|
||||
- "'IPSec User' in result.extended_key_usage"
|
||||
- "'Biometric Info' in result.extended_key_usage"
|
||||
|
||||
- name: (OwnCA, {{select_crypto_backend}}) Create ownca certificate with notBefore and notAfter
|
||||
x509_certificate:
|
||||
|
|
|
@ -99,19 +99,26 @@
|
|||
check_mode: yes
|
||||
register: selfsigned_certificate_csr_minimal_change
|
||||
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Check selfsigned certificate
|
||||
x509_certificate:
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Get certificate information
|
||||
community.crypto.x509_certificate_info:
|
||||
path: '{{ remote_tmp_dir }}/cert.pem'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
provider: assertonly
|
||||
has_expired: False
|
||||
version: 3
|
||||
signature_algorithms:
|
||||
- sha256WithRSAEncryption
|
||||
- sha256WithECDSAEncryption
|
||||
subject:
|
||||
commonName: www.example.com
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: result
|
||||
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Get private key information
|
||||
community.crypto.openssl_privatekey_info:
|
||||
path: '{{ remote_tmp_dir }}/privatekey.pem'
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: result_privatekey
|
||||
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Check selfsigned certificate
|
||||
assert:
|
||||
that:
|
||||
- result.public_key == result_privatekey.public_key
|
||||
- "result.signature_algorithm == 'sha256WithRSAEncryption' or result.signature_algorithm == 'sha256WithECDSAEncryption'"
|
||||
- "result.subject.commonName == 'www.example.com'"
|
||||
- not result.expired
|
||||
- result.version == 3
|
||||
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned v2 certificate
|
||||
x509_certificate:
|
||||
|
@ -158,31 +165,34 @@
|
|||
selfsigned_digest: sha256
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Check selfsigned certificate2
|
||||
x509_certificate:
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Get certificate information
|
||||
community.crypto.x509_certificate_info:
|
||||
path: '{{ remote_tmp_dir }}/cert2.pem'
|
||||
privatekey_path: '{{ remote_tmp_dir }}/privatekey2.pem'
|
||||
provider: assertonly
|
||||
has_expired: False
|
||||
version: 3
|
||||
signature_algorithms:
|
||||
- sha256WithRSAEncryption
|
||||
- sha256WithECDSAEncryption
|
||||
subject:
|
||||
commonName: www.example.com
|
||||
C: US
|
||||
ST: California
|
||||
L: Los Angeles
|
||||
O: ACME Inc.
|
||||
OU:
|
||||
- Roadrunner pest control
|
||||
- Pyrotechnics
|
||||
keyUsage:
|
||||
- digitalSignature
|
||||
extendedKeyUsage:
|
||||
- ipsecUser
|
||||
- biometricInfo
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: result
|
||||
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Get private key information
|
||||
community.crypto.openssl_privatekey_info:
|
||||
path: '{{ remote_tmp_dir }}/privatekey2.pem'
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
register: result_privatekey
|
||||
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Check selfsigned certificate2
|
||||
assert:
|
||||
that:
|
||||
- result.public_key == result_privatekey.public_key
|
||||
- "result.signature_algorithm == 'sha256WithRSAEncryption' or result.signature_algorithm == 'sha256WithECDSAEncryption'"
|
||||
- "result.subject.commonName == 'www.example.com'"
|
||||
- "result.subject.countryName == 'US'"
|
||||
- "result.subject.localityName == 'Los Angeles'" # L
|
||||
- "result.subject.organizationName == 'ACME Inc.'"
|
||||
- "['organizationalUnitName', 'Pyrotechnics'] in result.subject_ordered"
|
||||
- "['organizationalUnitName', 'Roadrunner pest control'] in result.subject_ordered"
|
||||
- not result.expired
|
||||
- result.version == 3
|
||||
- "'Digital Signature' in result.key_usage"
|
||||
- "'IPSec User' in result.extended_key_usage"
|
||||
- "'Biometric Info' in result.extended_key_usage"
|
||||
|
||||
- name: (Selfsigned, {{select_crypto_backend}}) Create private key 3
|
||||
openssl_privatekey:
|
||||
|
|
Loading…
Reference in New Issue