From f01140e89b9fde934e607c72bbeb53817dc7f4c5 Mon Sep 17 00:00:00 2001 From: Kishi85 Date: Thu, 28 Mar 2019 00:34:53 +0100 Subject: [PATCH] acertmgr: Add option to supersede previous cert on renewal Add option to automatically revoke the previous certificate with reason superseded after deployment and all actions have been successful. --- README.md | 1 + acertmgr/__init__.py | 16 +++++++++++++++- acertmgr/configuration.py | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 44cc0b0..18633b5 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ By default the directory (work_dir) containing the working data (csr,certificate | csr_file | **d**,g | Path to store (and load) the certificate CSR file | {cert_dir}/{cert_id}.csr | | ca_file | **d**,g | Path to store (and load) the certificate authority file | {cert_dir}/{cert_id}.ca | | cert_file | **d** | Path to store (and load) the certificate file | {cert_dir}/{cert_id}.crt | +| cert_revoke_superseded | **d**,g | Revoke the previous certificate with reason "superseded" after successful deployment | false | | key_file | **d**,g | Path to store (and load) the private key file | {cert_dir}/{cert_id}.key | | mode | **d**,g | Mode of challenge handling used | standalone | | webdir | **d**,g | [webdir] Put acme challenges into this path | /var/www/acme-challenge/ | diff --git a/acertmgr/__init__.py b/acertmgr/__init__.py index 5daefa7..7e03004 100755 --- a/acertmgr/__init__.py +++ b/acertmgr/__init__.py @@ -85,6 +85,7 @@ def cert_get(settings): # if resulting certificate is valid: store in final location if tools.is_cert_valid(crt, settings['ttl_days']): + print("Certificate '{}' renewed and valid until {}".format(crt, crt.not_valid_after)) tools.write_pem_file(crt, settings['cert_file'], stat.S_IREAD) if "static_ca" in settings and not settings['static_ca'] and ca is not None: tools.write_pem_file(ca, settings['ca_file']) @@ -163,6 +164,7 @@ def main(): # Mode: issue certificates (implicit) # post-update actions (run only once) actions = set() + superseded = set() # check certificate validity and obtain/renew certificates if needed for config in domainconfigs: cert = None @@ -172,6 +174,8 @@ def main(): ('force_renew' in runtimeconfig and re.search(r'(^| ){}( |$)'.format( re.escape(runtimeconfig['force_renew'])), config['domains'])): cert_get(config) + if str(config.get('cert_revoke_superseded')).lower() == 'true' and cert: + superseded.add(cert) # deploy new certificates after all are renewed for config in domainconfigs: @@ -181,6 +185,7 @@ def main(): actions.add(cert_put(cfg)) # run post-update actions + all_actions_success = True for action in actions: if action is not None: try: @@ -188,4 +193,13 @@ def main(): output = subprocess.check_output(action, shell=True, stderr=subprocess.STDOUT) print("Executed '{}' successfully: {}".format(action, output)) except subprocess.CalledProcessError as e: - print("Execution of '{}' failed with error '{}': {}".format(e.cmd, e.returncode, e.output)) \ No newline at end of file + print("Execution of '{}' failed with error '{}': {}".format(e.cmd, e.returncode, e.output)) + all_actions_success = False + + # revoke old certificates as superseded + if all_actions_success: + for superseded_cert in superseded: + print("Revoking previous certificate '{}' valid until {} as superseded".format( + superseded_cert, + superseded_cert.not_valid_after)) + cert_revoke(superseded_cert, domainconfigs, reason=4) # reason=4 is superseded diff --git a/acertmgr/configuration.py b/acertmgr/configuration.py index 32fa463..5de8973 100644 --- a/acertmgr/configuration.py +++ b/acertmgr/configuration.py @@ -123,6 +123,9 @@ def parse_config_entry(entry, globalconfig, runtimeconfig): update_config_value(config, 'ttl_days', localconfig, globalconfig, DEFAULT_TTL) config['ttl_days'] = int(config['ttl_days']) + # Revoke old certificate with reason superseded after renewal + update_config_value(config, 'cert_revoke_superseded', localconfig, globalconfig, "false") + # Use a static cert request update_config_value(config, 'csr_static', localconfig, globalconfig, "false")