-#!/usr/bin/python2
-# We need to force py2 until M2Crypto is ported:
-# https://gitlab.com/m2crypto/m2crypto/issues/114
+#!/usr/bin/python
+#-*- coding:utf-8 -*-
# pylint: disable=invalid-name
"""Renew Let's Encrypt certs!
import configparser
except ImportError:
import ConfigParser as configparser
+import cryptography.hazmat.backends
+from cryptography import x509
try:
from cStringIO import StringIO
except ImportError:
import datetime
import logging
import logging.handlers
-import M2Crypto
import os
-import pytz
import subprocess
import sys
def load_cert(path):
"""Load the cert at |path|"""
- with open(path) as f:
+ with open(path, 'rb') as f:
data = f.read()
- return M2Crypto.X509.load_cert_string(data)
+ return x509.load_pem_x509_certificate(
+ data, cryptography.hazmat.backends.default_backend())
def load_live_cert(domain):
logging.info('%s: checking', domain)
conf = load_conf(domain)
- webroot_path = conf.get('[webroot_map', domain)
+ try:
+ webroot_path = conf.get('[webroot_map', domain)
+ except configparser.NoOptionError:
+ webroot_path = conf.get('renewalparams', 'webroot_path')
+ # The conf writing has a bug here where it appends a comma.
+ webroot_path = webroot_path.rstrip(',')
cert_path = os.path.realpath(conf.get('globals', 'cert'))
cert = load_cert(cert_path)
- stamp = cert.get_not_after()
- now = pytz.timezone('UTC').localize(datetime.datetime.now())
- delta = stamp.get_datetime() - now
+ delta = cert.not_valid_after - datetime.datetime.utcnow()
logging.info('%s: expires in %2s days', domain, delta.days)
cmd = [
'--webroot-path', webroot_path,
'-d', domain,
]
- san = cert.get_ext('subjectAltName').get_value()
- domains = [x.strip()[4:] for x in san.split(',')]
- domains.remove(domain)
+ domains = []
+ try:
+ san = cert.extensions.get_extension_for_oid(
+ x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
+ domains = san.value.get_values_for_type(x509.DNSName)
+ except x509.ExtensionNotFound:
+ pass
for d in domains:
cmd += ['-d', d]
if delta.days < 30:
logging.info('%s: renewing', domain)
- logging.info('%s: %s', domain, cmd)
+ logging.info('%s: %s', domain, ' '.join(cmd))
if not dry_run:
- subprocess.check_call(cmd)
+ try:
+ subprocess.check_call(cmd)
+ except subprocess.CalledProcessError:
+ logging.error('failed', exc_info=True)
+ return 0
ret = 1
# Try to revoke the old one.
- cmd = ['certbot', 'revoke', '--cert-path', cert_path]
+ cmd = ['certbot', 'revoke', '--no-delete-after-revoke', '--cert-path',
+ cert_path]
logging.info('%s: revoking old cert', domain)
- logging.info('%s: %s', domain, cmd)
+ logging.info('%s: %s', domain, ' '.join(cmd))
if not dry_run:
- subprocess.check_call(cmd)
+ try:
+ subprocess.check_call(cmd, stdin=open('/dev/null', 'r'))
+ except subprocess.CalledProcessError:
+ logging.error('failed', exc_info=True)
else:
logging.info('%s: up-to-date!', domain)