From 4ee9f40e6230bf9068ff3d6fad24de71929ad458 Mon Sep 17 00:00:00 2001 From: Matthew Davis Date: Wed, 6 Nov 2019 05:57:08 +1100 Subject: [PATCH] Add aws_acm module (#60552) * convert aws_acm_facts to AnsibleAWSModule * factor aws_acm_facts into module_utils * add more filtering options for aws_acm_info * add aws_acm module and tests * uncomment aws_acm test * fix linting for aws_acm * fix __future__ linting for aws_acm * fix linting for aws_acm * fix linting for aws_acm * fix linting for aws_acm * fix linting for aws_acm * fix aws_acm_info arg type * remove test for old module name aws_acm_facts * simplify AWS ACM client creation * fix indent typo in aws_acm test * catch BotoCoreError in aws_acm * fix indent typo in aws_acm test * tighten AWS ACM test policy resource * move aws acm int test to venv * remove errant file * fix AWS ACM int test perms * undo copyright addition to wrong file * fix invalid log message in aws_acm Co-Authored-By: Jill R <4121322+jillr@users.noreply.github.com> * rephrase aws_acm_info doc from facts to information Co-Authored-By: Jill R <4121322+jillr@users.noreply.github.com> * rename aws_facts var to aws_info * remove case insensitivity for aws_acm pem compare * add no_log for aws_acm credential setting * add per-test prefix to aws_acm test resource names * make aws_acm use crypto module_util * clarify copyright for aws_acm * make aws_acm int test clearer * add explicit crypto dependency to aws_acm * change requests for aws_acm pr * fix wrong copyright owner aws_acm test * fix wrong copyright owner aws_acm test * rewrite aws_acm cert chain compare with regex, no dependency * fix linting for aws_acm unit test * fix linting for aws_acm unit test * fix linting and duplicate ignore * fix failed cert chain split in aws_acm, add more tests * remove errant file * more linting fixes for aws_acm * fix sanity ignore * rewrite cert compare in aws_acm to use base64 decode * improve regex for pem cert chain split in aws_acm * undo changes to crypto module util for aws_acm * increment ansible version for new aws_acm module * convert aws_acm return(x) to return x * increment version added for aws_acm_info new features * fix linting * fix bugs with AWS ACM * fix bad rebase * disable AWS ACM integration test, due to AWS account limit issue * remove aws acm integration test from shippable group --- .../testing_policies/security-policy.json | 25 + lib/ansible/module_utils/aws/acm.py | 213 ++++++++ lib/ansible/modules/cloud/amazon/aws_acm.py | 403 ++++++++++++++ .../modules/cloud/amazon/aws_acm_info.py | 135 ++--- test/integration/targets/aws_acm/aliases | 3 + .../targets/aws_acm/defaults/main.yml | 40 ++ .../integration/targets/aws_acm/meta/main.yml | 2 + .../targets/aws_acm/tasks/full_acm_test.yml | 504 ++++++++++++++++++ .../targets/aws_acm/tasks/main.yml | 33 ++ .../modules/cloud/amazon/fixtures/certs/a.pem | 31 ++ .../modules/cloud/amazon/fixtures/certs/b.pem | 47 ++ .../amazon/fixtures/certs/chain-1.0.cert | 121 +++++ .../amazon/fixtures/certs/chain-1.1.cert | 69 +++ .../amazon/fixtures/certs/chain-1.2.cert | 113 ++++ .../amazon/fixtures/certs/chain-1.3.cert | 124 +++++ .../amazon/fixtures/certs/chain-1.4.cert | 86 +++ .../cloud/amazon/fixtures/certs/chain-4.cert | 121 +++++ .../amazon/fixtures/certs/simple-chain-a.cert | 18 + .../amazon/fixtures/certs/simple-chain-b.cert | 18 + .../modules/cloud/amazon/test_aws_acm.py | 122 +++++ 20 files changed, 2140 insertions(+), 88 deletions(-) create mode 100644 lib/ansible/module_utils/aws/acm.py create mode 100644 lib/ansible/modules/cloud/amazon/aws_acm.py create mode 100644 test/integration/targets/aws_acm/aliases create mode 100644 test/integration/targets/aws_acm/defaults/main.yml create mode 100644 test/integration/targets/aws_acm/meta/main.yml create mode 100644 test/integration/targets/aws_acm/tasks/full_acm_test.yml create mode 100644 test/integration/targets/aws_acm/tasks/main.yml create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/a.pem create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/b.pem create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/chain-1.0.cert create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/chain-1.1.cert create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/chain-1.2.cert create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/chain-1.3.cert create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/chain-1.4.cert create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/chain-4.cert create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/simple-chain-a.cert create mode 100644 test/units/modules/cloud/amazon/fixtures/certs/simple-chain-b.cert create mode 100644 test/units/modules/cloud/amazon/test_aws_acm.py diff --git a/hacking/aws_config/testing_policies/security-policy.json b/hacking/aws_config/testing_policies/security-policy.json index e38842ef55a..f0d0768a7a2 100644 --- a/hacking/aws_config/testing_policies/security-policy.json +++ b/hacking/aws_config/testing_policies/security-policy.json @@ -195,6 +195,31 @@ "arn:aws:iam::{{ aws_account }}:user/ansible-test*", "arn:aws:iam::{{ aws_account }}:group/ansible-test*" ] + }, + { + "Sid": "AllowAccessToACMRestrictable", + "Effect": "Allow", + "Action": [ + "acm:ImportCertificate", + "acm:DescribeCertificate", + "acm:GetCertificate", + "acm:AddTagsToCertificate", + "acm:DeleteCertificate" + ], + "Resource": [ + "arn:aws:acm:{{aws_region}}:{{aws_account}}:certificate/*" + ] + }, + { + "Sid": "AllowAccessToACMUnrestrictable", + "Effect": "Allow", + "Action": [ + "acm:ListCertificates", + "acm:ListTagsForCertificate" + ], + "Resource": [ + "*" + ] } ] } diff --git a/lib/ansible/module_utils/aws/acm.py b/lib/ansible/module_utils/aws/acm.py new file mode 100644 index 00000000000..ed38027998b --- /dev/null +++ b/lib/ansible/module_utils/aws/acm.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +# This module is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software. If not, see . +# +# Author: +# - Matthew Davis +# on behalf of Telstra Corporation Limited +# +# Common functionality to be used by the modules: +# - acm + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +""" +Common Amazon Certificate Manager facts shared between modules +""" +import traceback +from ansible.module_utils.ec2 import get_aws_connection_info, boto3_conn +from ansible.module_utils.ec2 import camel_dict_to_snake_dict, AWSRetry, HAS_BOTO3, boto3_tag_list_to_ansible_dict, ansible_dict_to_boto3_tag_list +from ansible.module_utils._text import to_bytes + + +try: + import botocore + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # caught by imported HAS_BOTO3 + + +class ACMServiceManager(object): + """Handles ACM Facts Services""" + + def __init__(self, module): + self.module = module + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) + self.client = module.client('acm') + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) + def delete_certificate_with_backoff(self, client, arn): + client.delete_certificate(CertificateArn=arn) + + def delete_certificate(self, client, module, arn): + module.debug("Attempting to delete certificate %s" % arn) + try: + self.delete_certificate_with_backoff(client, arn) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't delete certificate %s" % arn) + module.debug("Successfully deleted certificate %s" % arn) + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) + def list_certificates_with_backoff(self, client, statuses=None): + paginator = client.get_paginator('list_certificates') + kwargs = dict() + if statuses: + kwargs['CertificateStatuses'] = statuses + return paginator.paginate(**kwargs).build_full_result()['CertificateSummaryList'] + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) + def get_certificate_with_backoff(self, client, certificate_arn): + response = client.get_certificate(CertificateArn=certificate_arn) + # strip out response metadata + return {'Certificate': response['Certificate'], + 'CertificateChain': response['CertificateChain']} + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) + def describe_certificate_with_backoff(self, client, certificate_arn): + return client.describe_certificate(CertificateArn=certificate_arn)['Certificate'] + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) + def list_certificate_tags_with_backoff(self, client, certificate_arn): + return client.list_tags_for_certificate(CertificateArn=certificate_arn)['Tags'] + + # Returns a list of certificates + # if domain_name is specified, returns only certificates with that domain + # if an ARN is specified, returns only that certificate + # only_tags is a dict, e.g. {'key':'value'}. If specified this function will return + # only certificates which contain all those tags (key exists, value matches). + def get_certificates(self, client, module, domain_name=None, statuses=None, arn=None, only_tags=None): + try: + all_certificates = self.list_certificates_with_backoff(client=client, statuses=statuses) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't obtain certificates") + if domain_name: + certificates = [cert for cert in all_certificates + if cert['DomainName'] == domain_name] + else: + certificates = all_certificates + + if arn: + # still return a list, not just one item + certificates = [c for c in certificates if c['CertificateArn'] == arn] + + results = [] + for certificate in certificates: + try: + cert_data = self.describe_certificate_with_backoff(client, certificate['CertificateArn']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't obtain certificate metadata for domain %s" % certificate['DomainName']) + + # in some states, ACM resources do not have a corresponding cert + if cert_data['Status'] not in ['PENDING_VALIDATION', 'VALIDATION_TIMED_OUT', 'FAILED']: + try: + cert_data.update(self.get_certificate_with_backoff(client, certificate['CertificateArn'])) + except (BotoCoreError, ClientError, KeyError) as e: + if e.response['Error']['Code'] != "RequestInProgressException": + module.fail_json_aws(e, msg="Couldn't obtain certificate data for domain %s" % certificate['DomainName']) + cert_data = camel_dict_to_snake_dict(cert_data) + try: + tags = self.list_certificate_tags_with_backoff(client, certificate['CertificateArn']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't obtain tags for domain %s" % certificate['DomainName']) + + cert_data['tags'] = boto3_tag_list_to_ansible_dict(tags) + results.append(cert_data) + + if only_tags: + for tag_key in only_tags: + try: + results = [c for c in results if ('tags' in c) and (tag_key in c['tags']) and (c['tags'][tag_key] == only_tags[tag_key])] + except (TypeError, AttributeError) as e: + for c in results: + if 'tags' not in c: + module.debug("cert is %s" % str(c)) + module.fail_json(msg="ACM tag filtering err", exception=e) + + return results + + # returns the domain name of a certificate (encoded in the public cert) + # for a given ARN + # A cert with that ARN must already exist + def get_domain_of_cert(self, client, module, arn): + if arn is None: + module.fail(msg="Internal error with ACM domain fetching, no certificate ARN specified") + try: + cert_data = self.describe_certificate_with_backoff(client=client, certificate_arn=arn) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't obtain certificate data for arn %s" % arn) + return cert_data['DomainName'] + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) + def import_certificate_with_backoff(self, client, certificate, private_key, certificate_chain, arn): + if certificate_chain: + if arn: + ret = client.import_certificate(Certificate=to_bytes(certificate), + PrivateKey=to_bytes(private_key), + CertificateChain=to_bytes(certificate_chain), + CertificateArn=arn) + else: + ret = client.import_certificate(Certificate=to_bytes(certificate), + PrivateKey=to_bytes(private_key), + CertificateChain=to_bytes(certificate_chain)) + else: + if arn: + ret = client.import_certificate(Certificate=to_bytes(certificate), + PrivateKey=to_bytes(private_key), + CertificateArn=arn) + else: + ret = client.import_certificate(Certificate=to_bytes(certificate), + PrivateKey=to_bytes(private_key)) + return ret['CertificateArn'] + + # Tags are a normal Ansible style dict + # {'Key':'Value'} + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) + def tag_certificate_with_backoff(self, client, arn, tags): + aws_tags = ansible_dict_to_boto3_tag_list(tags) + client.add_tags_to_certificate(CertificateArn=arn, Tags=aws_tags) + + def import_certificate(self, client, module, certificate, private_key, arn=None, certificate_chain=None, tags=None): + + original_arn = arn + + # upload cert + try: + arn = self.import_certificate_with_backoff(client, certificate, private_key, certificate_chain, arn) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't upload new certificate") + + if original_arn and (arn != original_arn): + # I'm not sure whether the API guarentees that the ARN will not change + # I'm failing just in case. + # If I'm wrong, I'll catch it in the integration tests. + module.fail_json(msg="ARN changed with ACM update, from %s to %s" % (original_arn, arn)) + + # tag that cert + try: + self.tag_certificate_with_backoff(client, arn, tags) + except (BotoCoreError, ClientError) as e: + module.debug("Attempting to delete the cert we just created, arn=%s" % arn) + try: + self.delete_certificate_with_backoff(client, arn) + except Exception as f: + module.warn("Certificate %s exists, and is not tagged. So Ansible will not see it on the next run.") + module.fail_json_aws(e, msg="Couldn't tag certificate %s, couldn't delete it either" % arn) + module.fail_json_aws(e, msg="Couldn't tag certificate %s" % arn) + + return arn diff --git a/lib/ansible/modules/cloud/amazon/aws_acm.py b/lib/ansible/modules/cloud/amazon/aws_acm.py new file mode 100644 index 00000000000..baef9f7f788 --- /dev/null +++ b/lib/ansible/modules/cloud/amazon/aws_acm.py @@ -0,0 +1,403 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +# This module is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software. If not, see . +# +# Author: +# - Matthew Davis +# on behalf of Telstra Corporation Limited + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +module: aws_acm +short_description: Upload and delete certificates in the AWS Certificate Manager service +description: + - Import and delete certificates in Amazon Web Service's Certificate Manager (AWS ACM). + - > + This module does not currently interact with AWS-provided certificates. + It currently only manages certificates provided to AWS by the user. + - The ACM API allows users to upload multiple certificates for the same domain name, + and even multiple identical certificates. + This module attempts to restrict such freedoms, to be idempotent, as per the Ansible philosophy. + It does this through applying AWS resource "Name" tags to ACM certificates. + - > + When C(state=present), + if there is one certificate in ACM + with a C(Name) tag equal to the C(name_tag) parameter, + and an identical body and chain, + this task will succeed without effect. + - > + When C(state=present), + if there is one certificate in ACM + a C(Name) tag equal to the C(name_tag) parameter, + and a different body, + this task will overwrite that certificate. + - > + When C(state=present), + if there are multiple certificates in ACM + with a C(Name) tag equal to the C(name_tag) parameter, + this task will fail. + - > + When C(state=absent) and C(certificate_arn) is defined, + this module will delete the ACM resource with that ARN if it exists in this region, + and succeed without effect if it doesn't exist. + - > + When C(state=absent) and C(domain_name) is defined, + this module will delete all ACM resources in this AWS region with a corresponding domain name. + If there are none, it will succeed without effect. + - > + When C(state=absent) and C(certificate_arn) is not defined, + and C(domain_name) is not defined, + this module will delete all ACM resources in this AWS region with a corresponding C(Name) tag. + If there are none, it will succeed without effect. + - Note that this may not work properly with keys of size 4096 bits, due to a limitation of the ACM API. +version_added: "2.10" +options: + certificate: + description: + - The body of the PEM encoded public certificate. + - Required when C(state) is not C(absent). + - If your certificate is in a file, use C(lookup('file', 'path/to/cert.pem')). + type: str + + certificate_arn: + description: + - The ARN of a certificate in ACM to delete + - Ignored when C(state=present). + - If C(state=absent), you must provide one of C(certificate_arn), C(domain_name) or C(name_tag). + - > + If C(state=absent) and no resource exists with this ARN in this region, + the task will succeed with no effect. + - > + If C(state=absent) and the corresponding resource exists in a different region, + this task may report success without deleting that resource. + type: str + + certificate_chain: + description: + - The body of the PEM encoded chain for your certificate. + - If your certificate chain is in a file, use C(lookup('file', 'path/to/chain.pem')). + - Ignored when C(state=absent) + type: str + + domain_name: + description: + - The domain name of the certificate. + - > + If C(state=absent) and C(domain_name) is specified, + this task will delete all ACM certificates with this domain. + - Exactly one of C(domain_name), C(name_tag) and C(certificate_arn) must be provided. + - > + If C(state=present) this must not be specified. + (Since the domain name is encoded within the public certificate's body.) + type: str + + name_tag: + description: + - The unique identifier for tagging resources using AWS tags, with key C(Name). + - This can be any set of characters accepted by AWS for tag values. + - > + This is to ensure Ansible can treat certificates idempotently, + even though the ACM API allows duplicate certificates. + - If C(state=preset), this must be specified. + - > + If C(state=absent), you must provide exactly one of + C(certificate_arn), C(domain_name) or C(name_tag). + type: str + + private_key: + description: + - The body of the PEM encoded private key. + - Required when C(state) is C(present). + - Ignored when C(state) is C(absent). + - If your private key is in a file, use C(lookup('file', 'path/to/key.pem')). + type: str + + state: + description: + - > + If C(state=present), the specified public certificate and private key + will be uploaded, with C(Name) tag equal to C(name_tag). + - > + If C(state=absent), any certificates in this region + with a corresponding C(domain_name), C(name_tag) or C(certificate_arn) + will be deleted. + choices: [present, absent] + default: present + type: str +requirements: + - boto3 +author: + - Matthew Davis (@matt-telstra) on behalf of Telstra Corporation Limited +extends_documentation_fragment: + - aws + - ec2 +''' + +EXAMPLES = ''' + +- name: upload a self-signed certificate + aws_acm: + certificate: "{{ lookup('file', 'cert.pem' ) }}" + privateKey: "{{ lookup('file', 'key.pem' ) }}" + name_tag: my_cert # to be applied through an AWS tag as "Name":"my_cert" + region: ap-southeast-2 # AWS region + +- name: create/update a certificate with a chain + aws_acm: + certificate: "{{ lookup('file', 'cert.pem' ) }}" + privateKey: "{{ lookup('file', 'key.pem' ) }}" + name_tag: my_cert + certificate_chain: "{{ lookup('file', 'chain.pem' ) }}" + state: present + region: ap-southeast-2 + register: cert_create + +- name: print ARN of cert we just created + debug: + var: cert_create.certificate.arn + +- name: delete the cert we just created + aws_acm: + name_tag: my_cert + state: absent + region: ap-southeast-2 + +- name: delete a certificate with a particular ARN + aws_acm: + certificate_arn: "arn:aws:acm:ap-southeast-2:123456789012:certificate/01234567-abcd-abcd-abcd-012345678901" + state: absent + region: ap-southeast-2 + +- name: delete all certificates with a particular domain name + aws_acm: + domain_name: acm.ansible.com + state: absent + region: ap-southeast-2 + +''' + +RETURN = ''' +certificate: + description: Information about the certificate which was uploaded + type: complex + returned: when C(state) is C(present) + contains: + arn: + description: The ARN of the certificate in ACM + type: str + returned: when C(state) is C(present) + sample: "arn:aws:acm:ap-southeast-2:123456789012:certificate/01234567-abcd-abcd-abcd-012345678901" + domain_name: + description: The domain name encoded within the public certificate + type: str + returned: when C(state) is C(present) + sample: acm.ansible.com +arns: + description: A list of the ARNs of the certificates in ACM which were deleted + type: list + returned: when C(state) is C(absent) + sample: + - "arn:aws:acm:ap-southeast-2:123456789012:certificate/01234567-abcd-abcd-abcd-012345678901" +''' + + +from ansible.module_utils.aws.core import AnsibleAWSModule +from ansible.module_utils.ec2 import boto3_conn, ec2_argument_spec, get_aws_connection_info +from ansible.module_utils.aws.acm import ACMServiceManager +from ansible.module_utils._text import to_bytes, to_text +from ssl import PEM_cert_to_DER_cert +import base64 +import traceback +import re # regex library + + +# Takes in two text arguments +# Each a PEM encoded certificate +# Or a chain of PEM encoded certificates +# May include some lines between each chain in the cert, e.g. "Subject: ..." +# Returns True iff the chains/certs are functionally identical (including chain order) +def chain_compare(module, a, b): + + chain_a_pem = pem_chain_split(module, a) + chain_b_pem = pem_chain_split(module, b) + + if len(chain_a_pem) != len(chain_b_pem): + return False + + # Chain length is the same + for (ca, cb) in zip(chain_a_pem, chain_b_pem): + der_a = PEM_body_to_DER(module, ca) + der_b = PEM_body_to_DER(module, cb) + if der_a != der_b: + return False + + return True + + +# Takes in PEM encoded data with no headers +# returns equivilent DER as byte array +def PEM_body_to_DER(module, pem): + try: + der = base64.b64decode(to_text(pem)) + except (ValueError, TypeError) as e: + module.fail_json(msg="Unable to decode certificate chain", + exception=traceback.format_exc()) + return der + + +# Store this globally to avoid repeated recompilation +pem_chain_split_regex = re.compile(r"------?BEGIN [A-Z0-9. ]*CERTIFICATE------?([a-zA-Z0-9\+\/=\s]+)------?END [A-Z0-9. ]*CERTIFICATE------?") + + +# Use regex to split up a chain or single cert into an array of base64 encoded data +# Using "-----BEGIN CERTIFICATE-----" and "----END CERTIFICATE----" +# Noting that some chains have non-pem data in between each cert +# This function returns only what's between the headers, excluding the headers +def pem_chain_split(module, pem): + + pem_arr = re.findall(pem_chain_split_regex, to_text(pem)) + + if len(pem_arr) == 0: + # This happens if the regex doesn't match at all + module.fail_json(msg="Unable to split certificate chain. Possibly zero-length chain?") + + return pem_arr + + +def main(): + argument_spec = dict( + certificate=dict(), + certificate_arn=dict(alias=['arn']), + certificate_chain=dict(), + domain_name=dict(alias=['domain']), + name_tag=dict(alias=['name']), + private_key=dict(no_log=True), + state=dict(default='present', choices=['present', 'absent']) + ) + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + acm = ACMServiceManager(module) + + # Check argument requirements + if module.params['state'] == 'present': + if not module.params['certificate']: + module.fail_json(msg="Parameter 'certificate' must be specified if 'state' is specified as 'present'") + elif module.params['certificate_arn']: + module.fail_json(msg="Parameter 'certificate_arn' is only valid if parameter 'state' is specified as 'absent'") + elif not module.params['name_tag']: + module.fail_json(msg="Parameter 'name_tag' must be specified if parameter 'state' is specified as 'present'") + elif not module.params['private_key']: + module.fail_json(msg="Parameter 'private_key' must be specified if 'state' is specified as 'present'") + else: # absent + + # exactly one of these should be specified + absent_args = ['certificate_arn', 'domain_name', 'name_tag'] + if sum([(module.params[a] is not None) for a in absent_args]) != 1: + for a in absent_args: + module.debug("%s is %s" % (a, module.params[a])) + module.fail_json(msg="If 'state' is specified as 'absent' then exactly one of 'name_tag', certificate_arn' or 'domain_name' must be specified") + + if module.params['name_tag']: + tags = dict(Name=module.params['name_tag']) + else: + tags = None + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) + client = boto3_conn(module, conn_type='client', resource='acm', + region=region, endpoint=ec2_url, **aws_connect_kwargs) + + # fetch the list of certificates currently in ACM + certificates = acm.get_certificates(client=client, + module=module, + domain_name=module.params['domain_name'], + arn=module.params['certificate_arn'], + only_tags=tags) + + module.debug("Found %d corresponding certificates in ACM" % len(certificates)) + + if module.params['state'] == 'present': + if len(certificates) > 1: + msg = "More than one certificate with Name=%s exists in ACM in this region" % module.params['name_tag'] + module.fail_json(msg=msg, certificates=certificates) + elif len(certificates) == 1: + # update the existing certificate + module.debug("Existing certificate found in ACM") + old_cert = certificates[0] # existing cert in ACM + if ('tags' not in old_cert) or ('Name' not in old_cert['tags']) or (old_cert['tags']['Name'] != module.params['name_tag']): + # shouldn't happen + module.fail_json(msg="Internal error, unsure which certificate to update", certificate=old_cert) + + if 'certificate' not in old_cert: + # shouldn't happen + module.fail_json(msg="Internal error, unsure what the existing cert in ACM is", certificate=old_cert) + + # Are the existing certificate in ACM and the local certificate the same? + same = True + same &= chain_compare(module, old_cert['certificate'], module.params['certificate']) + if module.params['certificate_chain']: + # Need to test this + # not sure if Amazon appends the cert itself to the chain when self-signed + same &= chain_compare(module, old_cert['certificate_chain'], module.params['certificate_chain']) + else: + # When there is no chain with a cert + # it seems Amazon returns the cert itself as the chain + same &= chain_compare(module, old_cert['certificate_chain'], module.params['certificate']) + + if same: + module.debug("Existing certificate in ACM is the same, doing nothing") + domain = acm.get_domain_of_cert(client=client, module=module, arn=old_cert['certificate_arn']) + module.exit_json(certificate=dict(domain_name=domain, arn=old_cert['certificate_arn']), changed=False) + else: + module.debug("Existing certificate in ACM is different, overwriting") + + # update cert in ACM + arn = acm.import_certificate(client, module, + certificate=module.params['certificate'], + private_key=module.params['private_key'], + certificate_chain=module.params['certificate_chain'], + arn=old_cert['certificate_arn'], + tags=tags) + domain = acm.get_domain_of_cert(client=client, module=module, arn=arn) + module.exit_json(certificate=dict(domain_name=domain, arn=arn), changed=True) + else: # len(certificates) == 0 + module.debug("No certificate in ACM. Creating new one.") + arn = acm.import_certificate(client=client, + module=module, + certificate=module.params['certificate'], + private_key=module.params['private_key'], + certificate_chain=module.params['certificate_chain'], + tags=tags) + domain = acm.get_domain_of_cert(client=client, module=module, arn=arn) + + module.exit_json(certificate=dict(domain_name=domain, arn=arn), changed=True) + + else: # state == absent + for cert in certificates: + acm.delete_certificate(client, module, cert['certificate_arn']) + module.exit_json(arns=[cert['certificate_arn'] for cert in certificates], + changed=(len(certificates) > 0)) + + +if __name__ == '__main__': + # tests() + main() diff --git a/lib/ansible/modules/cloud/amazon/aws_acm_info.py b/lib/ansible/modules/cloud/amazon/aws_acm_info.py index 63ec193f5e8..62f301e8d5e 100644 --- a/lib/ansible/modules/cloud/amazon/aws_acm_info.py +++ b/lib/ansible/modules/cloud/amazon/aws_acm_info.py @@ -15,8 +15,18 @@ short_description: Retrieve certificate information from AWS Certificate Manager description: - Retrieve information for ACM certificates - This module was called C(aws_acm_facts) before Ansible 2.9. The usage did not change. + - Note that this will not return information about uploaded keys of size 4096 bits, due to a limitation of the ACM API. version_added: "2.5" options: + certificate_arn: + description: + - If provided, the results will be filtered to show only the certificate with this ARN. + - If no certificate with this ARN exists, this task will fail. + - If a certificate with this ARN exists in a different region, this task will fail + aliases: + - arn + version_added: '2.10' + type: str domain_name: description: - The domain name of an ACM certificate to limit the search to @@ -29,6 +39,11 @@ options: choices: ['PENDING_VALIDATION', 'ISSUED', 'INACTIVE', 'EXPIRED', 'VALIDATION_TIMED_OUT', 'REVOKED', 'FAILED'] type: list elements: str + tags: + description: + - Filter results to show only certificates with tags that match all the tags specified here. + type: dict + version_added: '2.10' requirements: - boto3 author: @@ -50,6 +65,19 @@ EXAMPLES = ''' aws_acm_info: statuses: - PENDING_VALIDATION + +- name: obtain all certificates with tag Name=foo and myTag=bar + aws_acm_info: + tags: + Name: foo + myTag: bar + + +# The output is still a list of certificates, just one item long. +- name: obtain information about a certificate with a particular ARN + aws_acm_info: + certificate_arn: "arn:aws:acm:ap-southeast-2:123456789876:certificate/abcdeabc-abcd-1234-4321-abcdeabcde12" + ''' RETURN = ''' @@ -232,106 +260,37 @@ certificates: type: str ''' -import traceback - -from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.aws.core import AnsibleAWSModule from ansible.module_utils.ec2 import boto3_conn, ec2_argument_spec, get_aws_connection_info -from ansible.module_utils.ec2 import camel_dict_to_snake_dict, AWSRetry, HAS_BOTO3, boto3_tag_list_to_ansible_dict - - -try: - import botocore -except ImportError: - pass # caught by imported HAS_BOTO3 - - -@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) -def list_certificates_with_backoff(client, statuses=None): - paginator = client.get_paginator('list_certificates') - kwargs = dict() - if statuses: - kwargs['CertificateStatuses'] = statuses - return paginator.paginate(**kwargs).build_full_result()['CertificateSummaryList'] - - -@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) -def get_certificate_with_backoff(client, certificate_arn): - response = client.get_certificate(CertificateArn=certificate_arn) - # strip out response metadata - return {'Certificate': response['Certificate'], - 'CertificateChain': response['CertificateChain']} - - -@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) -def describe_certificate_with_backoff(client, certificate_arn): - return client.describe_certificate(CertificateArn=certificate_arn)['Certificate'] - - -@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) -def list_certificate_tags_with_backoff(client, certificate_arn): - return client.list_tags_for_certificate(CertificateArn=certificate_arn)['Tags'] - - -def get_certificates(client, module, domain_name=None, statuses=None): - try: - all_certificates = list_certificates_with_backoff(client, statuses) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't obtain certificates", - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) - if domain_name: - certificates = [cert for cert in all_certificates - if cert['DomainName'] == domain_name] - else: - certificates = all_certificates - - results = [] - for certificate in certificates: - try: - cert_data = describe_certificate_with_backoff(client, certificate['CertificateArn']) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't obtain certificate metadata for domain %s" % certificate['DomainName'], - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) - try: - cert_data.update(get_certificate_with_backoff(client, certificate['CertificateArn'])) - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] != "RequestInProgressException": - module.fail_json(msg="Couldn't obtain certificate data for domain %s" % certificate['DomainName'], - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) - cert_data = camel_dict_to_snake_dict(cert_data) - try: - tags = list_certificate_tags_with_backoff(client, certificate['CertificateArn']) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't obtain tags for domain %s" % certificate['DomainName'], - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) - cert_data['tags'] = boto3_tag_list_to_ansible_dict(tags) - results.append(cert_data) - return results +from ansible.module_utils.aws.acm import ACMServiceManager def main(): - argument_spec = ec2_argument_spec() - argument_spec.update( - dict( - domain_name=dict(aliases=['name']), - statuses=dict(type='list', choices=['PENDING_VALIDATION', 'ISSUED', 'INACTIVE', 'EXPIRED', 'VALIDATION_TIMED_OUT', 'REVOKED', 'FAILED']), - ) + argument_spec = dict( + certificate_arn=dict(aliases=['arn']), + domain_name=dict(aliases=['name']), + statuses=dict(type='list', choices=['PENDING_VALIDATION', 'ISSUED', 'INACTIVE', 'EXPIRED', 'VALIDATION_TIMED_OUT', 'REVOKED', 'FAILED']), + tags=dict(type='dict'), ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + acm_info = ACMServiceManager(module) + if module._name == 'aws_acm_facts': module.deprecate("The 'aws_acm_facts' module has been renamed to 'aws_acm_info'", version='2.13') - if not HAS_BOTO3: - module.fail_json(msg='boto3 and botocore are required by this module') - region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) client = boto3_conn(module, conn_type='client', resource='acm', region=region, endpoint=ec2_url, **aws_connect_kwargs) - certificates = get_certificates(client, module, domain_name=module.params['domain_name'], statuses=module.params['statuses']) + certificates = acm_info.get_certificates(client, module, + domain_name=module.params['domain_name'], + statuses=module.params['statuses'], + arn=module.params['certificate_arn'], + only_tags=module.params['tags']) + + if module.params['certificate_arn'] and len(certificates) != 1: + module.fail_json(msg="No certificate exists in this region with ARN %s" % module.params['certificate_arn']) + module.exit_json(certificates=certificates) diff --git a/test/integration/targets/aws_acm/aliases b/test/integration/targets/aws_acm/aliases new file mode 100644 index 00000000000..4ea906bd1c3 --- /dev/null +++ b/test/integration/targets/aws_acm/aliases @@ -0,0 +1,3 @@ +cloud/aws +aws_acm_info +unsupported diff --git a/test/integration/targets/aws_acm/defaults/main.yml b/test/integration/targets/aws_acm/defaults/main.yml new file mode 100644 index 00000000000..a159302ca53 --- /dev/null +++ b/test/integration/targets/aws_acm/defaults/main.yml @@ -0,0 +1,40 @@ +--- +# we'll generate 3 certificates locally for the test +# Upload the first +# overwrite it with the second +# and the third is unrelated, to check we only get info about the first when we want +local_certs: + - priv_key: "{{ remote_tmp_dir }}/private-1.pem" + cert: "{{ remote_tmp_dir }}/public-1.pem" + csr: "{{ remote_tmp_dir }}/csr-1.csr" + domain: acm1.ansible.com + name: "{{ resource_prefix }}_1" + + - priv_key: "{{ remote_tmp_dir }}/private-2.pem" + cert: "{{ remote_tmp_dir }}/public-2.pem" + csr: "{{ remote_tmp_dir }}/csr-2.csr" + domain: acm2.ansible.com + name: "{{ resource_prefix }}_2" + + - priv_key: "{{ remote_tmp_dir }}/private-3.pem" + cert: "{{ remote_tmp_dir }}/public-3.pem" + csr: "{{ remote_tmp_dir }}/csr-3.csr" + domain: acm3.ansible.com + name: "{{ resource_prefix }}_3" + +# we'll have one private key +# make 2 chains using it +# so we can test what happens when you change just the chain +# not the domain or key +chained_cert: + priv_key: "{{ remote_tmp_dir }}/private-ch-0.pem" + domain: acm-ch.ansible.com + name: "{{ resource_prefix }}_4" + chains: + - cert: "{{ remote_tmp_dir }}/public-ch-0.pem" + csr: "{{ remote_tmp_dir }}/csr-ch-0.csr" + ca: 0 # index into local_certs + - cert: "{{ remote_tmp_dir }}/public-ch-1.pem" + csr: "{{ remote_tmp_dir }}/csr-ch-1.csr" + ca: 1 # index into local_certs + \ No newline at end of file diff --git a/test/integration/targets/aws_acm/meta/main.yml b/test/integration/targets/aws_acm/meta/main.yml new file mode 100644 index 00000000000..1810d4bec98 --- /dev/null +++ b/test/integration/targets/aws_acm/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_remote_tmp_dir diff --git a/test/integration/targets/aws_acm/tasks/full_acm_test.yml b/test/integration/targets/aws_acm/tasks/full_acm_test.yml new file mode 100644 index 00000000000..64589a945d6 --- /dev/null +++ b/test/integration/targets/aws_acm/tasks/full_acm_test.yml @@ -0,0 +1,504 @@ +- name: AWS ACM integration test + block: + + - set_fact: + aws_connection_info: &aws_connection_info + aws_region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token }}" + no_log: True + + # just check this task doesn't fail + # I'm not sure if I can assume there aren't already other certs in this account + - name: list certs + aws_acm_info: + <<: *aws_connection_info + register: list_all + failed_when: list_all.certificates is not defined + + - name: ensure absent cert which doesn't exist - first time + aws_acm: + <<: *aws_connection_info + name_tag: "{{ item.name }}" + state: absent + with_items: "{{ local_certs }}" + + # just in case it actually existed and was deleted last task + # check we don't fail when deleting nothing + - name: ensure absent cert which doesn't exist - second time + aws_acm: + <<: *aws_connection_info + name_tag: "{{ item.name }}" + state: absent + with_items: "{{ local_certs }}" + register: absent_start_two + failed_when: absent_start_two.changed + + - name: list cert which shouldn't exist + aws_acm_info: + <<: *aws_connection_info + tags: + Name: "{{ item.name }}" + register: list_tag + with_items: "{{ local_certs }}" + failed_when: list_tag.certificates | length > 0 + + - name: check directory was made + assert: + that: + - remote_tmp_dir is defined + + # https://github.com/vbotka/ansible-certificate/blob/master/tasks/cert-self-signed.yml + - name: Generate private key for local certs + openssl_privatekey: + path: "{{ item.priv_key }}" + type: RSA + size: 2048 # ACM doesn't work properly with 4096 + with_items: "{{ local_certs }}" + + - name: Generate an OpenSSL Certificate Signing Request for own certs + openssl_csr: + path: "{{ item.csr }}" + privatekey_path: "{{ item.priv_key }}" + common_name: "{{ item.domain }}" + with_items: "{{ local_certs }}" + + - name: Generate a Self Signed OpenSSL certificate for own certs + openssl_certificate: + provider: selfsigned + path: "{{ item.cert }}" + csr_path: "{{ item.csr }}" + privatekey_path: "{{ item.priv_key }}" + signature_algorithms: + - 'sha256WithRSAEncryption' + # - 'sha512WithRSAEncryption' + with_items: "{{ local_certs }}" + + # now upload that certificate + - name: upload certificates first time + aws_acm: + name_tag: "{{ item.name }}" + <<: *aws_connection_info + certificate: "{{ lookup('file', item.cert ) }}" + private_key: "{{ lookup('file', item.priv_key ) }}" + state: present + register: upload + with_items: "{{ local_certs }}" + until: upload is succeeded + retries: 5 + delay: 10 + + - assert: + that: + - prev_task.certificate.arn is defined + - ('arn:aws:acm:123' | regex_search( 'arn:aws:acm:' )) is defined # check this works like s.startswith('arn') + - (prev_task.certificate.arn | regex_search( 'arn:aws:acm:' )) is defined + - prev_task.certificate.domain_name == original_cert.domain + - prev_task.changed + with_items: "{{ upload.results }}" + vars: + original_cert: "{{ item.item }}" + prev_task: "{{ item }}" + + - name: fetch data about cert just uploaded, by ARN + aws_acm_info: + certificate_arn: "{{ item.certificate.arn }}" + <<: *aws_connection_info + register: fetch_after_up + with_items: "{{ upload.results }}" + + - name: check output of prior task (fetch data about cert just uploaded, by ARN) + assert: + that: + - fetch_after_up_result.certificates | length == 1 + - fetch_after_up_result.certificates[0].certificate_arn == upload_result.certificate.arn + - fetch_after_up_result.certificates[0].domain_name == original_cert.domain + - (fetch_after_up_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) + == + (lookup( 'file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '' )) + - "'Name' in fetch_after_up_result.certificates[0].tags" + - fetch_after_up_result.certificates[0].tags['Name'] == original_cert.name + with_items: "{{ fetch_after_up.results }}" + vars: + fetch_after_up_result: "{{ item }}" # corresponding result from task registered as fetch_after_up + upload_result: "{{ item.item }}" # corresponding result from task registered as upload + original_cert: "{{ item.item.item }}" + + - name: fetch data about cert just uploaded, by name + aws_acm_info: + tags: + Name: "{{ original_cert.name }}" + <<: *aws_connection_info + register: fetch_after_up_name + with_items: "{{ upload.results }}" + vars: + upload_result: "{{ item }}" + original_cert: "{{ item.item }}" + + - name: check fetched data of cert we just uploaded + assert: + that: + - fetch_after_up_name_result.certificates | length == 1 + - fetch_after_up_name_result.certificates[0].certificate_arn == upload_result.certificate.arn + - fetch_after_up_name_result.certificates[0].domain_name == original_cert.domain + - (fetch_after_up_name_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) + == + (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) + - "'Name' in fetch_after_up_name_result.certificates[0].tags" + - fetch_after_up_name_result.certificates[0].tags['Name'] == original_cert.name + with_items: "{{ fetch_after_up_name.results }}" + vars: + fetch_after_up_name_result: "{{ item }}" # corresponding result from task registered as fetch_after_up_name + upload_result: "{{ item.item }}" # corresponding result from task registered as upload + original_cert: "{{ item.item.item }}" + + + - name: fetch data about cert just uploaded, by domain name + aws_acm_info: + domain_name: "{{ original_cert.domain }}" + <<: *aws_connection_info + register: fetch_after_up_domain + with_items: "{{ upload.results }}" + vars: + original_cert: "{{ item.item }}" + + - name: compare fetched data of cert just uploaded to upload task + assert: + that: + - fetch_after_up_domain_result.certificates | length == 1 + - fetch_after_up_domain_result.certificates[0].certificate_arn == upload_result.certificate.arn + - fetch_after_up_domain_result.certificates[0].domain_name == original_cert.domain + - (fetch_after_up_domain_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) + == + (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) + - "'Name' in fetch_after_up_domain_result.certificates[0].tags" + - fetch_after_up_domain_result.certificates[0].tags['Name'] == original_cert.name + with_items: "{{ fetch_after_up_domain.results }}" + vars: + fetch_after_up_domain_result: "{{ item }}" + upload_result: "{{ item.item }}" + original_cert: "{{ item.item.item }}" + + + # now upload that certificate + - name: upload certificates again, check not changed + aws_acm: + name_tag: "{{ item.name }}" + <<: *aws_connection_info + certificate: "{{ lookup('file', item.cert ) }}" + private_key: "{{ lookup('file', item.priv_key ) }}" + state: present + register: upload2 + with_items: "{{ local_certs }}" + failed_when: upload2.changed + + + - name: update first cert with body of the second, first time + aws_acm: + state: present + <<: *aws_connection_info + name_tag: "{{ local_certs[0].name }}" + certificate: "{{ lookup('file', local_certs[1].cert ) }}" + private_key: "{{ lookup('file', local_certs[1].priv_key ) }}" + register: overwrite + + - name: check output of previous task (update first cert with body of the second, first time) + assert: + that: + - overwrite.certificate.arn is defined + - overwrite.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined + - overwrite.certificate.arn == upload.results[0].certificate.arn + - overwrite.certificate.domain_name == local_certs[1].domain + - overwrite.changed + + - name: check update was sucessfull + aws_acm_info: + tags: + Name: "{{ local_certs[0].name }}" + <<: *aws_connection_info + register: fetch_after_overwrite + + - name: check output of update fetch + assert: + that: + - fetch_after_overwrite.certificates | length == 1 + - fetch_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[0].certificates[0].certificate_arn + - fetch_after_overwrite.certificates[0].domain_name == local_certs[1].domain + - (fetch_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert )| replace( ' ', '' ) | replace( '\n', '')) + - "'Name' in fetch_after_overwrite.certificates[0].tags" + - fetch_after_overwrite.certificates[0].tags['Name'] == local_certs[0].name + + - name: fetch other cert + aws_acm_info: + tags: + Name: "{{ local_certs[1].name }}" + <<: *aws_connection_info + register: check_after_overwrite + + - name: check other cert unaffected + assert: + that: + - check_after_overwrite.certificates | length == 1 + - check_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[1].certificates[0].certificate_arn + - check_after_overwrite.certificates[0].domain_name == local_certs[1].domain + - (check_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert ) | replace( ' ', '' ) | replace( '\n', '')) + - "'Name' in check_after_overwrite.certificates[0].tags" + - check_after_overwrite.certificates[0].tags['Name'] == local_certs[1].name + + - name: update first cert with body of the second again + aws_acm: + state: present + <<: *aws_connection_info + name_tag: "{{ local_certs[0].name }}" + certificate: "{{ lookup('file', local_certs[1].cert ) }}" + private_key: "{{ lookup('file', local_certs[1].priv_key ) }}" + register: overwrite2 + + - name: check output of previous task (update first cert with body of the second again) + assert: + that: + - overwrite2.certificate.arn is defined + - overwrite2.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined + - overwrite2.certificate.arn == upload.results[0].certificate.arn + - overwrite2.certificate.domain_name == local_certs[1].domain + - not overwrite2.changed + + - name: delete certs 1 and 2 + aws_acm: + <<: *aws_connection_info + state: absent + domain_name: "{{ local_certs[1].domain }}" + register: delete_both + + - name: test prev task + assert: + that: + - delete_both.arns is defined + - check_after_overwrite.certificates[0].certificate_arn in delete_both.arns + - upload.results[0].certificate.arn in delete_both.arns + - delete_both.changed + + - name: fetch info for certs 1 and 2 + aws_acm_info: + <<: *aws_connection_info + tags: + Name: "{{ local_certs[item].name }}" + register: check_del_one + with_items: + - 0 + - 1 + + - name: check certs 1 and 2 were already deleted + with_items: "{{ check_del_one.results }}" + assert: + that: item.certificates | length == 0 + + - name: check cert 3 not deleted + aws_acm_info: + <<: *aws_connection_info + tags: + Name: "{{ local_certs[2].name }}" + register: check_del_one_remain + failed_when: check_del_one_remain.certificates | length != 1 + + - name: delete cert 3 + aws_acm: + <<: *aws_connection_info + state: absent + domain_name: "{{ local_certs[2].domain }}" + register: delete_third + + - name: check cert 3 deletion went as expected + assert: + that: + - delete_third.arns is defined + - delete_third.arns | length == 1 + - delete_third.arns[0] == upload.results[2].certificate.arn + - delete_third.changed + + - name: check cert 3 was deleted + aws_acm_info: + <<: *aws_connection_info + tags: + Name: "{{ local_certs[2].name }}" + register: check_del_three + failed_when: check_del_three.certificates | length != 0 + + - name: delete cert 3 again + aws_acm: + <<: *aws_connection_info + state: absent + domain_name: "{{ local_certs[2].domain }}" + register: delete_third + + - name: check deletion of cert 3 not changed, because already deleted + assert: + that: + - delete_third.arns is defined + - delete_third.arns | length == 0 + - not delete_third.changed + + - name: check directory was made + assert: + that: + - remote_tmp_dir is defined + + - name: Generate private key for cert to be chained + openssl_privatekey: + path: "{{ chained_cert.priv_key }}" + type: RSA + size: 2048 # ACM doesn't work properly with 4096 + + - name: Generate two OpenSSL Certificate Signing Requests for cert to be chained + openssl_csr: + path: "{{ item.csr }}" + privatekey_path: "{{ chained_cert.priv_key }}" + common_name: "{{ chained_cert.domain }}" + with_items: "{{ chained_cert.chains }}" + + + - name: Sign new certs with cert 0 and 1 + openssl_certificate: + provider: ownca + path: "{{ item.cert }}" + csr_path: "{{ item.csr }}" + ownca_path: "{{ local_certs[item.ca].cert }}" + ownca_privatekey_path: "{{ local_certs[item.ca].priv_key }}" + signature_algorithms: + - 'sha256WithRSAEncryption' + # - 'sha512WithRSAEncryption' + with_items: "{{ chained_cert.chains }}" + + - name: check files exist (for next task) + file: + path: "{{ item }}" + state: file + with_items: + - "{{ local_certs[chained_cert.chains[0].ca].cert }}" + - "{{ local_certs[chained_cert.chains[1].ca].cert }}" + - "{{ chained_cert.chains[0].cert }}" + - "{{ chained_cert.chains[1].cert }}" + + - name: Find chains + certificate_complete_chain: + input_chain: "{{ lookup('file', item.cert ) }}" + root_certificates: + - "{{ local_certs[item.ca].cert }}" + with_items: "{{ chained_cert.chains }}" + register: chains + + - name: upload chained cert, first chain, first time + aws_acm: + name_tag: "{{ chained_cert.name }}" + <<: *aws_connection_info + certificate: "{{ lookup('file', chained_cert.chains[0].cert ) }}" + certificate_chain: "{{ chains.results[0].complete_chain | join('\n') }}" + private_key: "{{ lookup('file', chained_cert.priv_key ) }}" + state: present + register: upload_chain + failed_when: not upload_chain.changed + + - name: fetch chain of cert we just uploaded + aws_acm_info: + <<: *aws_connection_info + tags: + Name: "{{ chained_cert.name }}" + register: check_chain + + - name: check chain of cert we just uploaded + assert: + that: + - (check_chain.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) + == + ( chains.results[0].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) + - (check_chain.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) + == + ( lookup('file', chained_cert.chains[0].cert ) | replace( ' ', '' ) | replace( '\n', '') ) + + - name: upload chained cert again, check not changed + aws_acm: + name_tag: "{{ chained_cert.name }}" + <<: *aws_connection_info + certificate: "{{ lookup('file', chained_cert.chains[0].cert ) }}" + certificate_chain: "{{ chains.results[0].complete_chain | join('\n') }}" + private_key: "{{ lookup('file', chained_cert.priv_key ) }}" + state: present + register: upload_chain_2 + + - name: check previous task not changed + assert: + that: + - upload_chain_2.certificate.arn == upload_chain.certificate.arn + - not upload_chain_2.changed + + - name: upload chained cert, different chain + aws_acm: + name_tag: "{{ chained_cert.name }}" + <<: *aws_connection_info + certificate: "{{ lookup('file', chained_cert.chains[1].cert ) }}" + certificate_chain: "{{ chains.results[1].complete_chain | join('\n') }}" + private_key: "{{ lookup('file', chained_cert.priv_key ) }}" + state: present + register: upload_chain_3 + + - name: check uploading with different chain is changed + assert: + that: + - upload_chain_3.changed + - upload_chain_3.certificate.arn == upload_chain.certificate.arn + + - name: fetch info about chain of cert we just updated + aws_acm_info: + <<: *aws_connection_info + tags: + Name: "{{ chained_cert.name }}" + register: check_chain_2 + + - name: check chain of cert we just uploaded + assert: + that: + - (check_chain_2.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) + == + ( chains.results[1].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) + - (check_chain_2.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) + == + ( lookup('file', chained_cert.chains[1].cert ) | replace( ' ', '' ) | replace( '\n', '') ) + + - name: delete chained cert + aws_acm: + name_tag: "{{ chained_cert.name }}" + <<: *aws_connection_info + state: absent + register: delete_chain_3 + + - name: check deletion of chained cert 3 is changed + assert: + that: + - delete_chain_3.changed + - upload_chain.certificate.arn in delete_chain_3.arns + + + always: + + - name: delete first bunch of certificates + aws_acm: + name_tag: "{{ item.name }}" + <<: *aws_connection_info + state: absent + with_items: "{{ local_certs }}" + ignore_errors: yes + + - name: delete chained cert + aws_acm: + state: absent + name_tag: "{{ chained_cert.name }}" + <<: *aws_connection_info + ignore_errors: yes + + + - name: deleting local directory with test artefacts + file: + path: "{{ remote_tmp_dir }}" + state: directory + ignore_errors: yes \ No newline at end of file diff --git a/test/integration/targets/aws_acm/tasks/main.yml b/test/integration/targets/aws_acm/tasks/main.yml new file mode 100644 index 00000000000..4774276fabf --- /dev/null +++ b/test/integration/targets/aws_acm/tasks/main.yml @@ -0,0 +1,33 @@ +- name: AWS ACM integration test virtualenv wrapper + block: + - set_fact: + virtualenv: "{{ remote_tmp_dir }}/virtualenv" + virtualenv_command: "{{ ansible_python_interpreter }} -m virtualenv" + + - set_fact: + virtualenv_interpreter: "{{ virtualenv }}/bin/python" + + - pip: + name: virtualenv + + - pip: + name: + - 'botocore<1.13.0,>=1.12.211' + - boto3 + - coverage + - jinja2 + - pyyaml + - 'pyopenssl>=0.15' + - 'cryptography>=1.6' + virtualenv: "{{ virtualenv }}" + virtualenv_command: "{{ virtualenv_command }}" + virtualenv_site_packages: no + + - include_tasks: full_acm_test.yml + vars: + ansible_python_interpreter: "{{ virtualenv_interpreter }}" + + always: + - file: + path: "{{ virtualenv }}" + state: absent \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/fixtures/certs/a.pem b/test/units/modules/cloud/amazon/fixtures/certs/a.pem new file mode 100644 index 00000000000..4412f3258ae --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/a.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFVTCCBD2gAwIBAgISAx4pnfwvGxYrrQhr/UXiN7HCMA0GCSqGSIb3DQEBCwUA +MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD +ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA3MjUwMDI4NTdaFw0x +OTEwMjMwMDI4NTdaMBoxGDAWBgNVBAMTD2NyeXB0b2dyYXBoeS5pbzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKJDpCL99DVo83587MrVp6gunmKRoUfY +vcgk5u2v0tB9OmZkcIY37z6AunHWr18Yj55zHmm6G8Nf35hmu3ql2A26WThCbmOe +WXbxhgarkningZI9opUWnI2dIllguVIsq99GzhpNnDdCb26s5+SRhJI4cr4hYaKC +XGDKooKWyXUX09SJTq7nW/1+pq3y9ZMvldRKjJALeAdwnC7kmUB6pK7q8J2VlpfQ +wqGu6q/WHVdgnhWARw3GEFJWDn9wkxBAF08CpzhVaEj+iK+Ut/1HBgNYwqI47h7S +q+qv0G2qklRVUtEM0zYRsp+y/6vivdbFLlPw8VaerbpJN3gLtpVNcGECAwEAAaOC +AmMwggJfMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB +BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUjbe0bE1aZ8HiqtwqUfCe15bF +V8UwHwYDVR0jBBgwFoAUqEpqYwR93brm0Tm3pkVl7/Oo7KEwbwYIKwYBBQUHAQEE +YzBhMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbnQteDMubGV0c2VuY3J5cHQu +b3JnMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDMubGV0c2VuY3J5cHQu +b3JnLzAaBgNVHREEEzARgg9jcnlwdG9ncmFwaHkuaW8wTAYDVR0gBEUwQzAIBgZn +gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s +ZXRzZW5jcnlwdC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdgB0ftqDMa0z +EJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAAAWwmvtnXAAAEAwBHMEUCIFXHYX/E +xtbYCvjjQ3dN0HOLW1d8+aduktmax4mu3KszAiEAvTpxuSVVXJnVGA4tU2GOnI60 +sqTh/IK6hvrFN1k1HBUAdQApPFGWVMg5ZbqqUPxYB9S3b79Yeily3KTDDPTlRUf0 +eAAAAWwmvtm9AAAEAwBGMEQCIDn7sgzD+7JzR+XTvjKf7VyLWwX37O8uwCfCTKo7 ++tEhAiB05bHiICU5wkfRBrwcvqXf4bPF7NT5LVlRQYzJ/hbpvzANBgkqhkiG9w0B +AQsFAAOCAQEAcMU8E6D+5WC07QSeTppRTboC++7YgQg5NiSWm7OE2FlyiRZXnu0Y +uBoaqAkZIqj7dom9wy1c1UauxOfM9lUZKhYnDTBu9tIhBAvCS0J0avv1j1KQygQ1 +qV+urJsunUwqV/vPWo1GfWophvyXVN6MAycv34ZXZvAjtG7oDcoQVLLvK1SIo2vu +4/dNkOQzaeZez8q6Ij9762TbBWaK5C789VMdUWZCADWoToPIK533cWbDEp4IhBU/ +K73d7lGGl7S59SjT2V/XE6eJS9Zlj0M+A8pf/8tjM/ImHAjlOHB02sM/VfZ7HAuZ +61TPxohL+e+X1FYeqIXYGXJmCEuB8WEmBg== +-----END CERTIFICATE----- diff --git a/test/units/modules/cloud/amazon/fixtures/certs/b.pem b/test/units/modules/cloud/amazon/fixtures/certs/b.pem new file mode 100644 index 00000000000..2be4bca533a --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/b.pem @@ -0,0 +1,47 @@ +-----BEGIN CERTIFICATE----- +MIIIUjCCB/egAwIBAgIRALiJR3zQjp0MevT/Hk89sfAwCgYIKoZIzj0EAwIwgZIx +CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNV +BAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTgwNgYDVQQD +Ey9DT01PRE8gRUNDIERvbWFpbiBWYWxpZGF0aW9uIFNlY3VyZSBTZXJ2ZXIgQ0Eg +MjAeFw0xOTA3MzEwMDAwMDBaFw0yMDAyMDYyMzU5NTlaMGwxITAfBgNVBAsTGERv +bWFpbiBDb250cm9sIFZhbGlkYXRlZDEhMB8GA1UECxMYUG9zaXRpdmVTU0wgTXVs +dGktRG9tYWluMSQwIgYDVQQDExtzc2wzODczMzcuY2xvdWRmbGFyZXNzbC5jb20w +WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARPFdjdnBIJRPnHCPsCBJ/MmPytXnZX +KV6lD2bbG5EVNuUQln4Na8heCY+sfpV+SPuuiNzZxgDA46GvyzdRYFhxo4IGUTCC +Bk0wHwYDVR0jBBgwFoAUQAlhZ/C8g3FP3hIILG/U1Ct2PZYwHQYDVR0OBBYEFGLh +bHk1KAYIRfVwXA3L+yDf0CxjMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAA +MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBPBgNVHSAESDBGMDoGCysG +AQQBsjEBAgIHMCswKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5j +b20vQ1BTMAgGBmeBDAECATBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLmNv +bW9kb2NhNC5jb20vQ09NT0RPRUNDRG9tYWluVmFsaWRhdGlvblNlY3VyZVNlcnZl +ckNBMi5jcmwwgYgGCCsGAQUFBwEBBHwwejBRBggrBgEFBQcwAoZFaHR0cDovL2Ny +dC5jb21vZG9jYTQuY29tL0NPTU9ET0VDQ0RvbWFpblZhbGlkYXRpb25TZWN1cmVT +ZXJ2ZXJDQTIuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC5jb21vZG9jYTQu +Y29tMIIDkAYDVR0RBIIDhzCCA4OCG3NzbDM4NzMzNy5jbG91ZGZsYXJlc3NsLmNv +bYIMKi5hanJ0Y3QuY29tghMqLmFrcmVwYnVyY3UuZ2VuLnRyghUqLmFuZHJlYXNr +YW5lbGxvcy5jb22CDSouYW5zaWJsZS5jb22CGSouYXJ0b2Z0b3VjaC1raW5nd29v +ZC5jb22CFyouYm91bGRlcnN3YXRlcmhvbGUuY29tghcqLmJyb2Nrc3RlY2hzdXBw +b3J0LmNvbYIQKi5idXJjbGFyLndlYi50coIcKi5ob3Blc29uZ2ZyZW5jaGJ1bGxk +b2dzLm5ldIIMKi5odXJyZW0uY29tghAqLmh5dmVsaWNvbnMuY29tghAqLmthcm1h +Zml0LmNvLnVrghUqLmxvd3J5c3lzdGVtc2luYy5jb22CDioubWFuaWNydW4uY29t +ghUqLm11dHVvZmluYW5jaWVyYS5jb22CDyoucGlsZ3JpbWFnZS5waIINKi5wa2dh +bWVzLm9yZ4IbKi5ybHBjb25zdWx0aW5nc2VydmljZXMuY29tghYqLnJ1eWF0YWJp +cmxlcmkuZ2VuLnRyghQqLnJ5YW5hcHBoeXNpY3NjLmNvbYIVKi53ZWFyaXRiYWNr +d2FyZHMub3Jngg8qLnlldGlzbmFjay5jb22CCmFqcnRjdC5jb22CEWFrcmVwYnVy +Y3UuZ2VuLnRyghNhbmRyZWFza2FuZWxsb3MuY29tggthbnNpYmxlLmNvbYIXYXJ0 +b2Z0b3VjaC1raW5nd29vZC5jb22CFWJvdWxkZXJzd2F0ZXJob2xlLmNvbYIVYnJv +Y2tzdGVjaHN1cHBvcnQuY29tgg5idXJjbGFyLndlYi50coIaaG9wZXNvbmdmcmVu +Y2hidWxsZG9ncy5uZXSCCmh1cnJlbS5jb22CDmh5dmVsaWNvbnMuY29tgg5rYXJt +YWZpdC5jby51a4ITbG93cnlzeXN0ZW1zaW5jLmNvbYIMbWFuaWNydW4uY29tghNt +dXR1b2ZpbmFuY2llcmEuY29tgg1waWxncmltYWdlLnBoggtwa2dhbWVzLm9yZ4IZ +cmxwY29uc3VsdGluZ3NlcnZpY2VzLmNvbYIUcnV5YXRhYmlybGVyaS5nZW4udHKC +EnJ5YW5hcHBoeXNpY3NjLmNvbYITd2Vhcml0YmFja3dhcmRzLm9yZ4INeWV0aXNu +YWNrLmNvbTCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2ALIeBcyLos2KIE6HZvkr +uYolIGdr2vpw57JJUy3vi5BeAAABbEVw8SgAAAQDAEcwRQIgE2YeTfb/d4BBUwpZ +ihWXSR+vRyNNUg8GlOak2MFMHv0CIQCLBvtU401m5/Psg9KirQZs321BSxgUKgSQ +m9M691d3eQB2AF6nc/nfVsDntTZIfdBJ4DJ6kZoMhKESEoQYdZaBcUVYAAABbEVw +8VgAAAQDAEcwRQIgGYsGfr3/mekjzMS9+ALAjx1ryfIfhXB/+UghTcw4Y8ICIQDS +K2L18WX3+Oh4TjJhjh5tV1iYyZVYivcwwbr7mtmOqjAKBggqhkjOPQQDAgNJADBG +AiEAjNt7LF78GV7snky9jwFcBsLH55ndzduvsrkJ7Ne1SgYCIQDsMJsTr9VP6kar +4Kv8V9zNBmpGrGNuE7A1GixBvzNaHA== +-----END CERTIFICATE----- diff --git a/test/units/modules/cloud/amazon/fixtures/certs/chain-1.0.cert b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.0.cert new file mode 100644 index 00000000000..6997766acd0 --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.0.cert @@ -0,0 +1,121 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy +OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz +KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU +gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG +A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i +YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu +dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv +c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds +b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j +b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X +wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh +bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH +88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp +CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF +rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2 +DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz +A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1 +hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac +e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0 +qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + diff --git a/test/units/modules/cloud/amazon/fixtures/certs/chain-1.1.cert b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.1.cert new file mode 100644 index 00000000000..51f64f08d19 --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.1.cert @@ -0,0 +1,69 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIyOFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBvcmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuzKhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsUgdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9iYWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdvc3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2dsb2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5jb20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkhbxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFpCJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LFrJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zozA20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVace2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0yMjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vKEymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPanH05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jzkE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5BxxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uDJGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNVHSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1UdIwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4EFgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/AVn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTsY4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMbSXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + diff --git a/test/units/modules/cloud/amazon/fixtures/certs/chain-1.2.cert b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.2.cert new file mode 100644 index 00000000000..ce299241181 --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.2.cert @@ -0,0 +1,113 @@ +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy +OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz +KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU +gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG +A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i +YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu +dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv +c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds +b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j +b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X +wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh +bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH +88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp +CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF +rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2 +DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz +A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1 +hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac +e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0 +qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + diff --git a/test/units/modules/cloud/amazon/fixtures/certs/chain-1.3.cert b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.3.cert new file mode 100644 index 00000000000..0c947b17bac --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.3.cert @@ -0,0 +1,124 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy +OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz +KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU +gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG +A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i +YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu +dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv +c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds +b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j +b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X +wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh +bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH +88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp +CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF +rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2 +DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz +A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1 +hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac +e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0 +qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- + + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + + + + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- diff --git a/test/units/modules/cloud/amazon/fixtures/certs/chain-1.4.cert b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.4.cert new file mode 100644 index 00000000000..adbb8edcae9 --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/chain-1.4.cert @@ -0,0 +1,86 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy +OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz +KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU +gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG +A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i +YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu +dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv +c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds +b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j +b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X +wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh +bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH +88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp +CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF +rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2 +DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz +A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1 +hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac +e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0 +qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- diff --git a/test/units/modules/cloud/amazon/fixtures/certs/chain-4.cert b/test/units/modules/cloud/amazon/fixtures/certs/chain-4.cert new file mode 100644 index 00000000000..2b82edf6c9d --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/chain-4.cert @@ -0,0 +1,121 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=prod.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIJDCCBgygAwIBAgIUP9S/56XvOFzWk1vp1+7JJT17brEwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyNzAzMTU1 +NFoXDTIxMDgyNzAzMjUwMFowgZwxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MScwJQYDVQQD +DB5wcm9kLmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCrRouNZFOZwM1qyAU6v6ag9fzSx3y8zz36nR8HuqbA +/wqrbMmnpofwdx/9u1bilsHfJzIODv0hm7aGk+neTK3DIapiII3m0HKW0v+GLsl7 +JkDuc2o3XlakcXlA45qDKCZXbXZtY4/kdxKG0OSUZi7oQqohhYl/c/ojrTiey+4G +KhEVqWwOuQ1OC1DRw4qMH54d0koFxxSLPJ8JiiztLlK/e9n8BoJikj5fBqWy5R1F +bGXCdzjcfmPV6iSOzJShpUgj4ga91mO6j3S6LLfK5ibbTlY+pmUxUT+m9nKMon3h +mFptTYo9t9vUF/a/owjRxNLg01fJLNjYn8QV2vQvODGfAgMBAAGjggOqMIIDpjAJ +BgNVHRMEAjAAMB8GA1UdIwQYMBaAFLMSibWpSzW8FQDwgOnYeIfxE3x2MHMGCCsG +AQUFBwEBBGcwZTA3BggrBgEFBQcwAoYraHR0cDovL3RydXN0LnF1b3ZhZGlzZ2xv +YmFsLmNvbS9xdnNzbGczLmNydDAqBggrBgEFBQcwAYYeaHR0cDovL29jc3AucXVv +dmFkaXNnbG9iYWwuY29tMIGjBgNVHREEgZswgZiCHnByb2QuZW5lcmd5Lmluc2lk +ZS50ZWxzdHJhLmNvbYImcmVwb3J0cy5wcm9kLmVuZXJneS5pbnNpZGUudGVsc3Ry +YS5jb22CKGdyZWVuc3luYy5wcm9kLmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb22C +JG5nb3NzLnByb2QuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTBRBgNVHSAESjBI +MEYGDCsGAQQBvlgAAmQBATA2MDQGCCsGAQUFBwIBFihodHRwOi8vd3d3LnF1b3Zh +ZGlzZ2xvYmFsLmNvbS9yZXBvc2l0b3J5MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr +BgEFBQcDATA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnF1b3ZhZGlzZ2xv +YmFsLmNvbS9xdnNzbGczLmNybDAdBgNVHQ4EFgQUoIME5TykVAI8VF5g0zeh0xdv +i3owDgYDVR0PAQH/BAQDAgWgMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgBW +FAaaL9fC7NP14b1Esj7HRna5vJkRXMDvlJhV1onQ3QAAAWzRG8r0AAAEAwBHMEUC +IQDShuQyYMiy7KKxWOzffolVIcPRgWD7ClNEbIcUATHKyQIgXnTZBXcpcbXBQXLs +tFuvY36TbKIYc2ql2nmdydGQ9wcAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jj +d80OyA3cEAAAAWzRG8sAAAAEAwBHMEUCIGsLEoA9S7pNE3VoNZHxl2IAdeP3Dy2Q +Mk0rM46hp6CRAiEA08rOjswSdcn7qgDEoiyvlcrOTIFJAEcMlxSY65yLVUwAdgBV +gdTCFpA2AUrqC5tXPFPwwOQ4eHAlCBcvo6odBxPTDAAAAWzRG8q7AAAEAwBHMEUC +IAkVCcTFG8MBDI58JKIhMlPbzkdrKnYY3Kp9KqWuTAvMAiEAipeI7RCLBk8+T/p+ +gY7+vtFZxKDthcJMUpZz7qmica0wDQYJKoZIhvcNAQELBQADggIBAESe0U1qArxL +F2uk65q6x6HBcZuSocpceokzcUBv07Kxs6UJU9ybTbl8VYPuC+OUdpvut1kOJCJm +1TRrr5KMh+9as42xkbKRZnh5TQt7aHmVcLHLfA4x0UrELfNX3fVTDxwDAPAhE5oM +0w+d1foLakh7dXKKSxobEI3KRwFp19iuZeIqwI8XMWMr9ajhTC0T7D2QvKotpNBS +sNDHiIE3IXoa9o7UiOG8IfW0wAt7CEygv0F7ctHRTcQSP/SJIGYOUZ7uotULVL5i +elG31Y83Jx3sPNCy4IZfCip6Gw7MgsN2CZGApqi49edSqDWyRIfmCeXtMc7XI7Md +kqqWxbqGGTdYJCucoGqahqRR+BI9anEqTD9T5Gy0TpCi2pgp1i7czza71nfz0PcN +R0pw/1lqb9AqmJ2XELpBpo82B9XGple9thpincai7jPk3ezY5eEvDTmkHRlUFCp8 +8M66Ga19hZTgnHPWDKZYZzuZ7Lcl2WbapFOYYHJggSpBRy4GkH6eTSkUB9G9k8vU +gbvtS7sR5ggecbCBu0M4TWYmnUojR8UXtr0oOTlXysTHVGs5Tx9ChhOLyUqhX8tM +1zSDT8JJvbbw4RqpGzBKTNaO5nxRLgKVQOQdM8f1kjMr9/U58Lc4UiaTkJM14VfK +8GfV8+K/vRCBtME53ILvm1l18jtakG3c +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + diff --git a/test/units/modules/cloud/amazon/fixtures/certs/simple-chain-a.cert b/test/units/modules/cloud/amazon/fixtures/certs/simple-chain-a.cert new file mode 100644 index 00000000000..1d9bbe21375 --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/simple-chain-a.cert @@ -0,0 +1,18 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +aaa +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +bbb +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +ccc +-----END CERTIFICATE----- + diff --git a/test/units/modules/cloud/amazon/fixtures/certs/simple-chain-b.cert b/test/units/modules/cloud/amazon/fixtures/certs/simple-chain-b.cert new file mode 100644 index 00000000000..1d9bbe21375 --- /dev/null +++ b/test/units/modules/cloud/amazon/fixtures/certs/simple-chain-b.cert @@ -0,0 +1,18 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +aaa +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +bbb +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +ccc +-----END CERTIFICATE----- + diff --git a/test/units/modules/cloud/amazon/test_aws_acm.py b/test/units/modules/cloud/amazon/test_aws_acm.py new file mode 100644 index 00000000000..d2fd87b8dee --- /dev/null +++ b/test/units/modules/cloud/amazon/test_aws_acm.py @@ -0,0 +1,122 @@ +# (c) 2019 Telstra Corporation Limited +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +from ansible.modules.cloud.amazon.aws_acm import pem_chain_split, chain_compare +from ansible.module_utils._text import to_bytes, to_text +from pprint import pprint + + +def test_chain_compare(): + + # The functions we're testing take module as an argument + # Just so they can call module.fail_json + # Let's just use None for the unit tests, + # Because they shouldn't fail + # And if they do, fail_json is not applicable + module = None + + fixture_suffix = 'test/units/modules/cloud/amazon/fixtures/certs' + + # Test chain split function on super simple (invalid) certs + expected = ['aaa', 'bbb', 'ccc'] + + for fname in ['simple-chain-a.cert', 'simple-chain-b.cert']: + path = fixture_suffix + '/' + fname + with open(path, 'r') as f: + pem = to_text(f.read()) + actual = pem_chain_split(module, pem) + actual = [a.strip() for a in actual] + if actual != expected: + print("Expected:") + pprint(expected) + print("Actual:") + pprint(actual) + raise AssertionError("Failed to properly split %s" % fname) + + # Now test real chains + # chains with same same_as should be considered equal + test_chains = [ + { # Original Cert chain + 'path': fixture_suffix + '/chain-1.0.cert', + 'same_as': 1, + 'length': 3 + }, + { # Same as 1.0, but longer PEM lines + 'path': fixture_suffix + '/chain-1.1.cert', + 'same_as': 1, + 'length': 3 + }, + { # Same as 1.0, but without the stuff before each -------- + 'path': fixture_suffix + '/chain-1.2.cert', + 'same_as': 1, + 'length': 3 + }, + { # Same as 1.0, but in a different order, so should be considered different + 'path': fixture_suffix + '/chain-1.3.cert', + 'same_as': 2, + 'length': 3 + }, + { # Same as 1.0, but with last link missing + 'path': fixture_suffix + '/chain-1.4.cert', + 'same_as': 3, + 'length': 2 + }, + { # Completely different cert chain to all the others + 'path': fixture_suffix + '/chain-4.cert', + 'same_as': 4, + 'length': 3 + }, + { # Single cert + 'path': fixture_suffix + '/a.pem', + 'same_as': 5, + 'length': 1 + }, + { # a different, single cert + 'path': fixture_suffix + '/b.pem', + 'same_as': 6, + 'length': 1 + } + ] + + for chain in test_chains: + with open(chain['path'], 'r') as f: + chain['pem_text'] = to_text(f.read()) + + # Test to make sure our regex isn't too greedy + chain['split'] = pem_chain_split(module, chain['pem_text']) + if len(chain['split']) != chain['length']: + print("Cert before split") + print(chain['pem_text']) + print("Cert after split") + pprint(chain['split']) + print("path: %s" % chain['path']) + print("Expected chain length: %d" % chain['length']) + print("Actual chain length: %d" % len(chain['split'])) + raise AssertionError("Chain %s was not split properly" % chain['path']) + + for chain_a in test_chains: + for chain_b in test_chains: + expected = (chain_a['same_as'] == chain_b['same_as']) + + # Now test the comparison function + actual = chain_compare(module, chain_a['pem_text'], chain_b['pem_text']) + if expected != actual: + print("Error, unexpected comparison result between \n%s\nand\n%s" % (chain_a['path'], chain_b['path'])) + print("Expected %s got %s" % (str(expected), str(actual))) + assert(expected == actual)