Created
December 18, 2016 07:18
-
-
Save jh0ker/f1a735e152c2d4a96fd13bb5d40d203f to your computer and use it in GitHub Desktop.
Python 3.5 - Script to check letsencrypt certificates for all domains, renew them if required, concatenate them into bundles and restart haproxy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from os import listdir, system | |
from os.path import join | |
from subprocess import run, PIPE | |
import logging | |
import re | |
from datetime import datetime, timedelta | |
logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', | |
level=logging.INFO, | |
filename='/root/renew_certificates.log', | |
filemode='w') | |
BASE_DIR = '/etc/letsencrypt/live' | |
CERT_FILE = 'fullchain.pem' | |
KEY_FILE = 'privkey.pem' | |
COMBINED_FILE = 'cert_key.pem' | |
DOMAINS = listdir(BASE_DIR) | |
def certificate_expires_soon(domain): | |
try: | |
logging.info('Testing certificate for domain "%s"', domain) | |
cmd = ['openssl', 'x509', '-in', join(BASE_DIR, domain, CERT_FILE), '-text', '-noout'] | |
openssl_out = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True) | |
if openssl_out.returncode is not 0: | |
logging.error( | |
'Command "%s" failed with return code %d', ' '.join(cmd), openssl_out.returncode) | |
logging.error('Captured stdout: %s', openssl_out.stdout) | |
logging.error('Captured stderr: %s', openssl_out.stderr) | |
sys.exit(1) | |
valid_until, = re.match( | |
r'.*Not After : (.*?)\n.*', openssl_out.stdout, flags=re.DOTALL).groups() | |
valid_until = datetime.strptime(valid_until, '%b %d %H:%M:%S %Y %Z') | |
time_left = valid_until - datetime.now() | |
logging.info('Valid until: %s', valid_until) | |
logging.info('Time left: %s', time_left) | |
logging.info('Requires renewal: %s', time_left < timedelta(days=1)) | |
return time_left < timedelta(days=1) | |
except: | |
logging.exception('Testing certificate for domain "%s" failed!', domain) | |
sys.exit(1) | |
if any(certificate_expires_soon(domain) for domain in DOMAINS): | |
logging.info('At least one certificate is about to expire') | |
# Renew certificate | |
cmd = ['letsencrypt', 'renew', '--agree-tos'] | |
logging.info('Executing "%s"', ' '.join(cmd)) | |
letsencrypt_out = run(cmd, universal_newlines=True) | |
if letsencrypt_out.returncode is not 0: | |
logging.error( | |
'Command "%s" failed with return code %d', ' '.join(cmd), letsencrypt_out.returncode) | |
sys.exit(1) | |
for domain in DOMAINS: | |
# Generate combined file for haproxy | |
cmd = 'cat %s %s > %s' % ( | |
join(BASE_DIR, domain, CERT_FILE), | |
join(BASE_DIR, domain, KEY_FILE), | |
join(BASE_DIR, domain, COMBINED_FILE)) | |
logging.info('Executing "%s"', cmd) | |
returncode = system(cmd) | |
if returncode is not 0: | |
logging.error('Command "%s" failed with return code %d', cmd, returncode) | |
sys.exit(1) | |
# Restart haproxy | |
cmd = ['service', 'haproxy', 'restart'] | |
logging.info('Executing "%s"', ' '.join(cmd)) | |
service_out = run(cmd, universal_newlines=True) | |
if service_out.returncode is not 0: | |
logging.error( | |
'Command "%s" failed with return code %d', ' '.join(cmd), service_out.returncode) | |
sys.exit(1) | |
logging.info('Success!') | |
else: | |
logging.info('No certificate is about to expire') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment