acme_certificate: allow to request renewal of a certificate according to ARI (#739)
* Allow to request renewal of a certificate according to ARI in acme_certificate. * Improve docs. * Fix typo and use right object. * Add warning.pull/742/head^2
parent
6d4fc589ae
commit
33d278ad8f
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- "acme_certificate - add ``include_renewal_cert_id`` option to allow requesting renewal of a specific certificate according to the current ACME Renewal Information specification draft (https://github.com/ansible-collections/community.crypto/pull/739)."
|
|
@ -32,6 +32,7 @@ class Order(object):
|
|||
self.identifiers = []
|
||||
for identifier in data['identifiers']:
|
||||
self.identifiers.append((identifier['type'], identifier['value']))
|
||||
self.replaces_cert_id = data.get('replaces')
|
||||
self.finalize_uri = data.get('finalize')
|
||||
self.certificate_uri = data.get('certificate')
|
||||
self.authorization_uris = data['authorizations']
|
||||
|
@ -44,6 +45,7 @@ class Order(object):
|
|||
|
||||
self.status = None
|
||||
self.identifiers = []
|
||||
self.replaces_cert_id = None
|
||||
self.finalize_uri = None
|
||||
self.certificate_uri = None
|
||||
self.authorization_uris = []
|
||||
|
@ -62,7 +64,7 @@ class Order(object):
|
|||
return result
|
||||
|
||||
@classmethod
|
||||
def create(cls, client, identifiers):
|
||||
def create(cls, client, identifiers, replaces_cert_id=None):
|
||||
'''
|
||||
Start a new certificate order (ACME v2 protocol).
|
||||
https://tools.ietf.org/html/rfc8555#section-7.4
|
||||
|
@ -76,6 +78,8 @@ class Order(object):
|
|||
new_order = {
|
||||
"identifiers": acme_identifiers
|
||||
}
|
||||
if replaces_cert_id is not None:
|
||||
new_order["replaces"] = replaces_cert_id
|
||||
result, info = client.send_signed_request(
|
||||
client.directory['newOrder'], new_order, error_msg='Failed to start new order', expected_status_codes=[201])
|
||||
return cls.from_json(client, result, info['location'])
|
||||
|
|
|
@ -293,6 +293,30 @@ options:
|
|||
- "The identifier must be of the form
|
||||
V(C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10)."
|
||||
type: str
|
||||
include_renewal_cert_id:
|
||||
description:
|
||||
- Determines whether to request renewal of an existing certificate according to
|
||||
L(the ACME ARI draft 3, https://www.ietf.org/archive/id/draft-ietf-acme-ari-03.html#section-5).
|
||||
- This is only used when the certificate specified in O(dest) or O(fullchain_dest) already exists.
|
||||
- V(never) never sends the certificate ID of the certificate to renew. V(always) will always send it.
|
||||
- V(when_ari_supported) only sends the certificate ID if the ARI endpoint is found in the ACME directory.
|
||||
- Generally you should use V(when_ari_supported) if you know that the ACME service supports a compatible
|
||||
draft (or final version, once it is out) of the ARI extension. V(always) should never be necessary.
|
||||
If you are not sure, or if you receive strange errors on invalid C(replaces) values in order objects,
|
||||
use V(never), which also happens to be the default.
|
||||
- ACME servers might refuse to create new orders with C(replaces) for certificates that already have an
|
||||
existing order. This can happen if this module is used to create an order, and then the playbook/role
|
||||
fails in case the challenges cannot be set up. If the playbook/role does not record the order data to
|
||||
continue with the existing order, but tries to create a new one on the next run, creating the new order
|
||||
might fail. For this reason, this option should only be set to a value different from V(never) if the
|
||||
role/playbook using it keeps track of order data accross restarts.
|
||||
type: str
|
||||
choices:
|
||||
- never
|
||||
- when_ari_supported
|
||||
- always
|
||||
default: never
|
||||
version_added: 2.20.0
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
|
@ -586,6 +610,7 @@ from ansible_collections.community.crypto.plugins.module_utils.acme.orders impor
|
|||
)
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.acme.utils import (
|
||||
compute_cert_id,
|
||||
pem_to_der,
|
||||
)
|
||||
|
||||
|
@ -622,6 +647,7 @@ class ACMECertificateClient(object):
|
|||
self.order_uri = self.data.get('order_uri') if self.data else None
|
||||
self.all_chains = None
|
||||
self.select_chain_matcher = []
|
||||
self.include_renewal_cert_id = module.params['include_renewal_cert_id']
|
||||
|
||||
if self.module.params['select_chain']:
|
||||
for criterium_idx, criterium in enumerate(self.module.params['select_chain']):
|
||||
|
@ -679,6 +705,15 @@ class ACMECertificateClient(object):
|
|||
# stored in self.order_uri by the constructor).
|
||||
return self.order_uri is None
|
||||
|
||||
def _get_cert_info_or_none(self):
|
||||
if self.module.params.get('dest'):
|
||||
filename = self.module.params['dest']
|
||||
else:
|
||||
filename = self.module.params['fullchain_dest']
|
||||
if not os.path.exists(filename):
|
||||
return None
|
||||
return self.client.backend.get_cert_information(cert_filename=filename)
|
||||
|
||||
def start_challenges(self):
|
||||
'''
|
||||
Create new authorizations for all identifiers of the CSR,
|
||||
|
@ -693,7 +728,15 @@ class ACMECertificateClient(object):
|
|||
authz = Authorization.create(self.client, identifier_type, identifier)
|
||||
self.authorizations[authz.combined_identifier] = authz
|
||||
else:
|
||||
self.order = Order.create(self.client, self.identifiers)
|
||||
replaces_cert_id = None
|
||||
if (
|
||||
self.include_renewal_cert_id == 'always' or
|
||||
(self.include_renewal_cert_id == 'when_ari_supported' and self.client.directory.has_renewal_info_endpoint())
|
||||
):
|
||||
cert_info = self._get_cert_info_or_none()
|
||||
if cert_info is not None:
|
||||
replaces_cert_id = compute_cert_id(self.client.backend, cert_info=cert_info)
|
||||
self.order = Order.create(self.client, self.identifiers, replaces_cert_id)
|
||||
self.order_uri = self.order.url
|
||||
self.order.load_authorizations(self.client)
|
||||
self.authorizations.update(self.order.authorizations)
|
||||
|
@ -879,6 +922,7 @@ def main():
|
|||
subject_key_identifier=dict(type='str'),
|
||||
authority_key_identifier=dict(type='str'),
|
||||
)),
|
||||
include_renewal_cert_id=dict(type='str', choices=['never', 'when_ari_supported', 'always'], default='never'),
|
||||
))
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
|
|
Loading…
Reference in New Issue