From c173449c46627d1711b317dee86db113ffc2e4af Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sat, 31 Dec 2022 07:56:34 +0100 Subject: [PATCH] Add x509_crl_info filter (#558) * Add x509_crl_info filter. * Work around bugs in Ansible 2.9 and ansible-base 2.10. --- plugins/filter/x509_crl_info.py | 196 ++++++++++ plugins/modules/x509_crl_info.py | 4 + .../targets/filter_x509_crl_info/aliases | 7 + .../filter_x509_crl_info/meta/main.yml | 8 + .../filter_x509_crl_info/tasks/impl.yml | 346 ++++++++++++++++++ .../filter_x509_crl_info/tasks/main.yml | 91 +++++ 6 files changed, 652 insertions(+) create mode 100644 plugins/filter/x509_crl_info.py create mode 100644 tests/integration/targets/filter_x509_crl_info/aliases create mode 100644 tests/integration/targets/filter_x509_crl_info/meta/main.yml create mode 100644 tests/integration/targets/filter_x509_crl_info/tasks/impl.yml create mode 100644 tests/integration/targets/filter_x509_crl_info/tasks/main.yml diff --git a/plugins/filter/x509_crl_info.py b/plugins/filter/x509_crl_info.py new file mode 100644 index 00000000..c8b02638 --- /dev/null +++ b/plugins/filter/x509_crl_info.py @@ -0,0 +1,196 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2022, Felix Fontein +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +name: x509_crl_info +short_description: Retrieve information from X.509 CRLs in PEM format +version_added: 2.10.0 +author: + - Felix Fontein (@felixfontein) +description: + - Provided a X.509 crl in PEM format, retrieve information. + - This is a filter version of the M(community.crypto.x509_crl_info) module. +options: + _input: + description: + - The content of the X.509 CRL in PEM format. + type: string + required: true + list_revoked_certificates: + description: + - If set to C(false), the list of revoked certificates is not included in the result. + - This is useful when retrieving information on large CRL files. Enumerating all revoked + certificates can take some time, including serializing the result as JSON, sending it to + the Ansible controller, and decoding it again. + type: bool + default: true + version_added: 1.7.0 +extends_documentation_fragment: + - community.crypto.name_encoding +seealso: + - module: community.crypto.x509_crl_info +''' + +EXAMPLES = ''' +- name: Show the Organization Name of the CRL's subject + ansible.builtin.debug: + msg: >- + {{ + ( + lookup('ansible.builtin.file', '/path/to/cert.pem') + | community.crypto.x509_crl_info + ).issuer.organizationName + }} +''' + +RETURN = ''' +_value: + description: + - Information on the CRL. + type: dict + contains: + format: + description: + - Whether the CRL is in PEM format (C(pem)) or in DER format (C(der)). + returned: success + type: str + sample: pem + issuer: + description: + - The CRL's issuer. + - Note that for repeated values, only the last one will be returned. + - See I(name_encoding) for how IDNs are handled. + returned: success + type: dict + sample: {"organizationName": "Ansible", "commonName": "ca.example.com"} + issuer_ordered: + description: The CRL's issuer as an ordered list of tuples. + returned: success + type: list + elements: list + sample: [["organizationName", "Ansible"], ["commonName": "ca.example.com"]] + last_update: + description: The point in time from which this CRL can be trusted as ASN.1 TIME. + returned: success + type: str + sample: '20190413202428Z' + next_update: + description: The point in time from which a new CRL will be issued and the client has to check for it as ASN.1 TIME. + returned: success + type: str + sample: '20190413202428Z' + digest: + description: The signature algorithm used to sign the CRL. + returned: success + type: str + sample: sha256WithRSAEncryption + revoked_certificates: + description: List of certificates to be revoked. + returned: success if I(list_revoked_certificates=true) + type: list + elements: dict + contains: + serial_number: + description: Serial number of the certificate. + type: int + sample: 1234 + revocation_date: + description: The point in time the certificate was revoked as ASN.1 TIME. + type: str + sample: '20190413202428Z' + issuer: + description: + - The certificate's issuer. + - See I(name_encoding) for how IDNs are handled. + type: list + elements: str + sample: ["DNS:ca.example.org"] + issuer_critical: + description: Whether the certificate issuer extension is critical. + type: bool + sample: false + reason: + description: + - The value for the revocation reason extension. + - One of C(unspecified), C(key_compromise), C(ca_compromise), C(affiliation_changed), C(superseded), + C(cessation_of_operation), C(certificate_hold), C(privilege_withdrawn), C(aa_compromise), and + C(remove_from_crl). + type: str + sample: key_compromise + reason_critical: + description: Whether the revocation reason extension is critical. + type: bool + sample: false + invalidity_date: + description: | + The point in time it was known/suspected that the private key was compromised + or that the certificate otherwise became invalid as ASN.1 TIME. + type: str + sample: '20190413202428Z' + invalidity_date_critical: + description: Whether the invalidity date extension is critical. + type: bool + sample: false +''' + +import base64 +import binascii + +from ansible.errors import AnsibleFilterError +from ansible.module_utils.six import string_types +from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text + +from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import ( + OpenSSLObjectError, +) + +from ansible_collections.community.crypto.plugins.module_utils.crypto.pem import ( + identify_pem_format, +) + +from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.crl_info import ( + get_crl_info, +) + +from ansible_collections.community.crypto.plugins.plugin_utils.filter_module import FilterModuleMock + + +def x509_crl_info_filter(data, name_encoding='ignore', list_revoked_certificates=True): + '''Extract information from X.509 PEM certificate.''' + if not isinstance(data, string_types): + raise AnsibleFilterError('The community.crypto.x509_crl_info input must be a text type, not %s' % type(data)) + if not isinstance(name_encoding, string_types): + raise AnsibleFilterError('The name_encoding option must be of a text type, not %s' % type(name_encoding)) + if not isinstance(list_revoked_certificates, bool): + raise AnsibleFilterError('The list_revoked_certificates option must be a boolean, not %s' % type(list_revoked_certificates)) + name_encoding = to_native(name_encoding) + if name_encoding not in ('ignore', 'idna', 'unicode'): + raise AnsibleFilterError('The name_encoding option must be one of the values "ignore", "idna", or "unicode", not "%s"' % name_encoding) + + data = to_bytes(data) + if not identify_pem_format(data): + try: + data = base64.b64decode(to_native(data)) + except (binascii.Error, TypeError, ValueError, UnicodeEncodeError) as e: + pass + + module = FilterModuleMock({'name_encoding': name_encoding}) + try: + return get_crl_info(module, content=data, list_revoked_certificates=list_revoked_certificates) + except OpenSSLObjectError as exc: + raise AnsibleFilterError(to_native(exc)) + + +class FilterModule(object): + '''Ansible jinja2 filters''' + + def filters(self): + return { + 'x509_crl_info': x509_crl_info_filter, + } diff --git a/plugins/modules/x509_crl_info.py b/plugins/modules/x509_crl_info.py index f7d140b7..7b0ebcac 100644 --- a/plugins/modules/x509_crl_info.py +++ b/plugins/modules/x509_crl_info.py @@ -50,6 +50,10 @@ notes: They are all in UTC. seealso: - module: community.crypto.x509_crl + - ref: community.crypto.x509_crl_info filter + # - plugin: community.crypto.x509_crl_info + # plugin_type: filter + description: A filter variant of this module. ''' EXAMPLES = r''' diff --git a/tests/integration/targets/filter_x509_crl_info/aliases b/tests/integration/targets/filter_x509_crl_info/aliases new file mode 100644 index 00000000..4602f118 --- /dev/null +++ b/tests/integration/targets/filter_x509_crl_info/aliases @@ -0,0 +1,7 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +azp/generic/2 +azp/posix/2 +destructive diff --git a/tests/integration/targets/filter_x509_crl_info/meta/main.yml b/tests/integration/targets/filter_x509_crl_info/meta/main.yml new file mode 100644 index 00000000..54bf29e9 --- /dev/null +++ b/tests/integration/targets/filter_x509_crl_info/meta/main.yml @@ -0,0 +1,8 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +dependencies: + - setup_openssl + - setup_remote_tmp_dir diff --git a/tests/integration/targets/filter_x509_crl_info/tasks/impl.yml b/tests/integration/targets/filter_x509_crl_info/tasks/impl.yml new file mode 100644 index 00000000..7456da3e --- /dev/null +++ b/tests/integration/targets/filter_x509_crl_info/tasks/impl.yml @@ -0,0 +1,346 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Create CRL 1 + x509_crl: + path: '{{ remote_tmp_dir }}/ca-crl1.crl' + privatekey_path: '{{ remote_tmp_dir }}/ca.key' + issuer: + CN: Ansible + last_update: 20191013000000Z + next_update: 20191113000000Z + revoked_certificates: + - path: '{{ remote_tmp_dir }}/cert-1.pem' + revocation_date: 20191013000000Z + - path: '{{ remote_tmp_dir }}/cert-2.pem' + revocation_date: 20191013000000Z + reason: key_compromise + reason_critical: yes + invalidity_date: 20191012000000Z + - serial_number: 1234 + revocation_date: 20191001000000Z + +- name: Retrieve CRL 1 infos + set_fact: + crl_1_info_1: >- + {{ lookup('file', remote_tmp_dir ~ '/ca-crl1.crl') | community.crypto.x509_crl_info }} + +- name: Retrieve CRL 1 infos + set_fact: + crl_1_info_2: >- + {{ lookup('file', remote_tmp_dir ~ '/ca-crl1.crl') | b64encode | community.crypto.x509_crl_info }} + +- name: Validate CRL 1 info + assert: + that: + - crl_1_info_1.format == 'pem' + - crl_1_info_1.digest == 'ecdsa-with-SHA256' + - crl_1_info_1.issuer | length == 1 + - crl_1_info_1.issuer.commonName == 'Ansible' + - crl_1_info_1.issuer_ordered | length == 1 + - crl_1_info_1.last_update == '20191013000000Z' + - crl_1_info_1.next_update == '20191113000000Z' + - crl_1_info_1.revoked_certificates | length == 3 + - crl_1_info_1.revoked_certificates[0].invalidity_date is none + - crl_1_info_1.revoked_certificates[0].invalidity_date_critical == false + - crl_1_info_1.revoked_certificates[0].issuer is none + - crl_1_info_1.revoked_certificates[0].issuer_critical == false + - crl_1_info_1.revoked_certificates[0].reason is none + - crl_1_info_1.revoked_certificates[0].reason_critical == false + - crl_1_info_1.revoked_certificates[0].revocation_date == '20191013000000Z' + - crl_1_info_1.revoked_certificates[0].serial_number == certificate_infos.results[0].serial_number + - crl_1_info_1.revoked_certificates[1].invalidity_date == '20191012000000Z' + - crl_1_info_1.revoked_certificates[1].invalidity_date_critical == false + - crl_1_info_1.revoked_certificates[1].issuer is none + - crl_1_info_1.revoked_certificates[1].issuer_critical == false + - crl_1_info_1.revoked_certificates[1].reason == 'key_compromise' + - crl_1_info_1.revoked_certificates[1].reason_critical == true + - crl_1_info_1.revoked_certificates[1].revocation_date == '20191013000000Z' + - crl_1_info_1.revoked_certificates[1].serial_number == certificate_infos.results[1].serial_number + - crl_1_info_1.revoked_certificates[2].invalidity_date is none + - crl_1_info_1.revoked_certificates[2].invalidity_date_critical == false + - crl_1_info_1.revoked_certificates[2].issuer is none + - crl_1_info_1.revoked_certificates[2].issuer_critical == false + - crl_1_info_1.revoked_certificates[2].reason is none + - crl_1_info_1.revoked_certificates[2].reason_critical == false + - crl_1_info_1.revoked_certificates[2].revocation_date == '20191001000000Z' + - crl_1_info_1.revoked_certificates[2].serial_number == 1234 + - crl_1_info_1 == crl_1_info_2 + +- name: Recreate CRL 1 as DER file + x509_crl: + path: '{{ remote_tmp_dir }}/ca-crl1.crl' + privatekey_path: '{{ remote_tmp_dir }}/ca.key' + format: der + issuer: + CN: Ansible + last_update: 20191013000000Z + next_update: 20191113000000Z + revoked_certificates: + - path: '{{ remote_tmp_dir }}/cert-1.pem' + revocation_date: 20191013000000Z + - path: '{{ remote_tmp_dir }}/cert-2.pem' + revocation_date: 20191013000000Z + reason: key_compromise + reason_critical: yes + invalidity_date: 20191012000000Z + - serial_number: 1234 + revocation_date: 20191001000000Z + +- name: Read ca-crl1.crl + slurp: + src: "{{ remote_tmp_dir }}/ca-crl1.crl" + register: content + +- name: Retrieve CRL 1 infos from DER (raw bytes) + set_fact: + crl_1_info_4: >- + {{ content.content | b64decode | community.crypto.x509_crl_info }} + # Ansible 2.9 and ansible-base 2.10 on Python 2 mangle bytes, so do not run this on these versions + when: ansible_version.string is version('2.11', '>=') or ansible_python.version.major > 2 + +- name: Retrieve CRL 1 infos from DER (Base64 encoded) + set_fact: + crl_1_info_5: >- + {{ content.content | community.crypto.x509_crl_info }} + +- name: Validate CRL 1 + assert: + that: + - crl_1_info_4 is not defined or crl_1_info_4.format == 'der' + - crl_1_info_5.format == 'der' + - crl_1_info_4 is not defined or crl_1_info_4 == crl_1_info_5 + +- name: Create CRL 2 + x509_crl: + path: '{{ remote_tmp_dir }}/ca-crl2.crl' + privatekey_path: '{{ remote_tmp_dir }}/ca.key' + issuer_ordered: + - CN: Ansible + - CN: CRL + - countryName: US + - CN: Test + last_update: +0d + next_update: +0d + revoked_certificates: + - path: '{{ remote_tmp_dir }}/cert-2.pem' + reason: key_compromise + reason_critical: yes + invalidity_date: 20191012000000Z + ignore_timestamps: no + mode: update + return_content: yes + register: crl_2_change + +- name: Retrieve CRL 2 infos + set_fact: + crl_2_info_1: >- + {{ lookup('file', remote_tmp_dir ~ '/ca-crl2.crl') | community.crypto.x509_crl_info(list_revoked_certificates=false) }} + +- name: Create CRL 2 (changed order) + x509_crl: + path: '{{ remote_tmp_dir }}/ca-crl2.crl' + privatekey_path: '{{ remote_tmp_dir }}/ca.key' + issuer_ordered: + - CN: Ansible + - countryName: US + - CN: CRL + - CN: Test + last_update: +0d + next_update: +0d + revoked_certificates: + - path: '{{ remote_tmp_dir }}/cert-2.pem' + reason: key_compromise + reason_critical: yes + invalidity_date: 20191012000000Z + ignore_timestamps: true + mode: update + return_content: yes + register: crl_2_change_order + +- name: Retrieve CRL 2 infos again + set_fact: + crl_2_info_2: >- + {{ lookup('file', remote_tmp_dir ~ '/ca-crl2.crl') | community.crypto.x509_crl_info(list_revoked_certificates=false) }} + +- name: Validate CRL 2 info + assert: + that: + - "'revoked_certificates' not in crl_2_info_1" + - > + crl_2_info_1.issuer_ordered == [ + ['commonName', 'Ansible'], + ['commonName', 'CRL'], + ['countryName', 'US'], + ['commonName', 'Test'], + ] + - > + crl_2_info_2.issuer_ordered == [ + ['commonName', 'Ansible'], + ['countryName', 'US'], + ['commonName', 'CRL'], + ['commonName', 'Test'], + ] + +- name: Create CRL 3 + x509_crl: + path: '{{ remote_tmp_dir }}/ca-crl3.crl' + privatekey_path: '{{ remote_tmp_dir }}/ca.key' + issuer: + CN: Ansible + last_update: +0d + next_update: +0d + revoked_certificates: + - serial_number: 1234 + revocation_date: 20191001000000Z + # * cryptography < 2.1 strips username and password from URIs. To avoid problems, we do + # not pass usernames and passwords for URIs when the cryptography version is < 2.1. + # * Python 3.5 before 3.5.8 rc 1 has a bug in urllib.parse.urlparse() that results in an + # error if a Unicode netloc has a username or password included. + # (https://github.com/ansible-collections/community.crypto/pull/436#issuecomment-1101737134) + # This affects the Python 3.5 included in Ansible 2.9's default test container; to avoid + # this, we also do not pass usernames and passwords for Python 3.5. + issuer: + - "DNS:ca.example.org" + - "DNS:ffóò.ḃâŗ.çøṁ" + - "email:foo@ḃâŗ.çøṁ" + - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'admin:hunter2@' }}ffóò.ḃâŗ.çøṁ/baz?foo=bar" + - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'goo@' }}www.straße.de" + - "URI:https://straße.de:8080" + - "URI:http://gefäß.org" + - "URI:http://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'a:b@' }}ä:1" + issuer_critical: true + register: crl_3 + +- name: Create CRL 3 (IDNA encoding) + x509_crl: + path: '{{ remote_tmp_dir }}/ca-crl3.crl' + privatekey_path: '{{ remote_tmp_dir }}/ca.key' + issuer: + CN: Ansible + last_update: +0d + next_update: +0d + revoked_certificates: + - serial_number: 1234 + revocation_date: 20191001000000Z + issuer: + - "DNS:ca.example.org" + - "DNS:xn--ff-3jad.xn--2ca8uh37e.xn--7ca8a981n" + - "email:foo@xn--2ca8uh37e.xn--7ca8a981n" + - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'admin:hunter2@' }}xn--ff-3jad.xn--2ca8uh37e.xn--7ca8a981n/baz?foo=bar" + - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'goo@' }}www.xn--strae-oqa.de" + - "URI:https://xn--strae-oqa.de:8080" + - "URI:http://xn--gef-7kay.org" + - "URI:http://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'a:b@' }}xn--4ca:1" + issuer_critical: true + ignore_timestamps: true + name_encoding: idna + register: crl_3_idna + +- name: Create CRL 3 (Unicode encoding) + x509_crl: + path: '{{ remote_tmp_dir }}/ca-crl3.crl' + privatekey_path: '{{ remote_tmp_dir }}/ca.key' + issuer: + CN: Ansible + last_update: +0d + next_update: +0d + revoked_certificates: + - serial_number: 1234 + revocation_date: 20191001000000Z + issuer: + - "DNS:ca.example.org" + - "DNS:ffóò.ḃâŗ.çøṁ" + - "email:foo@ḃâŗ.çøṁ" + - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'admin:hunter2@' }}ffóò.ḃâŗ.çøṁ/baz?foo=bar" + - "URI:https://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'goo@' }}www.straße.de" + - "URI:https://straße.de:8080" + - "URI:http://gefäß.org" + - "URI:http://{{ '' if cryptography_version.stdout is version('2.1', '<') or ansible_facts.python.version.minor == 5 else 'a:b@' }}ä:1" + issuer_critical: true + ignore_timestamps: true + name_encoding: unicode + register: crl_3_unicode + +- name: Retrieve CRL 3 infos + set_fact: + crl_3_info: >- + {{ lookup('file', remote_tmp_dir ~ '/ca-crl3.crl') | community.crypto.x509_crl_info(list_revoked_certificates=true) }} + crl_3_info_idna: >- + {{ lookup('file', remote_tmp_dir ~ '/ca-crl3.crl') | community.crypto.x509_crl_info(list_revoked_certificates=true, name_encoding='idna') }} + crl_3_info_unicode: >- + {{ lookup('file', remote_tmp_dir ~ '/ca-crl3.crl') | community.crypto.x509_crl_info(list_revoked_certificates=true, name_encoding='unicode') }} + +- name: Validate CRL 3 info + assert: + that: + - crl_3.revoked_certificates == crl_3_info.revoked_certificates + - crl_3_idna.revoked_certificates == crl_3_info_idna.revoked_certificates + - crl_3_unicode.revoked_certificates == crl_3_info_unicode.revoked_certificates + +- name: Get invalid CRL info + set_fact: + result: >- + {{ [] | community.crypto.x509_crl_info }} + ignore_errors: true + register: output + +- name: Check that task failed and error message is OK + assert: + that: + - output is failed + - output.msg is search("^The community.crypto.x509_crl_info input must be a text type, not <(?:class|type) 'list'>$") + +- name: Get invalid CRL info + set_fact: + result: >- + {{ 'foo' | community.crypto.x509_crl_info }} + ignore_errors: true + register: output + +- name: Check that task failed and error message is OK + assert: + that: + - output is failed + - output.msg is search("^Error while decoding CRL") + +- name: Get invalid CRL info + set_fact: + result: >- + {{ 'foo' | community.crypto.x509_crl_info(name_encoding=[]) }} + ignore_errors: true + register: output + +- name: Check that task failed and error message is OK + assert: + that: + - output is failed + - output.msg is search("^The name_encoding option must be of a text type, not <(?:class|type) 'list'>$") + +- name: Get invalid name_encoding parameter + set_fact: + result: >- + {{ 'bar' | community.crypto.x509_crl_info(name_encoding='foo') }} + ignore_errors: true + register: output + +- name: Check that task failed and error message is OK + assert: + that: + - output is failed + - output.msg is search("^The name_encoding option must be one of the values \"ignore\", \"idna\", or \"unicode\", not \"foo\"$") + +- name: Get invalid list_revoked_certificates parameter + set_fact: + result: >- + {{ 'bar' | community.crypto.x509_crl_info(list_revoked_certificates=[]) }} + ignore_errors: true + register: output + +- name: Check that task failed and error message is OK + assert: + that: + - output is failed + - output.msg is search("^The list_revoked_certificates option must be a boolean, not <(?:class|type) 'list'>$") diff --git a/tests/integration/targets/filter_x509_crl_info/tasks/main.yml b/tests/integration/targets/filter_x509_crl_info/tasks/main.yml new file mode 100644 index 00000000..f043956f --- /dev/null +++ b/tests/integration/targets/filter_x509_crl_info/tasks/main.yml @@ -0,0 +1,91 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +- name: Make sure the Python idna library is installed + pip: + name: idna + state: present + +- set_fact: + certificates: + - name: ca + subject: + commonName: Ansible + is_ca: yes + - name: ca-2 + subject: + commonName: Ansible Other CA + is_ca: yes + - name: cert-1 + subject_alt_name: + - DNS:ansible.com + - name: cert-2 + subject_alt_name: + - DNS:example.com + - name: cert-3 + subject_alt_name: + - DNS:example.org + - IP:1.2.3.4 + - name: cert-4 + subject_alt_name: + - DNS:test.ansible.com + - DNS:b64.ansible.com + +- name: Generate private keys + openssl_privatekey: + path: '{{ remote_tmp_dir }}/{{ item.name }}.key' + type: ECC + curve: secp256r1 + loop: "{{ certificates }}" + +- name: Generate CSRs + openssl_csr: + path: '{{ remote_tmp_dir }}/{{ item.name }}.csr' + privatekey_path: '{{ remote_tmp_dir }}/{{ item.name }}.key' + subject: "{{ item.subject | default(omit) }}" + subject_alt_name: "{{ item.subject_alt_name | default(omit) }}" + basic_constraints: "{{ 'CA:TRUE' if item.is_ca | default(false) else omit }}" + use_common_name_for_san: no + loop: "{{ certificates }}" + +- name: Generate CA certificates + x509_certificate: + path: '{{ remote_tmp_dir }}/{{ item.name }}.pem' + csr_path: '{{ remote_tmp_dir }}/{{ item.name }}.csr' + privatekey_path: '{{ remote_tmp_dir }}/{{ item.name }}.key' + provider: selfsigned + loop: "{{ certificates }}" + when: item.is_ca | default(false) + +- name: Generate other certificates + x509_certificate: + path: '{{ remote_tmp_dir }}/{{ item.name }}.pem' + csr_path: '{{ remote_tmp_dir }}/{{ item.name }}.csr' + provider: ownca + ownca_path: '{{ remote_tmp_dir }}/ca.pem' + ownca_privatekey_path: '{{ remote_tmp_dir }}/ca.key' + loop: "{{ certificates }}" + when: not (item.is_ca | default(false)) + +- name: Get certificate infos + x509_certificate_info: + path: '{{ remote_tmp_dir }}/{{ item }}.pem' + loop: + - cert-1 + - cert-2 + - cert-3 + - cert-4 + register: certificate_infos + +- block: + - name: Running tests + include_tasks: impl.yml + + when: cryptography_version.stdout is version('1.2', '>=')