Let’s Encrypt with Hiawatha

Let’s Encrypt is a popular, free certificate authority provided by the Internet Security Research Group (ISRG). It is 100% automated, open source, and the results of each signing or revocation are transparent to the public. Let’s Encrypt (aka “LE”) makes it easy for admins to not only utilize HTTPS everyplace, but its emphasis on automation allows us to do so in a hassle-free manner. Recent versions of the security-oriented Hiawatha webserver ship with a script that makes this process even easier.

Let's Encrypt automated

Why Let’s Encrypt?

While there are certainly cases where using a traditional CA makes much more sense, LE is perfectly acceptable for most use cases most of the time. Besides being entirely free in both senses of the word, Let’s Encrypt is also very convenient. Its entire concept revolves around quick turnaround and automation. LE issues short-lived certs, only 90 days as of this writing, and the implication is that administrators are responsible to renew (reissue) those certs regularly—typically via the automated method of their choosing. From the point of issuance forward, proper automation should assure that LE certs are continuously renewed. Therefore, with LE + automation, the bother of keeping certs from lapsing is more or less mitigated.

How Let’s Encrypt works

To put it as simply as possible, LE’s API checks your webserver to verify that the system requesting the certificate is legitimate. The LE side makes an HTTP “GET” request for a custom, temporary file in the root of the web service. Once the file is served to the LE service as expected, a TLS cert is issued to the requester over the same API. If you’re following so far, you’ve noticed that several things must line up in order for the process to work properly:

  1. Public DNS records to the domain(s) for which you’re requesting certs must point to the server from where you’re making the request.
  2. Your server’s web service must be reachable by Let’s Encrypt’s servers for verification.
  3. The temporary verification file must be readable by the account under which your web service runs so it can, in turn, serve it to the LE verification servers.
  4. Once successfully issued, the certificate needs to be placed into the correct directory on your server, and the temporary verification file in your domain’s web root needs to be cleaned out.
  5. To avoid lapses, the TLS certificate needs to be periodically renewed.

Obviously the first two items need to be handled by the administrator, as is always the case with web hosting in general. As for the latter three, Hiawatha ships with a handy Let’s Encrypt script that can take care of the rest for you. The remainder of this article will walk you through setting it up correctly.

Using Let’s Encrypt with Hiawatha

From this point forward I’ll assume you’re running a UNIX®-like system such as Debian GNU/Linux when addressing specifics, though in general the principles should apply to any platform.

Requirements

The requirements for using Let’s Encrypt with Hiawatha webserver are all fairly straight-forward:

  • Publicly propagated DNS records for the domains where you want LE certs must exist (it typically takes 5-60 minutes from the time your DNS records are created until they are widely publicly available) and point to the webserver where you want the certs
  • Your webserver must be publicly reachable and answer HTTP requests on port 80
  • Root/admin rights on your server
  • Hiawatha webserver installed and functioning
  • The LE script which ships with Hiawatha is written in PHP, so you’ll need the PHP command-line interpreter installed* (the package for which is typically called something like “php5-cli” or “php7-cli” on most systems)

* If your server isn’t already utilizing PHP and you want to keep it clean, don’t worry – you don’t need the full PHP stack installed, just the CLI tool.

Setting up the script

When installing the Debian package for Hiawatha, the Let’s Encrypt script is included (located in /usr/share/doc/hiawatha/letsencrypt.tar.gz by default). Packages for other distributions are managed by many people under many different sets of project standards and therefore may or may not bundle the script. If yours doesn’t, you can always download the latest stable version here.

The Hiawatha LE script needs to be extracted someplace on the system. Locations like /opt/ or /usr/local/ are typical, standard locations for such things. For instance:

tar xzvf /usr/share/doc/hiawatha/letsencrypt.tar.gz -C /opt/

The above would result in a new directory under the /opt directory called “letsencrypt” (/opt/letsencrypt), which contains the script.

Now we need to ensure that the directory isn’t accessible by any system accounts which shouldn’t have access to it or its contents:

chown -R root:root /opt/letsencrypt
chmod 750 /opt/letsencrypt

Some variables need to be set prior to running the script the first time. Using the text editor of your choice, edit the config file. E.g.:

nvim /opt/letsencrypt/letsencrypt.conf

