From 1bd6368d2d4a0e296cd9d2a16952445441e5ed87 Mon Sep 17 00:00:00 2001 From: Kishi85 Date: Tue, 10 Mar 2026 13:26:33 +0100 Subject: [PATCH 1/3] acme: fix default return in ARI prototype --- acertmgr/authority/acme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acertmgr/authority/acme.py b/acertmgr/authority/acme.py index 0a02c91..9e48589 100644 --- a/acertmgr/authority/acme.py +++ b/acertmgr/authority/acme.py @@ -38,4 +38,4 @@ def revoke_crt(self, crt, reason=None): # @param issuer certificate necessary for correct AKI data # @return True if the certificate should be renewed, False otherwise def check_ari_for_renewal(self, crt, issuer): - raise False # Do not mark for renewal if this is not implemented properly (use default renewal checks instead) + return False # Do not mark for renewal if this is not implemented properly (use default renewal checks instead) From f42402cd88b3c377d0d921fefd1caf9c6641f1e5 Mon Sep 17 00:00:00 2001 From: Kishi85 Date: Tue, 10 Mar 2026 13:27:51 +0100 Subject: [PATCH 2/3] acme.v2: Run check only if renewalInfo is available in endpoint discovery --- acertmgr/authority/v2.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acertmgr/authority/v2.py b/acertmgr/authority/v2.py index 3046135..4cc0d48 100644 --- a/acertmgr/authority/v2.py +++ b/acertmgr/authority/v2.py @@ -303,6 +303,8 @@ def revoke_crt(self, crt, reason=None): # @param issuer certificate necessary for correct AKI data # @return True if the certificate should be renewed, False otherwise def check_ari_for_renewal(self, crt, issuer): + if 'renewalInfo' not in self.directory: + return False try: sn = tools.get_cert_serialnumber_bytes(crt) aki = tools.get_issuer_cert_aki_bytes(issuer) From b2da23795d6a1e107826e52af41a57fc251fc603 Mon Sep 17 00:00:00 2001 From: Kishi85 Date: Tue, 10 Mar 2026 13:33:21 +0100 Subject: [PATCH 3/3] Add configuration option validate_ari so RFC9773 checks can be optionally disabled --- README.md | 1 + acertmgr/__init__.py | 2 +- acertmgr/configuration.py | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c985ef0..f3178b8 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ By default the directory (work_dir) containing the working data (csr,certificate | account_key_algorithm | d,**g** | Key-algorithm for newly generated account keys (RSA, EC, ED25519, ED448) | RSA | | account_key_length | d,**g** | Key-length for newly generated RSA account keys (in bits) or EC curve (256=P-256, 384=P-384, 521=P-521) | depends on account_key_algorithm | | ttl_days | d,**g** | Renew certificate if it has less than this value validity left (in days or if between 0 and 1 as a fraction of total certificate lifetime) | 0.33333 | +| validate_ari | d,**g** | Renew certificate if it's RFC9773 ARI window has been reached (if available) | true | | validate_ocsp | d,**g** | Renew certificate if it's OCSP status is REVOKED. Allowed values for this key are: false, sha1, sha224, sha256, sha384, sha512 | sha1 (as mandated by RFC5019) | | cert_dir | d,**g** | Directory containing all certificate related data (crt,key,csr) | {work_dir} | | key_algorithm | d,**g** | Key-algorithm for newly generated private keys (RSA, ECC, ED25519, ED448) | RSA | diff --git a/acertmgr/__init__.py b/acertmgr/__init__.py index e40ea31..d455ca5 100755 --- a/acertmgr/__init__.py +++ b/acertmgr/__init__.py @@ -198,7 +198,7 @@ def main(): or (str(config.get('validate_ocsp')).lower() != 'false' and issuer and not tools.is_ocsp_valid(cert, issuer, config['validate_ocsp']) ) - or (issuer + or (str(config.get('validate_ari')).lower() != 'false' and issuer and cert_check_ari_for_renewal(cert, issuer, domainconfigs, runtimeconfig['fallback_authority']) ) ): diff --git a/acertmgr/configuration.py b/acertmgr/configuration.py index d53e750..362298e 100644 --- a/acertmgr/configuration.py +++ b/acertmgr/configuration.py @@ -19,6 +19,7 @@ DEFAULT_CONF_DIR = "/etc/acertmgr" DEFAULT_CONF_FILENAME = "acertmgr.conf" DEFAULT_TTL = 0.33333 # percent of the certificates total lifetime +DEFAULT_VALIDATE_ARI = "true" DEFAULT_VALIDATE_OCSP = "sha1" # mandated by RFC5019 DEFAULT_API = "v2" DEFAULT_AUTHORITY = "https://acme-v02.api.letsencrypt.org" @@ -112,6 +113,9 @@ def parse_config_entry(entry, globalconfig, runtimeconfig): update_config_value(config, 'ttl_days', localconfig, globalconfig, DEFAULT_TTL) config['ttl_days'] = float(config['ttl_days']) + # Validate ARI on certificate verification + update_config_value(config, 'validate_ari', localconfig, globalconfig, DEFAULT_VALIDATE_ARI) + # Validate OCSP on certificate verification update_config_value(config, 'validate_ocsp', localconfig, globalconfig, DEFAULT_VALIDATE_OCSP)