#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (c) 2024 Felix Fontein # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = ''' --- module: acme_certificate_order_info author: Felix Fontein (@felixfontein) version_added: 2.24.0 short_description: Obtain information for an ACME v2 order description: - Obtain information for an ACME v2 order. This can be used during the process of obtaining a new certificate with the L(ACME protocol,https://tools.ietf.org/html/rfc8555) from a Certificate Authority such as L(Let's Encrypt,https://letsencrypt.org/) or L(Buypass,https://www.buypass.com/). This module does not support ACME v1, the original version of the ACME protocol before standardization. - This module needs to be used in conjunction with the M(community.crypto.acme_certificate_order_create), M(community.crypto.acme_certificate_order_validate), and M(community.crypto.acme_certificate_order_finalize) modules. seealso: - module: community.crypto.acme_certificate_order_create description: Create an ACME order. - module: community.crypto.acme_certificate_order_validate description: Validate pending authorizations of an ACME order. - module: community.crypto.acme_certificate_order_finalize description: Finalize an ACME order after satisfying the challenges. - name: Automatic Certificate Management Environment (ACME) description: The specification of the ACME protocol (RFC 8555). link: https://tools.ietf.org/html/rfc8555 - name: ACME TLS ALPN Challenge Extension description: The specification of the V(tls-alpn-01) challenge (RFC 8737). link: https://www.rfc-editor.org/rfc/rfc8737.html - module: community.crypto.acme_inspect description: Allows to debug problems. - module: community.crypto.acme_certificate_deactivate_authz description: Allows to deactivate (invalidate) ACME v2 orders. extends_documentation_fragment: - community.crypto.acme.basic - community.crypto.acme.account - community.crypto.attributes - community.crypto.attributes.actiongroup_acme - community.crypto.attributes.idempotent_not_modify_state - community.crypto.attributes.info_module options: order_uri: description: - The order URI provided by RV(community.crypto.acme_certificate_order_create#module:order_uri). type: str required: true ''' EXAMPLES = r''' - name: Create a challenge for sample.com using a account key from a variable community.crypto.acme_certificate_order_create: account_key_content: "{{ account_private_key }}" csr: /etc/pki/cert/csr/sample.com.csr register: order - name: Obtain information on the order community.crypto.acme_certificate_order_info: account_key_src: /etc/pki/cert/private/account.key order_uri: "{{ order.order_uri }}" register: order_info - name: Show information ansible.builtin.debug: var: order_info ''' RETURN = ''' account_uri: description: ACME account URI. returned: success type: str order_uri: description: ACME order URI. returned: success type: str order: description: - The order object. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.3) for its specification. returned: success type: dict contains: status: description: - The status of this order. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6) for state changes. type: str returned: always choices: - pending - ready - processing - valid - invalid expires: description: - The timestamp after which the server will consider this order invalid. - Encoded in the format specified in L(RFC 3339, https://www.rfc-editor.org/rfc/rfc3339). type: str returned: if RV(order.status) is V(pending) or V(valid), and sometimes in other situations identifiers: description: - An array of identifier objects that the order pertains to. returned: always type: list elements: dict contains: type: description: - The type of identifier. - So far V(dns) and V(ip) are defined values. type: str returned: always sample: dns choices: - dns - ip value: description: - The identifier itself. type: str returned: always sample: example.com notBefore: description: - The requested value of the C(notBefore) field in the certificate. - Encoded in the date format defined in L(RFC 3339, https://www.rfc-editor.org/rfc/rfc3339). type: str returned: depending on order notAfter (optional, string): description: - The requested value of the C(notAfter) field in the certificate. - Encoded in the date format defined in L(RFC 3339, https://www.rfc-editor.org/rfc/rfc3339). type: str returned: depending on order error: description: - The error that occurred while processing the order, if any. - This field is structured as a L(problem document according to RFC 7807, https://www.rfc-editor.org/rfc/rfc7807). type: dict returned: sometimes authorizations: description: - For pending orders, the authorizations that the client needs to complete before the requested certificate can be issued, including unexpired authorizations that the client has completed in the past for identifiers specified in the order. - The authorizations required are dictated by server policy; there may not be a 1:1 relationship between the order identifiers and the authorizations required. - For final orders (in the V(valid) or V(invalid) state), the authorizations that were completed. Each entry is a URL from which an authorization can be fetched with a POST-as-GET request. - The authorizations themselves are returned as RV(authorizations_by_identifier). type: list elements: str returned: always finalize: description: - A URL that a CSR must be POSTed to once all of the order's authorizations are satisfied to finalize the order. The result of a successful finalization will be the population of the certificate URL for the order. type: str returned: always certificate: description: - A URL for the certificate that has been issued in response to this order. type: str returned: when the certificate has been issued authorizations_by_identifier: description: - A dictionary mapping identifiers to their authorization objects. returned: success type: dict contains: identifier: description: - The keys in this dictionary are the identifiers. C(identifier) is a placeholder used in the documentation. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.4) for how authorization objects look like. type: dict contains: identifier: description: - The identifier that the account is authorized to represent. type: dict returned: always contains: type: description: - The type of identifier. - So far V(dns) and V(ip) are defined values. type: str returned: always sample: dns choices: - dns - ip value: description: - The identifier itself. type: str returned: always sample: example.com status: description: - The status of this authorization. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6) for state changes. type: str choices: - pending - valid - invalid - deactivated - expired - revoked returned: always expires: description: - The timestamp after which the server will consider this authorization invalid. - Encoded in the format specified in L(RFC 3339, https://www.rfc-editor.org/rfc/rfc3339). type: str returned: if RV(authorizations_by_identifier.identifier.status=valid), and sometimes in other situations challenges: description: - For pending authorizations, the challenges that the client can fulfill in order to prove possession of the identifier. - For valid authorizations, the challenge that was validated. - For invalid authorizations, the challenge that was attempted and failed. - Each array entry is an object with parameters required to validate the challenge. A client should attempt to fulfill one of these challenges, and a server should consider any one of the challenges sufficient to make the authorization valid. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-8) for the general structure. The structure of every entry depends on the challenge's type. For C(tls-alpn-01) challenges, the structure is defined in U(https://www.rfc-editor.org/rfc/rfc8737.html#section-3). type: list elements: dict returned: always contains: type: description: - The type of challenge encoded in the object. type: str returned: always choices: - http-01 - dns-01 - tls-alpn-01 url: description: - The URL to which a response can be posted. type: str returned: always status: description: - The status of this challenge. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6) for state changes. type: str choices: - pending - processing - valid - invalid returned: always validated: description: - The time at which the server validated this challenge. - Encoded in the format specified in L(RFC 3339, https://www.rfc-editor.org/rfc/rfc3339). type: str returned: always if RV(authorizations_by_identifier.identifier.challenges[].type=valid), otherwise in some situations error: description: - Error that occurred while the server was validating the challenge, if any. - This field is structured as a L(problem document according to RFC 7807, https://www.rfc-editor.org/rfc/rfc7807). type: dict returned: always if RV(authorizations_by_identifier.identifier.challenges[].type=invalid), otherwise in some situations wildcard: description: - This field B(must) be present and true for authorizations created as a result of a C(newOrder) request containing a DNS identifier with a value that was a wildcard domain name. For other authorizations, it B(must) be absent. - Wildcard domain names are described in U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.3) of the ACME specification. type: bool returned: sometimes authorizations_by_status: description: - For every status, a list of identifiers whose authorizations have this status. returned: success type: dict contains: pending: description: - A list of all identifiers whose authorizations are in the C(pending) state. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6) for state changes of authorizations. type: list elements: str returned: always invalid: description: - A list of all identifiers whose authorizations are in the C(invalid) state. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6) for state changes of authorizations. type: list elements: str returned: always valid: description: - A list of all identifiers whose authorizations are in the C(valid) state. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6) for state changes of authorizations. type: list elements: str returned: always revoked: description: - A list of all identifiers whose authorizations are in the C(revoked) state. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6) for state changes of authorizations. type: list elements: str returned: always deactivated: description: - A list of all identifiers whose authorizations are in the C(deactivated) state. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6) for state changes of authorizations. type: list elements: str returned: always expired: description: - A list of all identifiers whose authorizations are in the C(expired) state. - See U(https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6) for state changes of authorizations. type: list elements: str returned: always ''' from ansible_collections.community.crypto.plugins.module_utils.acme.acme import ( create_backend, create_default_argspec, ) from ansible_collections.community.crypto.plugins.module_utils.acme.errors import ( ModuleFailException, ) from ansible_collections.community.crypto.plugins.module_utils.acme.certificate import ( ACMECertificateClient, ) def main(): argument_spec = create_default_argspec(with_certificate=False) argument_spec.update_argspec( order_uri=dict(type='str', required=True), ) module = argument_spec.create_ansible_module(supports_check_mode=True) if module.params['acme_version'] == 1: module.fail_json('The module does not support acme_version=1') backend = create_backend(module, False) try: client = ACMECertificateClient(module, backend) order = client.load_order() authorizations_by_identifier = dict() authorizations_by_status = { 'pending': [], 'invalid': [], 'valid': [], 'revoked': [], 'deactivated': [], 'expired': [], } for identifier, authz in order.authorizations.items(): authorizations_by_identifier[identifier] = authz.to_json() authorizations_by_status[authz.status].append(identifier) module.exit_json( changed=False, account_uri=client.client.account_uri, order_uri=order.url, order=order.data, authorizations_by_identifier=authorizations_by_identifier, authorizations_by_status=authorizations_by_status, ) except ModuleFailException as e: e.do_fail(module) if __name__ == '__main__': main()