From here we’ll set some options to suit our needs. All are probably pretty self-explanatory, but I’ll go over each setting in order of appearance:

  • ACCOUNT_RSA_KEY_SIZE — The size of your LE account’s RSA key (2048/4096). Default is 2048
  • ACCOUNT_EMAIL_ADDRESS — A valid email address which essentially functions as your LE account name.
  • HIAWATHA_CONFIG_DIR — The directory where your Hiawatha configuration resides. Default is /etc/hiawatha
  • HIAWATHA_CERT_DIR — The location where your TLS certs will be placed, e.g. /etc/ssl/private/. Default is {HIAWATHA_CONFIG_DIR}/tls
  • HIAWATHA_RESTART_COMMAND — If Hiawatha will be automatically reset as part of the renewal process, how the system should accomplish this. Default is /etc/init.d/hiawatha restart
  • CERTIFICATE_RSA_KEY_SIZE — The size of the RSA key for use with the certificate (2048/4096). Default is 2048
  • RENEWAL_EXPIRE_THRESHOLD — How close in days the LE certificate needs to be to the expiration date before it is renewed by the script (1-88). Default is 7 (but Let’s Encrypt generally recommends a value of 30 days).
  • RENEWAL_REUSE_KEY — Whether or not the same RSA key should be reused when renewing the certificate, or if a new one should be automatically generated (true/false). Default is false
  • RENEWAL_SCRIPT_DIR — The directory relative to the base ‘letsencrypt‘ script where any supplementary scripts are located. Out of the box, there is only an example script which will also copy Let’s Encrypt certs for use by Dovecot and Postfix. Default is scripts
  • LE_CA_HOSTNAME — Which LE hostname should be used to request/reissue/revoke certificates. Default is acme-staging.api.letsencrypt.org (testing). For production, use acme-v01.api.letsencrypt.org instead.
  • LE_CA_TERMS — Location of LE’s license agreement. Default is https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf (but you probably won’t need to mess with this).
  • LE_ISSUERS — List of approved LE certificate issuers. Default is Let's Encrypt Authority X3 \ Let's Encrypt Authority X4 (you probably won’t need to change this either).

Example letsencrypt.conf:

# Account settings
#
ACCOUNT_RSA_KEY_SIZE = 4096
ACCOUNT_EMAIL_ADDRESS = webmaster@example.org

# Hiawatha settings
#
HIAWATHA_CONFIG_DIR = /etc/hiawatha
HIAWATHA_CERT_DIR = {HIAWATHA_CONFIG_DIR}/tls
HIAWATHA_RESTART_COMMAND = /etc/init.d/hiawatha restart

# Certificate settings
#
CERTIFICATE_RSA_KEY_SIZE = 2048

# Renewal settings
#
RENEWAL_EXPIRE_THRESHOLD = 30 # number of days
RENEWAL_REUSE_KEY = false
RENEWAL_SCRIPT_DIR = scripts

# Let's Encrypt API settings
#
LE_CA_HOSTNAME = acme-v01.api.letsencrypt.org          # Production
#LE_CA_HOSTNAME = acme-staging.api.letsencrypt.org       # Testing
LE_CA_TERMS = https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf
LE_ISSUERS = Let's Encrypt Authority X3 \
             Let's Encrypt Authority X4

Usage

Hiawatha’s letsencrypt script is simple to use. However, it should be noted that in order to handle all of the facets of automating Let’s Encrypt on your Hiawatha server, it should probably be run as root. It’ll need to write into a few particularly sensitive directories on your server, potentially restart services, etc. While it is possible to configure your system in such a way that running the script as root is not strictly necessary, such a scenario adds sufficient complexity without enough benefit for me to cover in this article.

Running the script with the ‘help’ argument will list each command and what it does:

Usage: ./letsencrypt 
Commands: register: Register your account key at the Let's Encrypt CA.
          request  []: Request new certificate for website.
          expire: show number of days left before certificate expires.
          renew [restart]: Renew the almost expired Let's Encrypt certificates
                           in Hiawatha's certificate directory.
          revoke : Revoke the certificate.

