From d6fb9da8ed992c09cac6bfaa30417166dba53191 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sun, 2 Feb 2020 12:42:52 +0100 Subject: [PATCH] openssl_* modules: allow direct input and output for some files (#65400) * Allow to return generated object. * Use slurp module instead of file lookup + b64encode. * Rename return_xxx_content -> return_content. --- changelogs/fragments/65400-openssl-output.yml | 7 +++ lib/ansible/module_utils/crypto.py | 20 +++++++ .../modules/crypto/openssl_certificate.py | 37 ++++++++++++ lib/ansible/modules/crypto/openssl_csr.py | 20 +++++++ lib/ansible/modules/crypto/openssl_dhparam.py | 16 ++++++ lib/ansible/modules/crypto/openssl_pkcs12.py | 21 +++++++ .../modules/crypto/openssl_privatekey.py | 33 +++++++++++ .../modules/crypto/openssl_publickey.py | 22 +++++++ .../openssl_certificate/tasks/ownca.yml | 2 + .../openssl_certificate/tasks/removal.yml | 2 + .../openssl_certificate/tasks/selfsigned.yml | 2 + .../tests/validate_ownca.yml | 6 ++ .../tests/validate_selfsigned.yml | 6 ++ .../targets/openssl_csr/tasks/impl.yml | 9 ++- .../targets/openssl_csr/tests/validate.yml | 13 ++++- .../targets/openssl_dhparam/tasks/impl.yml | 4 ++ .../openssl_dhparam/tests/validate.yml | 7 +++ .../targets/openssl_pkcs12/tasks/impl.yml | 14 +++++ .../targets/openssl_pkcs12/tests/validate.yml | 2 + .../targets/openssl_privatekey/tasks/impl.yml | 57 +++++++++++++++++++ .../openssl_privatekey/tests/validate.yml | 15 +++++ .../targets/openssl_publickey/tasks/impl.yml | 20 +++++++ .../openssl_publickey/tests/validate.yml | 15 +++++ 23 files changed, 346 insertions(+), 4 deletions(-) create mode 100644 changelogs/fragments/65400-openssl-output.yml diff --git a/changelogs/fragments/65400-openssl-output.yml b/changelogs/fragments/65400-openssl-output.yml new file mode 100644 index 00000000000..fd5e2bcf5e7 --- /dev/null +++ b/changelogs/fragments/65400-openssl-output.yml @@ -0,0 +1,7 @@ +minor_changes: +- "openssl_certificate - allow to return the existing/generated certificate directly as ``certificate`` by setting ``return_content`` to ``yes``." +- "openssl_csr - allow to return the existing/generated CSR directly as ``csr`` by setting ``return_content`` to ``yes``." +- "openssl_dhparam - allow to return the existing/generated DH params directly as ``dhparams`` by setting ``return_content`` to ``yes``." +- "openssl_pkcs12 - allow to return the existing/generated PKCS#12 directly as ``pkcs12`` by setting ``return_content`` to ``yes``." +- "openssl_privatekey - allow to return the existing/generated private key directly as ``privatekey`` by setting ``return_content`` to ``yes``." +- "openssl_publickey - allow to return the existing/generated public key directly as ``publickey`` by setting ``return_content`` to ``yes``." diff --git a/lib/ansible/module_utils/crypto.py b/lib/ansible/module_utils/crypto.py index 3467bee2d7f..6d4f8aac78d 100644 --- a/lib/ansible/module_utils/crypto.py +++ b/lib/ansible/module_utils/crypto.py @@ -187,6 +187,26 @@ def get_fingerprint(path, passphrase=None, content=None): return get_fingerprint_of_bytes(publickey) +def load_file_if_exists(path, module=None, ignore_errors=False): + try: + with open(path, 'rb') as f: + return f.read() + except EnvironmentError as exc: + if exc.errno == errno.ENOENT: + return None + if ignore_errors: + return None + if module is None: + raise + module.fail_json('Error while loading {0} - {1}'.format(path, str(exc))) + except Exception as exc: + if ignore_errors: + return None + if module is None: + raise + module.fail_json('Error while loading {0} - {1}'.format(path, str(exc))) + + def load_privatekey(path, passphrase=None, check_passphrase=True, content=None, backend='pyopenssl'): """Load the specified OpenSSL private key. diff --git a/lib/ansible/modules/crypto/openssl_certificate.py b/lib/ansible/modules/crypto/openssl_certificate.py index dcb0544583d..727925447ef 100644 --- a/lib/ansible/modules/crypto/openssl_certificate.py +++ b/lib/ansible/modules/crypto/openssl_certificate.py @@ -590,6 +590,13 @@ options: default: https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml version_added: "2.9" + return_content: + description: + - If set to C(yes), will return the (current or generated) certificate's content as I(certificate). + type: bool + default: no + version_added: "2.10" + extends_documentation_fragment: files notes: - All ASN.1 TIME values should be specified following the YYYYMMDDHHMMSSZ pattern. @@ -852,6 +859,11 @@ backup_file: returned: changed and if I(backup) is C(yes) type: str sample: /path/to/www.ansible.com.crt.2019-03-09@11:22~ +certificate: + description: The (current or generated) certificate's content. + returned: if I(state) is C(present) and I(return_content) is C(yes) + type: str + version_added: "2.10" ''' @@ -929,6 +941,7 @@ class Certificate(crypto_utils.OpenSSLObject): self.csr = None self.backend = backend self.module = module + self.return_content = module.params['return_content'] # The following are default values which make sure check() works as # before if providers do not explicitly change these properties. @@ -1115,6 +1128,8 @@ class CertificateAbsent(Certificate): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + result['certificate'] = None return result @@ -1230,6 +1245,9 @@ class SelfSignedCertificateCryptography(Certificate): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['certificate'] = content.decode('utf-8') if content else None if check_mode: result.update({ @@ -1327,6 +1345,9 @@ class SelfSignedCertificate(Certificate): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['certificate'] = content.decode('utf-8') if content else None if check_mode: result.update({ @@ -1521,6 +1542,9 @@ class OwnCACertificateCryptography(Certificate): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['certificate'] = content.decode('utf-8') if content else None if check_mode: result.update({ @@ -1644,6 +1668,9 @@ class OwnCACertificate(Certificate): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['certificate'] = content.decode('utf-8') if content else None if check_mode: result.update({ @@ -1959,6 +1986,9 @@ class AssertOnlyCertificateBase(Certificate): 'privatekey': self.privatekey_path, 'csr': self.csr_path, } + if self.return_content: + content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['certificate'] = content.decode('utf-8') if content else None return result @@ -2424,6 +2454,9 @@ class EntrustCertificate(Certificate): if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['certificate'] = content.decode('utf-8') if content else None result.update(self._get_cert_details()) @@ -2511,6 +2544,9 @@ class AcmeCertificate(Certificate): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['certificate'] = content.decode('utf-8') if content else None return result @@ -2526,6 +2562,7 @@ def main(): csr_content=dict(type='str'), backup=dict(type='bool', default=False), select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), + return_content=dict(type='bool', default=False), # General properties of a certificate privatekey_path=dict(type='path'), diff --git a/lib/ansible/modules/crypto/openssl_csr.py b/lib/ansible/modules/crypto/openssl_csr.py index d3a6b8cb995..ea2cf68c2a4 100644 --- a/lib/ansible/modules/crypto/openssl_csr.py +++ b/lib/ansible/modules/crypto/openssl_csr.py @@ -280,6 +280,12 @@ options: I(authority_cert_issuer) and I(authority_cert_serial_number) is specified. type: int version_added: "2.9" + return_content: + description: + - If set to C(yes), will return the (current or generated) CSR's content as I(csr). + type: bool + default: no + version_added: "2.10" extends_documentation_fragment: - files notes: @@ -419,6 +425,11 @@ backup_file: returned: changed and if I(backup) is C(yes) type: str sample: /path/to/www.ansible.com.csr.2019-03-09@11:22~ +csr: + description: The (current or generated) CSR's content. + returned: if I(state) is C(present) and I(return_content) is C(yes) + type: str + version_added: "2.10" ''' import abc @@ -510,6 +521,8 @@ class CertificateSigningRequestBase(crypto_utils.OpenSSLObject): self.authority_cert_serial_number = module.params['authority_cert_serial_number'] self.request = None self.privatekey = None + self.csr_bytes = None + self.return_content = module.params['return_content'] if self.create_subject_key_identifier and self.subject_key_identifier is not None: module.fail_json(msg='subject_key_identifier cannot be specified if create_subject_key_identifier is true') @@ -559,6 +572,8 @@ class CertificateSigningRequestBase(crypto_utils.OpenSSLObject): result = self._generate_csr() if self.backup: self.backup_file = module.backup_local(self.path) + if self.return_content: + self.csr_bytes = result crypto_utils.write_file(module, result) self.changed = True @@ -606,6 +621,10 @@ class CertificateSigningRequestBase(crypto_utils.OpenSSLObject): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + if self.csr_bytes is None: + self.csr_bytes = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['csr'] = self.csr_bytes.decode('utf-8') if self.csr_bytes else None return result @@ -1059,6 +1078,7 @@ def main(): authority_cert_issuer=dict(type='list', elements='str'), authority_cert_serial_number=dict(type='int'), select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), + return_content=dict(type='bool', default=False), ), required_together=[('authority_cert_issuer', 'authority_cert_serial_number')], required_if=[('state', 'present', ['privatekey_path', 'privatekey_content'], True)], diff --git a/lib/ansible/modules/crypto/openssl_dhparam.py b/lib/ansible/modules/crypto/openssl_dhparam.py index 4e7dca01399..5e06db97177 100644 --- a/lib/ansible/modules/crypto/openssl_dhparam.py +++ b/lib/ansible/modules/crypto/openssl_dhparam.py @@ -70,6 +70,12 @@ options: default: auto choices: [ auto, cryptography, openssl ] version_added: '2.10' + return_content: + description: + - If set to C(yes), will return the (current or generated) DH params' content as I(dhparams). + type: bool + default: no + version_added: "2.10" extends_documentation_fragment: - files seealso: @@ -112,6 +118,11 @@ backup_file: returned: changed and if I(backup) is C(yes) type: str sample: /path/to/dhparams.pem.2019-03-09@11:22~ +dhparams: + description: The (current or generated) DH params' content. + returned: if I(state) is C(present) and I(return_content) is C(yes) + type: str + version_added: "2.10" ''' import abc @@ -155,6 +166,7 @@ class DHParameterBase(object): self.size = module.params['size'] self.force = module.params['force'] self.changed = False + self.return_content = module.params['return_content'] self.backup = module.params['backup'] self.backup_file = None @@ -218,6 +230,9 @@ class DHParameterBase(object): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['dhparams'] = content.decode('utf-8') if content else None return result @@ -332,6 +347,7 @@ def main(): path=dict(type='path', required=True), backup=dict(type='bool', default=False), select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'openssl']), + return_content=dict(type='bool', default=False), ), supports_check_mode=True, add_file_common_args=True, diff --git a/lib/ansible/modules/crypto/openssl_pkcs12.py b/lib/ansible/modules/crypto/openssl_pkcs12.py index bf20f5a37dc..c80e616ef7a 100644 --- a/lib/ansible/modules/crypto/openssl_pkcs12.py +++ b/lib/ansible/modules/crypto/openssl_pkcs12.py @@ -95,6 +95,12 @@ options: type: bool default: no version_added: "2.8" + return_content: + description: + - If set to C(yes), will return the (current or generated) PKCS#12's content as I(pkcs12). + type: bool + default: no + version_added: "2.10" extends_documentation_fragment: - files seealso: @@ -169,8 +175,14 @@ backup_file: returned: changed and if I(backup) is C(yes) type: str sample: /path/to/ansible.com.pem.2019-03-09@11:22~ +pkcs12: + description: The (current or generated) PKCS#12's content Base64 encoded. + returned: if I(state) is C(present) and I(return_content) is C(yes) + type: str + version_added: "2.10" ''' +import base64 import stat import os import traceback @@ -212,6 +224,8 @@ class Pkcs(crypto_utils.OpenSSLObject): self.pkcs12 = None self.privatekey_passphrase = module.params['privatekey_passphrase'] self.privatekey_path = module.params['privatekey_path'] + self.pkcs12_bytes = None + self.return_content = module.params['return_content'] self.src = module.params['src'] if module.params['mode'] is None: @@ -294,6 +308,10 @@ class Pkcs(crypto_utils.OpenSSLObject): result['privatekey_path'] = self.privatekey_path if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + if self.pkcs12_bytes is None: + self.pkcs12_bytes = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['pkcs12'] = base64.b64encode(self.pkcs12_bytes) if self.pkcs12_bytes else None return result @@ -358,6 +376,8 @@ class Pkcs(crypto_utils.OpenSSLObject): if self.backup: self.backup_file = module.backup_local(self.path) crypto_utils.write_file(module, content, mode) + if self.return_content: + self.pkcs12_bytes = content def main(): @@ -376,6 +396,7 @@ def main(): state=dict(type='str', default='present', choices=['absent', 'present']), src=dict(type='path'), backup=dict(type='bool', default=False), + return_content=dict(type='bool', default=False), ) required_if = [ diff --git a/lib/ansible/modules/crypto/openssl_privatekey.py b/lib/ansible/modules/crypto/openssl_privatekey.py index f7632e923b2..77780370331 100644 --- a/lib/ansible/modules/crypto/openssl_privatekey.py +++ b/lib/ansible/modules/crypto/openssl_privatekey.py @@ -156,6 +156,14 @@ options: type: bool default: no version_added: "2.8" + return_content: + description: + - If set to C(yes), will return the (current or generated) private key's content as I(privatekey). + - Note that especially if the private key is not encrypted, you have to make sure that the returned + value is treated appropriately and not accidentally written to logs etc.! Use with care! + type: bool + default: no + version_added: "2.10" extends_documentation_fragment: - files seealso: @@ -232,9 +240,17 @@ backup_file: returned: changed and if I(backup) is C(yes) type: str sample: /path/to/privatekey.pem.2019-03-09@11:22~ +privatekey: + description: + - The (current or generated) private key's content. + - Will be Base64-encoded if the key is in raw format. + returned: if I(state) is C(present) and I(return_content) is C(yes) + type: str + version_added: "2.10" ''' import abc +import base64 import os import traceback from distutils.version import LooseVersion @@ -303,6 +319,8 @@ class PrivateKeyBase(crypto_utils.OpenSSLObject): self.fingerprint = {} self.format = module.params['format'] self.format_mismatch = module.params['format_mismatch'] + self.privatekey_bytes = None + self.return_content = module.params['return_content'] self.backup = module.params['backup'] self.backup_file = None @@ -333,6 +351,8 @@ class PrivateKeyBase(crypto_utils.OpenSSLObject): self.backup_file = module.backup_local(self.path) self._generate_private_key() privatekey_data = self._get_private_key_data() + if self.return_content: + self.privatekey_bytes = privatekey_data crypto_utils.write_file(module, privatekey_data, 0o600) self.changed = True elif not self.check(module, perms_required=False, ignore_conversion=False): @@ -340,6 +360,8 @@ class PrivateKeyBase(crypto_utils.OpenSSLObject): if self.backup: self.backup_file = module.backup_local(self.path) privatekey_data = self._get_private_key_data() + if self.return_content: + self.privatekey_bytes = privatekey_data crypto_utils.write_file(module, privatekey_data, 0o600) self.changed = True @@ -393,6 +415,16 @@ class PrivateKeyBase(crypto_utils.OpenSSLObject): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + if self.privatekey_bytes is None: + self.privatekey_bytes = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + if self.privatekey_bytes: + if crypto_utils.identify_private_key_format(self.privatekey_bytes) == 'raw': + result['privatekey'] = base64.b64encode(self.privatekey_bytes) + else: + result['privatekey'] = self.privatekey_bytes.decode('utf-8') + else: + result['privatekey'] = None return result @@ -744,6 +776,7 @@ def main(): format=dict(type='str', default='auto_ignore', choices=['pkcs1', 'pkcs8', 'raw', 'auto', 'auto_ignore']), format_mismatch=dict(type='str', default='regenerate', choices=['regenerate', 'convert']), select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'), + return_content=dict(type='bool', default=False), ), supports_check_mode=True, add_file_common_args=True, diff --git a/lib/ansible/modules/crypto/openssl_publickey.py b/lib/ansible/modules/crypto/openssl_publickey.py index 9511448fa40..687490a2e50 100644 --- a/lib/ansible/modules/crypto/openssl_publickey.py +++ b/lib/ansible/modules/crypto/openssl_publickey.py @@ -90,6 +90,12 @@ options: default: auto choices: [ auto, cryptography, pyopenssl ] version_added: "2.9" + return_content: + description: + - If set to C(yes), will return the (current or generated) public key's content as I(publickey). + type: bool + default: no + version_added: "2.10" extends_documentation_fragment: - files seealso: @@ -171,6 +177,11 @@ backup_file: returned: changed and if I(backup) is C(yes) type: str sample: /path/to/publickey.pem.2019-03-09@11:22~ +publickey: + description: The (current or generated) public key's content. + returned: if I(state) is C(present) and I(return_content) is C(yes) + type: str + version_added: "2.10" ''' import os @@ -229,6 +240,8 @@ class PublicKey(crypto_utils.OpenSSLObject): self.privatekey_content = self.privatekey_content.encode('utf-8') self.privatekey_passphrase = module.params['privatekey_passphrase'] self.privatekey = None + self.publickey_bytes = None + self.return_content = module.params['return_content'] self.fingerprint = {} self.backend = backend @@ -270,6 +283,8 @@ class PublicKey(crypto_utils.OpenSSLObject): if not self.check(module, perms_required=False) or self.force: try: publickey_content = self._create_publickey(module) + if self.return_content: + self.publickey_bytes = publickey_content if self.backup: self.backup_file = module.backup_local(self.path) @@ -302,6 +317,8 @@ class PublicKey(crypto_utils.OpenSSLObject): try: with open(self.path, 'rb') as public_key_fh: publickey_content = public_key_fh.read() + if self.return_content: + self.publickey_bytes = publickey_content if self.backend == 'cryptography': if self.format == 'OpenSSH': # Read and dump public key. Makes sure that the comment is stripped off. @@ -353,6 +370,10 @@ class PublicKey(crypto_utils.OpenSSLObject): } if self.backup_file: result['backup_file'] = self.backup_file + if self.return_content: + if self.publickey_bytes is None: + self.publickey_bytes = crypto_utils.load_file_if_exists(self.path, ignore_errors=True) + result['publickey'] = self.publickey_bytes.decode('utf-8') if self.publickey_bytes else None return result @@ -370,6 +391,7 @@ def main(): privatekey_passphrase=dict(type='str', no_log=True), backup=dict(type='bool', default=False), select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'), + return_content=dict(type='bool', default=False), ), supports_check_mode=True, add_file_common_args=True, diff --git a/test/integration/targets/openssl_certificate/tasks/ownca.yml b/test/integration/targets/openssl_certificate/tasks/ownca.yml index 99214b44075..151461a74cc 100644 --- a/test/integration/targets/openssl_certificate/tasks/ownca.yml +++ b/test/integration/targets/openssl_certificate/tasks/ownca.yml @@ -62,6 +62,7 @@ provider: ownca ownca_digest: sha256 select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: ownca_certificate - name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (idempotent) @@ -74,6 +75,7 @@ provider: ownca ownca_digest: sha256 select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: ownca_certificate_idempotence - name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (check mode) diff --git a/test/integration/targets/openssl_certificate/tasks/removal.yml b/test/integration/targets/openssl_certificate/tasks/removal.yml index f51d673fd91..e45e952d935 100644 --- a/test/integration/targets/openssl_certificate/tasks/removal.yml +++ b/test/integration/targets/openssl_certificate/tasks/removal.yml @@ -27,6 +27,7 @@ path: "{{ output_dir }}/removal_cert.pem" state: absent select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: removal_1 - name: "(Removal, {{select_crypto_backend}}) Check that file is gone" @@ -48,3 +49,4 @@ - removal_1 is changed - not removal_1_poststat.stat.exists - removal_2 is not changed + - removal_1.certificate is none diff --git a/test/integration/targets/openssl_certificate/tasks/selfsigned.yml b/test/integration/targets/openssl_certificate/tasks/selfsigned.yml index c370bede7d9..657d4e25777 100644 --- a/test/integration/targets/openssl_certificate/tasks/selfsigned.yml +++ b/test/integration/targets/openssl_certificate/tasks/selfsigned.yml @@ -32,6 +32,7 @@ provider: selfsigned selfsigned_digest: sha256 select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: selfsigned_certificate - name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate - idempotency @@ -42,6 +43,7 @@ provider: selfsigned selfsigned_digest: sha256 select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: selfsigned_certificate_idempotence - name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (check mode) diff --git a/test/integration/targets/openssl_certificate/tests/validate_ownca.yml b/test/integration/targets/openssl_certificate/tests/validate_ownca.yml index 72375c741b8..0a68b092d89 100644 --- a/test/integration/targets/openssl_certificate/tests/validate_ownca.yml +++ b/test/integration/targets/openssl_certificate/tests/validate_ownca.yml @@ -31,6 +31,12 @@ - ownca_certificate.notBefore == ownca_certificate_idempotence.notBefore - ownca_certificate.notAfter == ownca_certificate_idempotence.notAfter +- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca data return + assert: + that: + - ownca_certificate.certificate == lookup('file', output_dir ~ '/ownca_cert.pem', rstrip=False) + - ownca_certificate.certificate == ownca_certificate_idempotence.certificate + - block: - name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate v2 (test - ownca certificate version == 2) shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert_v2.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"' diff --git a/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml b/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml index 5143b01eb80..a178338fee5 100644 --- a/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml +++ b/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml @@ -30,6 +30,12 @@ - selfsigned_certificate.notBefore == selfsigned_certificate_idempotence.notBefore - selfsigned_certificate.notAfter == selfsigned_certificate_idempotence.notAfter +- name: (Selfsigned validation, {{select_crypto_backend}}) Validate data retrieval + assert: + that: + - selfsigned_certificate.certificate == lookup('file', output_dir ~ '/cert.pem', rstrip=False) + - selfsigned_certificate.certificate == selfsigned_certificate_idempotence.certificate + - name: Make sure that changes in CSR are detected even if private key is specified assert: that: diff --git a/test/integration/targets/openssl_csr/tasks/impl.yml b/test/integration/targets/openssl_csr/tasks/impl.yml index 20f8d516bcb..ef0bde709a7 100644 --- a/test/integration/targets/openssl_csr/tasks/impl.yml +++ b/test/integration/targets/openssl_csr/tasks/impl.yml @@ -10,6 +10,7 @@ subject: commonName: www.ansible.com select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes check_mode: yes register: generate_csr_check @@ -20,6 +21,7 @@ subject: commonName: www.ansible.com select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: generate_csr - name: Generate CSR (idempotent) @@ -29,7 +31,8 @@ subject: commonName: www.ansible.com select_crypto_backend: '{{ select_crypto_backend }}' - register: generate_csr_check_idempotent + return_content: yes + register: generate_csr_idempotent - name: Generate CSR (idempotent, check mode) openssl_csr: @@ -38,8 +41,9 @@ subject: commonName: www.ansible.com select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes check_mode: yes - register: generate_csr_check_idempotent_check + register: generate_csr_idempotent_check - name: Generate CSR without SAN (check mode) openssl_csr: @@ -330,6 +334,7 @@ state: absent backup: yes select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: csr_backup_4 - name: Generate CSR (remove, idempotent) openssl_csr: diff --git a/test/integration/targets/openssl_csr/tests/validate.yml b/test/integration/targets/openssl_csr/tests/validate.yml index 28458ba8984..f321837a40c 100644 --- a/test/integration/targets/openssl_csr/tests/validate.yml +++ b/test/integration/targets/openssl_csr/tests/validate.yml @@ -22,8 +22,16 @@ that: - generate_csr_check is changed - generate_csr is changed - - generate_csr_check_idempotent is not changed - - generate_csr_check_idempotent_check is not changed + - generate_csr_idempotent is not changed + - generate_csr_idempotent_check is not changed + +- name: Validate CSR (data retrieval) + assert: + that: + - generate_csr_check.csr is none + - generate_csr.csr == lookup('file', output_dir ~ '/csr.csr', rstrip=False) + - generate_csr.csr == generate_csr_idempotent.csr + - generate_csr.csr == generate_csr_idempotent_check.csr - name: Validate CSR without SAN (check mode, idempotency) assert: @@ -168,6 +176,7 @@ - csr_backup_4.backup_file is string - csr_backup_5 is not changed - csr_backup_5.backup_file is undefined + - csr_backup_4.csr is none - name: Check CSR with everything assert: diff --git a/test/integration/targets/openssl_dhparam/tasks/impl.yml b/test/integration/targets/openssl_dhparam/tasks/impl.yml index dcef8da59d5..d367436de4d 100644 --- a/test/integration/targets/openssl_dhparam/tasks/impl.yml +++ b/test/integration/targets/openssl_dhparam/tasks/impl.yml @@ -6,12 +6,15 @@ size: 768 path: '{{ output_dir }}/dh768.pem' select_crypto_backend: "{{ select_crypto_backend }}" + return_content: yes + register: dhparam - name: "[{{ select_crypto_backend }}] Don't regenerate parameters with no change" openssl_dhparam: size: 768 path: '{{ output_dir }}/dh768.pem' select_crypto_backend: "{{ select_crypto_backend }}" + return_content: yes register: dhparam_changed - name: "[{{ select_crypto_backend }}] Generate parameters with size option" @@ -87,6 +90,7 @@ state: absent backup: yes select_crypto_backend: "{{ select_crypto_backend }}" + return_content: yes register: dhparam_backup_4 - name: "[{{ select_crypto_backend }}] Generate params (remove, idempotent)" openssl_dhparam: diff --git a/test/integration/targets/openssl_dhparam/tests/validate.yml b/test/integration/targets/openssl_dhparam/tests/validate.yml index a670ab9ceff..e7d3951a233 100644 --- a/test/integration/targets/openssl_dhparam/tests/validate.yml +++ b/test/integration/targets/openssl_dhparam/tests/validate.yml @@ -31,6 +31,12 @@ - dhparam_changed_to_512 is changed - dhparam_changed_force is changed +- name: "[{{ select_crypto_backend }}] Make sure correct values are returned" + assert: + that: + - dhparam.dhparams == lookup('file', output_dir ~ '/dh768.pem', rstrip=False) + - dhparam.dhparams == dhparam_changed.dhparams + - name: "[{{ select_crypto_backend }}] Verify that broken params will be regenerated" assert: that: @@ -49,3 +55,4 @@ - dhparam_backup_4.backup_file is string - dhparam_backup_5 is not changed - dhparam_backup_5.backup_file is undefined + - dhparam_backup_4.dhparams is none diff --git a/test/integration/targets/openssl_pkcs12/tasks/impl.yml b/test/integration/targets/openssl_pkcs12/tasks/impl.yml index 492724fa399..cffa4a1c932 100644 --- a/test/integration/targets/openssl_pkcs12/tasks/impl.yml +++ b/test/integration/targets/openssl_pkcs12/tasks/impl.yml @@ -51,6 +51,7 @@ privatekey_path: "{{ output_dir }}/ansible_pkey.pem" certificate_path: "{{ output_dir }}/ansible.crt" state: present + return_content: yes register: p12_standard - name: 'Generate PKCS#12 file again, idempotency' @@ -60,8 +61,20 @@ privatekey_path: "{{ output_dir }}/ansible_pkey.pem" certificate_path: "{{ output_dir }}/ansible.crt" state: present + return_content: yes register: p12_standard_idempotency + - name: Read ansible.p12 + slurp: + src: "{{ output_dir }}/ansible.p12" + register: ansible_p12_content + + - name: 'Validate PKCS#12' + assert: + that: + - p12_standard.pkcs12 == ansible_p12_content.content + - p12_standard_idempotency.pkcs12 == p12_standard.pkcs12 + - name: 'Generate PKCS#12 file (force)' openssl_pkcs12: path: "{{ output_dir }}/ansible.p12" @@ -216,6 +229,7 @@ path: "{{ output_dir }}/ansible_backup.p12" state: absent backup: yes + return_content: yes register: p12_backup_4 - name: 'Generate PKCS#12 file (remove, idempotent)' openssl_pkcs12: diff --git a/test/integration/targets/openssl_pkcs12/tests/validate.yml b/test/integration/targets/openssl_pkcs12/tests/validate.yml index 37d6d72118a..baff2282c61 100644 --- a/test/integration/targets/openssl_pkcs12/tests/validate.yml +++ b/test/integration/targets/openssl_pkcs12/tests/validate.yml @@ -1,3 +1,4 @@ +--- - name: 'Validate PKCS#12' command: "openssl pkcs12 -info -in {{ output_dir }}/ansible.p12 -nodes -passin pass:''" register: p12 @@ -53,3 +54,4 @@ - p12_backup_4.backup_file is string - p12_backup_5 is not changed - p12_backup_5.backup_file is undefined + - p12_backup_4.pkcs12 is none diff --git a/test/integration/targets/openssl_privatekey/tasks/impl.yml b/test/integration/targets/openssl_privatekey/tasks/impl.yml index 8197b4cfac3..06416c95bf1 100644 --- a/test/integration/targets/openssl_privatekey/tasks/impl.yml +++ b/test/integration/targets/openssl_privatekey/tasks/impl.yml @@ -3,6 +3,15 @@ openssl_privatekey: path: '{{ output_dir }}/privatekey1.pem' select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes + register: privatekey1 + +- name: Generate privatekey1 - standard (idempotence) + openssl_privatekey: + path: '{{ output_dir }}/privatekey1.pem' + select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes + register: privatekey1_idempotence - name: Generate privatekey2 - size 2048 openssl_privatekey: @@ -27,6 +36,15 @@ state: absent path: '{{ output_dir }}/privatekey4.pem' select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes + register: privatekey4_delete + +- name: Delete privatekey4 - standard (idempotence) + openssl_privatekey: + state: absent + path: '{{ output_dir }}/privatekey4.pem' + select_crypto_backend: '{{ select_crypto_backend }}' + register: privatekey4_delete_idempotence - name: Generate privatekey5 - standard - with passphrase openssl_privatekey: @@ -381,30 +399,69 @@ type: X448 format: raw select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: privatekey_fmt_2_step_3 + - name: Read privatekey_fmt_2.pem + slurp: + src: "{{ output_dir }}/privatekey_fmt_2.pem" + register: content + + - name: Generate privatekey_fmt_2 - verify that returned content is base64 encoded + assert: + that: + - privatekey_fmt_2_step_3.privatekey == content.content + - name: Generate privatekey_fmt_2 - raw format (idempotent) openssl_privatekey: path: '{{ output_dir }}/privatekey_fmt_2.pem' type: X448 format: raw select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: privatekey_fmt_2_step_4 + - name: Read privatekey_fmt_2.pem + slurp: + src: "{{ output_dir }}/privatekey_fmt_2.pem" + register: content + + - name: Generate privatekey_fmt_2 - verify that returned content is base64 encoded + assert: + that: + - privatekey_fmt_2_step_4.privatekey == content.content + - name: Generate privatekey_fmt_2 - auto format (ignore) openssl_privatekey: path: '{{ output_dir }}/privatekey_fmt_2.pem' type: X448 format: auto_ignore select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: privatekey_fmt_2_step_5 + - name: Read privatekey_fmt_2.pem + slurp: + src: "{{ output_dir }}/privatekey_fmt_2.pem" + register: content + + - name: Generate privatekey_fmt_2 - verify that returned content is base64 encoded + assert: + that: + - privatekey_fmt_2_step_5.privatekey == content.content + - name: Generate privatekey_fmt_2 - auto format (no ignore) openssl_privatekey: path: '{{ output_dir }}/privatekey_fmt_2.pem' type: X448 format: auto select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes register: privatekey_fmt_2_step_6 + - name: Generate privatekey_fmt_2 - verify that returned content is not base64 encoded + assert: + that: + - privatekey_fmt_2_step_6.privatekey == lookup('file', output_dir ~ '/privatekey_fmt_2.pem', rstrip=False) + when: 'select_crypto_backend == "cryptography" and cryptography_version.stdout is version("2.6", ">=")' diff --git a/test/integration/targets/openssl_privatekey/tests/validate.yml b/test/integration/targets/openssl_privatekey/tests/validate.yml index 0c5f29fb129..b6594cf5d73 100644 --- a/test/integration/targets/openssl_privatekey/tests/validate.yml +++ b/test/integration/targets/openssl_privatekey/tests/validate.yml @@ -1,4 +1,12 @@ --- +- name: Validate privatekey1 idempotency and content returned + assert: + that: + - privatekey1_idempotence is not changed + - privatekey1.privatekey == lookup('file', output_dir ~ '/privatekey1.pem', rstrip=False) + - privatekey1.privatekey == privatekey1_idempotence.privatekey + + - name: Validate privatekey1 (test - RSA key with size 4096 bits) shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey1.pem | grep Private | sed 's/\\(RSA *\\)*Private-Key: (\\(.*\\) bit.*)/\\2/'" register: privatekey1 @@ -39,6 +47,13 @@ that: - privatekey4.stat.exists == False +- name: Validate privatekey4 removal behavior + assert: + that: + - privatekey4_delete is changed + - privatekey4_delete.privatekey is none + - privatekey4_delete_idempotence is not changed + - name: Validate privatekey5 (test - Passphrase protected key + idempotence) shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey5.pem -passin pass:ansible | grep Private | sed 's/\\(RSA *\\)*Private-Key: (\\(.*\\) bit.*)/\\2/'" diff --git a/test/integration/targets/openssl_publickey/tasks/impl.yml b/test/integration/targets/openssl_publickey/tasks/impl.yml index 8885cf8e56b..738013ff663 100644 --- a/test/integration/targets/openssl_publickey/tasks/impl.yml +++ b/test/integration/targets/openssl_publickey/tasks/impl.yml @@ -8,6 +8,16 @@ path: '{{ output_dir }}/publickey.pub' privatekey_path: '{{ output_dir }}/privatekey.pem' select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes + register: publickey + +- name: Generate publickey - PEM format (idempotence) + openssl_publickey: + path: '{{ output_dir }}/publickey.pub' + privatekey_path: '{{ output_dir }}/privatekey.pem' + select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes + register: publickey_idempotence - name: Generate publickey - OpenSSH format openssl_publickey: @@ -38,6 +48,16 @@ path: '{{ output_dir }}/publickey2.pub' privatekey_path: '{{ output_dir }}/privatekey.pem' select_crypto_backend: '{{ select_crypto_backend }}' + return_content: yes + register: publickey2_absent + +- name: Delete publickey2 - standard (idempotence) + openssl_publickey: + state: absent + path: '{{ output_dir }}/publickey2.pub' + privatekey_path: '{{ output_dir }}/privatekey.pem' + select_crypto_backend: '{{ select_crypto_backend }}' + register: publickey2_absent_idempotence - name: Generate privatekey3 - with passphrase openssl_privatekey: diff --git a/test/integration/targets/openssl_publickey/tests/validate.yml b/test/integration/targets/openssl_publickey/tests/validate.yml index a07b7b0499b..aaf9c100016 100644 --- a/test/integration/targets/openssl_publickey/tests/validate.yml +++ b/test/integration/targets/openssl_publickey/tests/validate.yml @@ -1,4 +1,12 @@ --- +- name: Validate publickey 1 idempotence and result behavior + assert: + that: + - publickey is changed + - publickey_idempotence is not changed + - publickey.publickey == lookup('file', output_dir ~ '/publickey.pub', rstrip=False) + - publickey.publickey == publickey_idempotence.publickey + - name: Validate public key (test - privatekey modulus) shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey.pem' register: privatekey_modulus @@ -45,6 +53,13 @@ that: - publickey2.stat.exists == False +- name: Validate publickey2 removal behavior + assert: + that: + - publickey2_absent is changed + - publickey2_absent_idempotence is not changed + - publickey2_absent.publickey is none + - name: Validate publickey3 (test - privatekey modulus) shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey3.pem -passin pass:ansible'