Using a (free!) “Let’s encrypt”-certificate for Seafile

(An updated version of this guide can be found here.)

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, “letsencrypt”. 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 the letsencrypt client 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.

Here we go:

sudo apt-get install git
sudo git clone /opt/letsencrypt
cd /opt/letsencrypt
sudo -H ./letsencrypt-auto

These commands set up the client program in /opt/letsencrypt. Note that I got some obscure warnings about a wrongly set $HOME variable when I ran the last command without the “-H” option. (This option sets $HOME to /root instead of the regular account’s /home/pi or similar.)

3. Seafile specifics

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 -p /mnt/usbdrive/letsencrypt-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/letsencrypt-webroot;

Using this directive all requests to the acme-challenge folder will be redirected to the letsencrypt-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 letsencrypt

In order to automate Let’s Encrypt (that’s the whole point after all), we’ll pass the necessary parameters through a config file instead of as parameters on the command line. In practice I simply copied a sample config file and modified it to suit my needs. In particular I opted for a standard 2048 bits key.

sudo cp /opt/letsencrypt/examples/cli.ini /etc/letsencrypt

Edit /etc/letsencrypt/cli.ini and modify it as follows:

# This is an example of the kind of things you can do in a configuration file.
# All flags used by the client can be configured here. Run Let's Encrypt with
# "--help" to learn more about the available options.

rsa-key-size = 2048

# Uncomment and update to register with the specified e-mail address
email =

# Uncomment and update to generate certificates for the specified
# domains.
domains =

# Uncomment to use a text interface instead of ncurses
text = True

# Uncomment to use the webroot authenticator. Replace webroot-path with the
# path to the public_html / webroot folder being served by your web server.
authenticator = webroot
webroot-path = /mnt/usbdrive/letsencrypt-webroot

Now everything’s set up and the system should be ready to retrieve its first Let’s Encrypt certificates:

cd /opt/letsencrypt
sudo -H ./letsencrypt-auto certonly

If everything works fine (check the program’s output!), the newly created (and signed!) certificates should be stored in /etc/letsencrypt/live/

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/;
ssl_certificate_key /etc/letsencrypt/live/;

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 is achieved through a simple shell script that is run once a month by cron:

# This script should be saved as /root/
# Don't forget to set the "execute" permission...
# (script based on

if [ "$(id -u)" != "0" ]; then
   echo "This script must be run as root!"             1>&2
   echo "You may add the following line(s) to"         1>&2
   echo "root's crontab (sudo crontab -e):"            1>&2
   echo "# m h dom mon dow command"                    1>&2
   echo "  0 3 15  *   *   /root/" 1>&2
   echo 1>&2
   echo "Please replace the \"15\" (day of month) by some random integer" 1>&2
   echo "between 1 and 28 in order to avoid DDOSing the server."          1>&2
   exit 1

EMAIL=$(awk '/email/ {print $3}' /etc/letsencrypt/cli.ini)

# add a random sleep interval (up to one hour)
# in order to avoid DDOSing the server.
sleep $[ $RANDOM % 3600 ]s

cd /mnt/usbdrive/letsencrypt
./letsencrypt-auto --renew certonly

if [ $? -ne 0 ]
        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
        echo "Let's Encrypt certificate for Seafile updated." \
        | mail -s "Seafile TLS cert. update successful." $EMAIL
        service nginx reload

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 ;-)

Of course you could also copy this script (temporarily) to /etc/cron.hourly and check if everything is working as expected.

Tags : lets-encryptraspberrypissltlsseafilehowto

Copyright © 2015–2018 Hambier