With this in mind, we’ll go through the process of registering a new LE account, requesting our first certificate for a particular domain, and verifying that everything is working as expected. I’ll also briefly cover the process of revocation.

  1. Registering an account — In order to utilize Let’s Encrypt, you’ll need to register an account. The script makes this a very simple process:
    /opt/letsencrypt/letsencrypt register

    This will create an account key, and register it and your account email address with LE.

    IMPORTANT: The ‘account.key’ file which is created during this process is a private key, and should be handled as such. Make certain the file is only readable by the user who owns it, e.g. root, and that it is never transmitted insecurely!

  2. Requesting a certificate — To request a certificate, you’ll also need a valid vhost in your hiawatha.conf file. To illustrate, I might have a vhost stanza in my Hiawatha configuration which contains something like this:
    VirtualHost {
        Hostname = example.org, www.example.org, *.example.org
        ...
    }

    To request a cert for that domain, I’d simply call the letsencrypt script like so, referring to the first Hostname in the VirtualHost stanza:

    /opt/letsencrypt/letsencrypt request example.org

    This would issue a certificate which is valid for both ‘example.org’ and ‘www.example.org’. Unfortunately, wildcards are not yet supported by Let’s Encrypt, so wildcard entries (e.g. *.example.org) in the Hostname clause will currently be ignored. Any subdomains which you’d also like included in the certificate as alternative names must be listed explicitly in the Hostname line for now.

    When the operation has completed, you will wind up with a complete certificate bundle that matches the hostname you specified. It can be found in the certificate directory you specified in the HIAWATHA_CERT_DIR variable of your letsencrypt.conf file earlier. With our ‘example.org’ domain and a default certificate directory of /etc/hiawatha/tls, we’d have a file called ‘/etc/hiawatha/tls/example.org.pem‘. If it is not already, this file needs to be referenced by the hiawatha.conf file. If the cert issued will be for the primary domain for your webserver (which answers by default on the HTTPS port if no domain is specified by the client), it should be placed in each Binding stanza of hiawatha.conf which will be providing TLS:

    Binding {
            Port = 443
            TLScertFile = /etc/hiawatha/tls/example.org.pem
            ...
    }

    If the cert was issued for a vhost that will be utilizing SNI, it’d be placed in the relevant VirtualHost stanza instead:

    VirtualHost {
        Hostname = example.org, www.example.org, *.example.org
        TLScertFile = /etc/hiawatha/tls/example.org.pem
        ...
    }

    After the cert is issued and you’ve verified that Hiawatha is configured appropriately, you can restart it to implement your changes:

    /etc/init.d/hiawatha restart
  3. Verifying your certificate — There are several ways to test your results, but I’ll quickly go over a few of the simpler methods. You can use curl from the command line:
    curl -v --head https://example.org/
  4. If successful, you should get a bunch of verbiage about TLS and, more interestingly, some information about the server certificate:

    * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
    * ALPN, server did not agree to a protocol
    * Server certificate:
    *  subject: CN=example.org
    *  start date: Sep 29 10:10:33 2017 GMT
    *  expire date: Dec 28 10:10:33 2017 GMT
    *  subjectAltName: host "example.org" matched cert's "example.org"
    *  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
    *  SSL certificate verify ok.

    If you don’t have access to curl or if using the CLI from your desktop just isn’t your style, there’s always Qualys SSL Labs, or Chrome’s built-in developer tools (Ctrl+Shift+I on Linux or Windows, Option+Command+I on OS X).

  • Checking certificate expiration dates — The script can also work out when each of your Let’s Encrypt certificates are due to expire and give you a report in days, like so:
    /opt/letsencrypt/letsencrypt expire

    The results will look something like the following:

    example.org                         89
    blog.example.org                    89
    mytestserver.net                    89
    wiki.mytestserver.net               89
    static.mytestserver.net             89
  • Revoking a certificate — If for some reason you need to revoke a Let’s Encrypt certificate, this process is equally trivial. Should I want to revoke my current cert for ‘example.org’ prior to expiration, it’d be as easy as:
    /opt/letsencrypt/letsencrypt revoke example.org

Automation

This is where Hiawatha’s ‘letsencrypt‘ really shines… making it handle your LE-issued certs automatically. If all of the above has been correctly configured and works properly, we’ll let the script take over renewals from now on.

  1. Create a cron job for your letsencrypt script. Since it doesn’t need very high granularity you can set this to run daily, which means you won’t need to mess with crontabs unless you really want to. The most straight-forward way to do this is by creating a new script file in /etc/cron.daily/ using the text editor of your choice:
    nvim /etc/cron.daily/letsencrypt

    A minimalist cron script for this purpose might contain no more than the following:

    #!/bin/sh
    /opt/letsencrypt/letsencrypt renew restart

    That’d tell the ‘letsencrypt‘ script to automatically check the expiration dates of each of your LE-issued certificates in its configured ‘HIAWATHA_CERT_DIR‘ path and, if they are due to expire in equal or less than the amount of days specified in ‘RENEWAL_EXPIRE_THRESHOLD‘, renew them (replacing the original certificate in the process). The ‘restart‘ command at the end specifies that if a certificate was replaced, the script should also restart the webserver to make the change go into effect immediately.

  2. When you’ve created your script, also make sure it’s executable (and owned by root):
    chmod 755 /etc/cron.daily/letsencrypt
    ls -lah /etc/cron.daily/letsencrypt
    -rwxr-xr-x 1 root root 53 Oct  5 10:27 /etc/cron.daily/letsencrypt

From this point forward, all of your Let’s Encrypt certificates should be automatically kept up to date for you. You can add as many LE certificates for as many domains as you wish; the script will automatically keep them all renewed within the threshold you’ve defined in letsencrypt.conf.

Leave a Reply

Your email address will not be published. Required fields are marked *