“Let’s encrypt” Seafile using a free(!) certificate

(This post is an updated version of an older HOWTO I wrote a few months ago and which has quickly become outdated.)

1. Introduction

In a previous post I documented how I set up Seafile on a Raspberry Pi. To be honest, that guide wasn’t all that spectacular: I simply followed the official manual. To summarize: MySQL, nginx + SSL, Seafile.

In this post I’ll show you how I’ve recently configured my Seafile installation to always have a perfectly valid and signed SSL/TLS-certificate that is accepted by all web browsers. And it’s free. To achieve this I’m using a certificate from the “Let’s Encrypt” CA. The kind people from the Internet Security Research Group, the public benefit corporation behind Let’s Encrypt, have developed a custom protocol, ACME, in order to automate the usually cumbersome domain ownership verification procedures.

Even though the provided certificates are very short-lived (3 months validity), the resulting setup is actually much less maintenance-intensive than my previous (free) StartCom StartSSL certificates: I don’t have to keep track of expiry dates, manually create certificates and signing requests, and so on. The whole premise of the Let’s Encrypt approach is to force the admin’s hands in automating certificate handling, all while providing the necessary tools.

2. Installation

Since the Let’s Encrypt project is still in its infancy, I didn’t really consider any of the alternative ACME clients, I stayed with the official one, “certbot”. It has a multitude of plugins to facilitate its integration in an existing server setup. To be honest, though, I’m no fan of having an automated tool mess with my nginx configuration files. That’s why I elected to use the socalled webroot plugin. Basically, I indicate to certbot where my webroot directory is located. The client in turn communicates with the Let’s Encrypt-ACME-server and puts some cryptographic tokens in a hidden subdirectory of the webroot. These are checked by the (remote) ACME-server in order to ascertain that the client (or rather the admin) truly has domain ownership (or at least administrative rights). Finally the (locally created!) certificates are signed by the CA and stored in /etc/letsencrypt/live. Do note that the ACME-server always checks the cryptographic token through http. (security reasons) It does however follow redirects from http to https. (This just in case that you set up your server to listen on port 443 exclusively.)

To actually install certbot I refer you to the official documentation. Indeed, it very much depends on your linux distribution whether or not certbot is provided through your default package manager. Depending on the path you chose, you may end up with one of the following commands to interact with the ACME-servers: letsencrypt, letsencrypt-auto, certbot or certbot-auto. In this tutorial I’ll use the certbot command, but they’re all compatible in regard to the arguments they take. (letsencrypt is the old name of the client; certbot the new one.)

3. Preparing the webroot folder

The “webroot” method of the letsencrypt client presents a minor inconvenience when used in conjunction with Seafile: it requires an active webroot... However, in our case, the nginx web server is used as a simple FastCGI-Proxy for Seafile, i.e. it simply forwards incoming requests to one of seafile’s daemons.

Hence the need for a slight modification to nginx’s config.

But first we’ll create a “fake” webroot for nginx/seafile:

sudo mkdir /mnt/usbdrive/certbot-webroot

(/mnt/usbdrive corresponds to the drive on which I’ve installed Seafile. You could put the webroot anywhere you like.)

Then we add the additional location directive inside of the main server block:

# in /etc/nginx/sites-enabled/seafile:

location '/.well-known/acme-challenge' {
    default_type "text/plain";
    root /mnt/usbdrive/certbot-webroot;
}

Using this directive all requests to the acme-challenge folder will be redirected to the certbot-webroot folder.

Next step: reload nginx (sudo service nginx reload). Note that at no time we’ll have to truly shut down nginx.

4. Calling certbot

The first call to certbot instructs it to use the webroot plugin and create a certificate for the indicated domain.

sudo certbot certonly --webroot -w /mnt/usbdrive/certbot-webroot -d seafile.example.org

