Difference between revisions of "Let’s Encrypt"

From wiki
(New page)
 
(Installation guide)
Line 18: Line 18:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Renewal ==
+
== Configuration ==
  
Save the following file as <code>/usr/local/sbin/renew_certificates</code> and make it eecutable
+
=== Nginx ===
 +
 
 +
* First create folder <code>/var/www/acme-challenge</code>
 +
<syntaxhighlight lang="console">
 +
# mkdir -p /var/www/acme-challenge/.well-known/acme-challenge
 +
# chmod -R 750 /var/www/acme-challenge
 +
# chown chown -R root:www-data /var/www/acme-challenge
 +
</syntaxhighlight>
 +
 
 +
* Create file <code>/etc/nginx/snippets/acme-challenge.conf</code>
 +
<syntaxhighlight lang="nginx">
 +
location ^~ /.well-known/acme-challenge/ {
 +
    root /var/www/acme-challenge;
 +
    auth_basic off;
 +
    allow all;
 +
}
 +
</syntaxhighlight>
 +
 
 +
=== Renewal Script ===
 +
 
 +
Let’s Encrypt delivers certificates that are valid for 90 days. It make automatic renewal an important part of the setup. They also have a [https://community.letsencrypt.org/t/rate-limits-for-lets-encrypt/6769 limit of 5 certificates per week per domain].
 +
 
 +
In order to avoid blocking your domain (in case you need to create a new certificate), the following script will renew at most one certificate per run and will run every two days.
 +
 
 +
Certificates are renewed 30d before expiry. Additionally, if a certificate is close to expiry (20 days) a warning will be displayed with details.
 +
 
 +
* Save the following file as <code>/usr/local/sbin/renew_certificates</code> and make it executable
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
#! /usr/bin/env python
 
#! /usr/bin/env python
Line 126: Line 152:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Don't forget to edit the config on top of the file.
+
Don't forget to edit the config on top of the file. If you are installing letsencrypt for the first time, keep an empty array.
 
 
This script will renew one certificate per run. Additionally, if a certificate is close to expiry (20 days) a warning will be displayed with details.
 
  
You can then run this automatically during the night. Add this to the file <code>/etc/crontab</code>
+
* You can then run it automatically during the night. Add this to the file <code>/etc/crontab</code>
 
<syntaxhighlight lang="bash">
 
<syntaxhighlight lang="bash">
 
12 4    */2 * * root    /usr/local/sbin/renew_certificates
 
12 4    */2 * * root    /usr/local/sbin/renew_certificates
 
</syntaxhighlight>
 
</syntaxhighlight>
This will run the renew script every other nights at 4:12. The every other night parts ensure that you stay bellow the [https://community.letsencrypt.org/t/rate-limits-for-lets-encrypt/6769 beta rate limits] so you can always issue a manual certificate if needed.
 

Revision as of 21:38, 23 February 2016

Warning Warning: These instructions were only tested on Debian. It will probably work for other Linux distributions, but you might need to adapt the provided instructions.


Warning Warning: This page is a work in progress and is not completed. Important informations might be missing or wrong.

This guide will show you how to get free certificates that are automatically renewed.

Prerequisite

This guide assume that you have an Nginx server running and listening on port 80.

The certificates can be then used for other purposes, like email server. Nginx is only used for the renewal process.

Installation

At the time of writing, letsencrypt is not available in the stable release of Debian. You will need to configure your apt to work with stretch (testing) for the following command to work.

apt install letsencrypt

Configuration

Nginx

  • First create folder /var/www/acme-challenge
# mkdir -p /var/www/acme-challenge/.well-known/acme-challenge
# chmod -R 750 /var/www/acme-challenge
# chown chown -R root:www-data /var/www/acme-challenge
  • Create file /etc/nginx/snippets/acme-challenge.conf
location ^~ /.well-known/acme-challenge/ {
    root /var/www/acme-challenge;
    auth_basic off;
    allow all;
}

Renewal Script

Let’s Encrypt delivers certificates that are valid for 90 days. It make automatic renewal an important part of the setup. They also have a limit of 5 certificates per week per domain.

In order to avoid blocking your domain (in case you need to create a new certificate), the following script will renew at most one certificate per run and will run every two days.

Certificates are renewed 30d before expiry. Additionally, if a certificate is close to expiry (20 days) a warning will be displayed with details.

  • Save the following file as /usr/local/sbin/renew_certificates and make it executable
#! /usr/bin/env python

from datetime import timedelta, date
import time
import subprocess
import OpenSSL
import pyrfc3339

# Configure your certificates here
# Each item in the list represent one certificate
# If domains list contains multiple domains, the first one is used as filename for the certificate
config = [{
    'domains': ['www.example.com', 'example.com'],
    'reload': [['service', 'nginx', 'reload']]
}, {
    'domains': ['imap.example.rocks', 'smtp.example.com'],
    'reload': [['service', 'dovecot', 'reload'], ['service', 'exim4', 'reload']]
}]

RENEW_CMD = '/usr/bin/letsencrypt'
RENEW_ARGS = ['certonly', '--non-interactive', '-a', 'webroot', '--webroot-path', '/var/www/acme-challenge/', '--keep-until-expiring', '--expand']
LIVE_FOLDER = '/etc/letsencrypt/live/'
CERT_FILE = '/cert.pem'
RENEW_DATE = timedelta(days=30)
ALERT_DATE = timedelta(days=20)

def get_date(cert_path):
    with open(cert_path) as f:
        x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, f.read())
    timestamp = OpenSSL.crypto.X509.get_notAfter(x509)
    reformatted_timestamp = [timestamp[0:4], "-", timestamp[4:6], "-",
                             timestamp[6:8], "T", timestamp[8:10], ":",
                             timestamp[10:12], ":", timestamp[12:]]
    return pyrfc3339.parse("".join(reformatted_timestamp)).date()

def get_next_renew():
    mindate = date.max
    cert = None
    for conf in config:
        main_name = conf.get('domains')[0]
        filename = LIVE_FOLDER + main_name + CERT_FILE
        expiration = get_date(filename)
        if expiration < mindate:
            mindate = expiration
            cert = {
                'file': filename,
                'domains': conf.get('domains'),
                'reload': conf.get('reload'),
                'expiration': expiration
            }
    return cert

def should_renew(cert):
    now = date.today()
    return cert.get('expiration') - now < RENEW_DATE

def should_alert(cert):
    now = date.today()
    return cert.get('expiration') - now < ALERT_DATE

def renew(cert):
    cmd = renew_cmd(cert)
    subprocess.call(cmd)

def renew_cmd(cert):
    return [RENEW_CMD] + RENEW_ARGS + [arg for domain in cert.get('domains') for arg in ['-d', domain]]

def after_cert(cert):
    if 'reload' in cert:
        print 'Restarting services:'
        for cmd in cert.get('reload'):
            print ' '.join(cmd)
            subprocess.call(cmd)

if __name__ == "__main__":
    next = get_next_renew()
    if should_renew(next):
        print 'Renewing certificate for ' + ', '.join(next.get('domains')) + ' that will expire on ' + next.get('expiration').isoformat() + '\n\n'
        renew(next)

        # Waiting OCSP responses
        # https://community.letsencrypt.org/t/ocsp-server-sometimes-has-malformed-response-of-5-bytes-or-unauthorized/10568/10
        time.sleep(5)
        
        was_renewed = get_date(next.get('file')) != next.get('expiration')
        if was_renewed:
            after_cert(next)

        next = get_next_renew()
        if should_alert(next):
            print """
=============================================================
                          WARNING
=============================================================

Your certificate for %s will expire on %s

Certificate should have been renewed already. Maybe there is a issue with renewal process.

Renew command
%s
""" % (', '.join(next.get('domains')), next.get('expiration').isoformat(), ' '.join(renew_cmd(cert)))

Don't forget to edit the config on top of the file. If you are installing letsencrypt for the first time, keep an empty array.

  • You can then run it automatically during the night. Add this to the file /etc/crontab
12 4    */2 * * root     /usr/local/sbin/renew_certificates