Skip to content

Commit

Permalink
Allow to request renewal of a certificate according to ARI in acme_ce…
Browse files Browse the repository at this point in the history
…rtificate.
  • Loading branch information
felixfontein committed Apr 30, 2024
1 parent 6d4fc58 commit 787238e
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -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)."
6 changes: 5 additions & 1 deletion plugins/module_utils/acme/orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def _setup(self, client, data):
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']
Expand All @@ -44,6 +45,7 @@ def __init__(self, url):

self.status = None
self.identifiers = []
self.replaces_cert_id = None
self.finalize_uri = None
self.certificate_uri = None
self.authorization_uris = []
Expand All @@ -62,7 +64,7 @@ def from_url(cls, client, url):
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
Expand All @@ -76,6 +78,8 @@ def create(cls, client, identifiers):
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'])
Expand Down
36 changes: 35 additions & 1 deletion plugins/modules/acme_certificate.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,20 @@
- "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 if 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.
type: str
choices:
- never
- when_ari_supported
- always
default: never
version_added: 2.20.0
'''

EXAMPLES = r'''
Expand Down Expand Up @@ -586,6 +600,7 @@
)

from ansible_collections.community.crypto.plugins.module_utils.acme.utils import (
compute_cert_id,
pem_to_der,
)

Expand Down Expand Up @@ -622,6 +637,7 @@ def __init__(self, module, backend):
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']):
Expand Down Expand Up @@ -679,6 +695,15 @@ def is_first_step(self):
# stored in self.order_uri by the constructor).
return self.order_uri is None

def _get_cert_info_or_none(self):
if module.params.get('dest'):
filename = self.module.params['dest']
else:
filename = self.module.params['fullchain_dest']
if not os.path.exist(filename):
return None
return self.backend.get_cert_information(cert_filename=filename)

def start_challenges(self):
'''
Create new authorizations for all identifiers of the CSR,
Expand All @@ -693,7 +718,15 @@ def start_challenges(self):
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.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)
Expand Down Expand Up @@ -879,6 +912,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,
Expand Down

0 comments on commit 787238e

Please sign in to comment.