Certbot interactively asks for an e-mail address (for urgent notices and certificate revocation) and that’s about it! If everything works fine (check the program’s output!), the newly created (and signed!) certificates should be stored in /etc/letsencrypt/live/seafile.example.org.

The last step of the initial setup consists in updating nginx’s config file to point to the new location of the certificate and key. (I suppose that you’re already hosting Seafile through SSL/TLS, even if it’s “only” with a self-signed certificate. If you’re using the Let’s Encrypt certificates for an initial setup, you should probably consult either the official manual or my own write-up for the specifics of configuring Seafile for SSL/TLS.)

ssl_certificate     /etc/letsencrypt/live/seafile.example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/seafile.example.org/privkey.pem;

Now, reload nginx a second time and everything should work just fine. (If not, try reloading seafile too.)

5. Automating certificate renewal

The last piece of the puzzle, the crucial one, is to actually automate the renewal process. This has become very easy with certbot’s recent enhancements regarding exit codes and addition of a --quiet switch. Current versions are ready to be called directly through cron without having to use a wrapper script. (See below for an example script.)

It’s sufficient to add a daily (or twice daily) call to either the system crontab or root’s own crontab. Type crontab -e as root in order to edit root’s crontab. Add the following line:

47 */12 * * * sleep 16; /path/to/certbot renew --quiet --post-hook "/usr/sbin/service nginx reload"

Please change the minutes number (47) and seconds number (16) to random integers between 0 and 59, in order to spread the load on the ACME-servers!

That should be all! Certbot only actually contacts the server if one of the configured certificates approaches its expiration date (leaving you a month to fix any upcoming issues). It only exits with code 1 if it encounters a serious problem. In this case cron will collect the error output and send an e-mail to the administrator. Obviously you should make sure that your crontab contains a working mailto directive! (e.g. MAILTO="admin@example.org")

Note (2016-08-10, certbot v0.8.1): Currently the command given to --post-hook is always executed even when no renewal attempt was made. This is a bug; it will be fixed in the next release. In the meantime you may reduce certbot’s frequency in crontab or use a simple wrapper script that checks the modification time of the certificate.

Further debugging output can be found in /var/log/letsencrypt.

I hope this write-up doesn’t contain too many errors. If you find any, please notify my in the comments.


Old renewal script (no longer needed!)

#!/bin/bash
# This script should be saved as /etc/cron.daily/certbot-renew
# Don't forget to set the "execute" permission...
# (script originally based on
# https://community.letsencrypt.org/t/how-to-automatically-renew-certificates/4393/7)

if [ "$(id -u)" != "0" ]; then
   echo "This script must be run as root!"             1>&2
   exit 1
fi

EMAIL=admin@example.org
CERTDIR="/etc/letsencrypt/live/seafile.example.org"

# add a random sleep interval (up to one hour)
# in order to avoid DDOSing the server; timezones will
# take care of distributing the server load over 24 hours.
sleep $[ $RANDOM % 3600 ]s

# The exact command/path to call the client depends
# on your installation method, but the arguments should be
# as given by the example command below:
certbot renew --quiet --post-hook "service nginx reload"

if [ $? -ne 0 ]
then
        ERRORLOG=$(tail /var/log/letsencrypt/letsencrypt.log)
        echo -e "Let's Encrypt cert. for Seafile has not been renewed!\n\n" \
            $ERRORLOG | mail -s "ALERT: Lets Encrypt failed!" $EMAIL
else
        if find "$CERTDIR" -mmin -60 | grep -q fullchain.pem
        then
                echo "Let's Encrypt certificate for Seafile updated." \
                    | mail -s "Seafile TLS cert. update successful." $EMAIL
        fi
fi

exit 0

Note that the script above assumes a valid and working mail configuration on your server. I’m using a very basic setup consisting of the msmtp nullmailer and the heirloom-mailx. But that’s for another time ;-)

Tags : lets-encryptcertbotraspberrypissltlsseafilehowto

Copyright © 2015–2018 Hambier
GS RU