Skip to content

Instantly share code, notes, and snippets.

@nikolaik
Created October 9, 2016 19:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nikolaik/e6a4f4705f1241c36c865f008ac1d42c to your computer and use it in GitHub Desktop.
Save nikolaik/e6a4f4705f1241c36c865f008ac1d42c to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# coding: utf-8
import os
from subprocess import Popen, PIPE
import shutil
# SETTINGS
CERT_DIR = '/etc/nginx/ssl/'
WEBROOT = '/tmp/letsencrypt'
LEGO = './lego'
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
LEGO_CERT_DIR = os.path.join(BASE_DIR, '.lego/certificates/')
RENEW_MIN_DAYS = 30
# Load settings from local_settings.py
try:
from local_settings import *
except ImportError:
pass
def get_env():
env = os.environ.copy()
# Set DNS vars in environment
for k, v in RFC2136.items():
env['RFC2136_' + k] = v
return env
def copy_certs(domain):
cert_path = os.path.join(LEGO_CERT_DIR, '{}.crt'.format(domain))
key_path = os.path.join(LEGO_CERT_DIR, '{}.key'.format(domain))
dst_dir = os.path.join(CERT_DIR, domain)
# copy cert and key
shutil.copy(cert_path, dst_dir)
shutil.copy(key_path, dst_dir)
def lego_cmd(domains):
domain_params = []
for d in domains:
domain_params.append('-d')
domain_params.append(d)
global_options = [
'--accept-tos',
'--email', ACCOUNT,
'--dns', 'rfc2136',
'--exclude', 'http-01',
'--exclude', 'tls-sni-01',
] + domain_params
command_options = [
'--days', str(RENEW_MIN_DAYS),
'--reuse-key'
]
cmd = [LEGO] + global_options + ['renew'] + command_options
print(' '.join(cmd))
p = Popen(cmd, stdout=PIPE, stderr=PIPE, env=get_env())
out, err = p.communicate()
if p.returncode != 0:
print(out.decode('utf-8'), err.decode('utf-8'))
return p.returncode
def renew(domains):
if len(domains) == 0:
return
primary_domain = domains[0] # First in list
cert_dir = os.path.join(CERT_DIR, primary_domain)
if not os.path.exists(cert_dir):
# Create missing cert dir
os.mkdir(cert_dir)
ret = lego_cmd(domains)
# TODO chmod private files
if ret == 0:
# FIXME: ret is too often 0
copy_certs(primary_domain)
return ret
def reload_nginx():
cmd = ['nginx', '-s', 'reload']
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
if p.returncode != 0:
print(out.decode('utf-8'), err.decode('utf-8'))
return False
return True
def renew_certs(cert_domains):
"""Take a list of domain certs and try to renew them"""
needs_reload = False
for cert in cert_domains:
print('{}...'.format(cert[0]))
res = renew(cert)
if res == 0:
print('OK')
needs_reload = True
elif res == 1:
print("ERROR")
if needs_reload:
print("Reloading Nginx")
reload_nginx()
if __name__ == '__main__':
"""
Create/renew letsencrypt certificates with the lego client
Ref: https://github.com/xenolf/lego
Ref: https://letsencrypt.org
Uses DNS verification via RFC2136.
Assumes the domain server is setup with correct keys
Provides certs that can be included like this:
ssl_stapling on;
ssl_stapling_verify on;
ssl_certificate /etc/nginx/ssl/DOMAIN/DOMAIN.crt;
ssl_certificate_key /etc/nginx/ssl/DOMAIN/DOMAIN.key;
"""
if not os.path.exists(CERT_DIR):
print("Creating nginx cert dir {}.".format(CERT_DIR))
os.mkdir(CERT_DIR)
if not os.path.exists(WEBROOT):
print("Creating webroot dir {}.".format(WEBROOT))
os.mkdir(WEBROOT)
if not shutil.which(LEGO):
print("ERROR: Missing {} in PATH.".format(LEGO))
exit(1)
print("Renewing {} certs with letsencrypt".format(len(CERT_DOMAINS)))
renew_certs(CERT_DOMAINS)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment