Creating SSL Certificates with Letsencrypt

Categories: Linux, Security, Infrastructure

Introduction

This article describes how to generate server SSL certificates for an Ubuntu server which are signed (for free) by the Letsencrypt Certificate Authority. Letsencrypt issues/renews a maximum of 5 certificates per week for a specific domain-name, so may not be appropriate for some large-scale certificate users, but otherwise places no restrictions on who may request certificate signatures, and what those certificates may be used for.

Other types of certificates (client certificate used for PGP signed email etc) are not discussed here.

It is assumed you know how to set up a website for a “virtual host”.

This article provides a fair amount of “background information” about certificates and certificate-authorities. If you are familiar with these, just spring ahead to “Letsencrypt on Ubuntu”. On the other hand, if you want a lot of detail about the process, see here for more information about keys and certificates and the letsencrypt website for more information about letsencrypt itself.

About TLS and Certificate Authorities

TLS is the dominant way to set up an encrypted channel from any arbitrary client application and a server. As well as providing encryption, the TLS protcol requires the server to provide a certificate which the client can verify to ensure that the connection they opened has not been redirected to an alternate hostile server. TLS is commonly used for things like connection to an email-server, and the HTTPS protocol is simply HTTP over a TLS-protected connection. The term SSL is (roughly) a synonym for TLS.

Optionally, a client application can provide a certificate to the server over TLS, thus providing the server with verifiable information about the client identity, ie bidirectional authentication. However this is not commonly used; instead TLS is typically used only for “single-sided” authentication of the server. Many services do not need to know the identity of the client, and when client identity is required then commonly the client provides normal (user,password) type credentials over the TLS encrypted channel to identify itself, rather than a client certificate.

To provide the server-side part of TLS, the server must generate an unsigned SSL certificate and then get some organisation to sign that certificate with their own key. And this organisation must be one whose own public certificate is bundled with the major software products such as operating-systems, web-browsers, java runtimes, etc. (or an organisation which has a signing-certificate issued by such an organisation). Such organisations are called Certificate Authorities (CAs).

There are about 80 organisations (CAs) world-wide whose public certificates are bundled with major software products in this way. A bundle of certificates is called a “trust store”; each software distributor who includes a “trust store” chooses which top-level certificates they regard as trustworthy, but in general they are very similar across all operating-systems, browsers, etc. Not every application includes its own trust-store; in fact very few do with most applications instead relying on the trust-store provided by some other component (the underlying OS, the underlying Java runtime, etc.). Some browsers rely on the OS-provided trust-store (eg Microsoft browsers) while other browsers do not trust the OS and explicitly provide their own trust-store (eg Firefox).

Until recently, the only CAs which offered to sign certificates for others were commercial and demanded a fee - including each time the certificate is renewed. A few CAs offer free signing under certain circumstances, but there is always a hook - they are for-profit entities after all.

Letsencrypt is a project of the non-profit Internet Security Research Group whose single goal is to sign certificates free-of-charge. They provide an API for doing so, and there are various client applications that integrate with this API to generate keys and sign certificates. The letsencrypt public certificates are distributed with most modern operating-systems and browsers, and are also signed by an organisation whose public certificate has been available for a long time, so they work with older products too.

Why is a Certificate Necessary?

A TLS key consists of a (private-key, public-key) pair; this can be generated fairly easily using appropriate tools on any computer (eg openssl). A certificate is simply a (public-key, name, signature) structure, representing the assertion that the specific public-key belongs to the specified name. Anybody can generate a certificate for any arbitrary (key, name) pair - but not with a signature that will be “trusted” by other applications.

In the case of a “server certificate” the name part is the DNS hostname of the server to which that certificate belongs.

After generating an initial unsigned or “self-signed” certificate, this needs to be signed by a CA, as described above, in order for it to be trusted by applications which use the standard trust-stores (sets of preinstalled trusted certificates).

The result is that when software really wants to communicate with a system with a specific name (eg www.example.com) then it can ask various sources (even untrustworthy ones) for “the certificate for www.example.com and by verifying the response it can tell if the data it was given is a “real certificate” (and thus the public-key is truly the correct one) or if the response is faked (the public-key is malicious). Knowing that the public-key is valid then allows data to be encrypted with that key in the certainty that only the owner of www.example.com can decode it (well, unless the corresponding private-key has been stolen, but that’s not a flaw in the system).

An alternative to certificates would be to have a world-wide master database of (public-key, name) pairs, and for client applications to consult this database for each connection to a new server. However that is simply not scalable, and would require client applications to always have internet connectivity; the certificate system is scalable and does not require a network connection to some central service.

See this article for more information on certificates.

Proving Identity to the CA

The core concept on which the whole certificate infrastructure is built is that a CA will only sign certificates for some name (eg www.example.com) when the certificate was supplied by the registered owner of that name. Obviously, if these checks aren’t done properly then an “attacker” can obtain a certificate for someone else’s name and their own public key, which would then give them the ability to impersonate that server. There are therefore very strict rules for CA organisations, and regular checks to make sure they are following the rules; an organisation that doesn’t check carefully enough (or deliberately issues certificates wrongly) gets removed from the trust-stores of the relevant software products which almost certainly means irrelevance, and for commercial CAs a quick bankruptcy. This has happened a handful of times.

Some certificate authorities verify name “ownership” with a face-to-face meeting, paper documentation, etc. Old-school, expensive, not easily scalable, but effective.

Letsencrypt instead supports several online ways of proving ownership of a domain.

Webroot Ownership

The simplest method of proving domain ownership is “webroot” - this requires the certificate applicant to have a website on the hostname being signed. When certificate-signing is requested, letsencrypt generates a file that the applicant must put on that website; only the owner of the website (and thus the hostname) should be able to do that. If letsencrypt’s own servers can later download that file from the website, then the certificate applicant is really the owner of the name and so the certificate is signed and returned.

If the server for which the certificate is being issued doesn’t normally run a webserver, then the letsencrypt-provided software can start a temporary webserver just for the purposes of verification. This can be useful for a mailserver which needs a certificate to encrypt IMAP/SMTP network traffic but otherwise has no website, and similar usecases.

DNS Ownership

Letsencrypt also supports proving domain ownership by publishing a DNS record; only the owner of a domain can do this. This does require communicating with your DNS provider, and there is no standard interface for this; the letsencrypt certbot program provides “plugins” for various popular DNS providers including Google, AWS (Route53), CloudFlare, and DigitalOcean.

I find the webroot approach slightly easier, so the DNS approach is not discussed further here; see the letsencrypt/certbot documentation for further information.

Letsencrypt on Ubuntu

Ubuntu 16.04-LTS and later provide the letsencrypt/certbot client application which makes issuing of certificates reasonably easy.

To install the letsencrypt client app, just run:

  • apt install letsencrypt

The letsencrypt package is just a synonym for “certbot”, the standard letsencrypt client app.

Assuming there is an existing website for the hostname you need a certificate for (eg www.example.com), just execute the following:

letsencrypt certonly 
  --email my.email@somewhere.com
  --agree-tos
  --webroot
  --webroot-path /var/www/html
  --domain www.example.com

The above command:

  • generates a public/private key pair and saves them under /etc/letsencrypt/live/{domain}
  • generates a certificate-signing-request (csr) with the specified domain-name and the public-key
  • writes a temporary file into dir {webroot-path} which contains some kind of signature of the csr
  • (letsencrypt servers then fetches the signature from your server and verifies it matches the csr, proving that someone in control of your server did indeed submit the csr)
  • writes the returned (signed) certificate into /etc/letsencrypt/live/{domain}/fullchain.pem
  • removes the temporary file from {webroot-path}
  • creates a permanent config file /etc/letsencrypt/renewal/{domain}.conf which contains the commandline args for the letsencrypt certonly command, so that they can be reused on renewal

The above can be repeated as many times as desired for different certificates (eg one per web “virtual host”, where each virtual-host has a matching DNS CNAME record).

Because letsencrypt certificates are only valid for 90 days, they must be regularly renewed. In directory /etc/cron.weekly, add the following file (with executable flag set):

#!/bin/sh
letsencrypt renew

This checks regularly if the cert needs to be renewed, and if so does it automatically.

You should also edit /etc/crontab and modify the date/time at which weekly tasks are run, as it is set to ‘06:47’ on Sunday (day=7) on all Ubuntu installations.

A single certificate covering multiple domains can be requested by repeating the “–webroot-path .. –domain …” options multiple times. Each domain must have a corresponding website.

The webserver can then be set up to point to the letsencrypt-managed certificates, eg for NGINX:

server {
	listen 80;
	listen [::]:80;

	server_name my.virtual.hostname;

	root /my/website/root; # eg /var/www/html

	location ^~ /.well-known/ {
		# This directory must be available under http for letsencrypt cert renewal
		try_files $uri $uri/ =404;
	}

	location / {
        	# force all other requests to fail (http only allowed for /.well-known/*)
		return 403;
	}
}


server {
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;
        ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
        ....
}

Other Options with Letsencrypt

The certbot client for letsencrypt supports a number of alternatives to the simple “certonly” and “webroot” approach shown above. If neither of these approaches suits you, see the man-page for certbot.

For certain webservers, the certbot application can not only generate the certificate, but also modify the webserver configuration automatically to enable SSL and point to the appropriate certificate. Personally, I find the idea of an application manipulating the config-files of my webserver not so appealing, and prefer the “–webroot” option rather than the server-specific option (though in the case of nginx, automatic config update isn’t yet supported anyway).

Clients other than certbot are listed here.

Web-based Clients

It appears that sslforfree offers a javascript client app for generating certificates and signing them via letsencrypt. However given that letsencrypt certificates are valid only for a few months (ie need to be renewed regularly), this doesn’t seem to be a very useful thing except for test-certificates. For other cases you’ll need some automated renewal process running on a server - and interactive sites such as this one can’t help there.

Other Free Certificate Issuers

CACert issues certificates for free. However their public certificate is not widely distributed, ie such certificates are fairly useless.

StartCom (aka StartSSL) previously issued base-level certificates for free, for “non-commercial” uses. However their trustworthiness as a company has been called into doubt (related to their new owner WoSign) and multiple software distributors no longer trust new certificates issued by StartCom.

TLS and Webserver Virtual Hosts

Just some useful information that is tangentially related to TLS certificates..

It is common to use CNAME records to point multiple hostnames to the same physical server. However having multiple distinct certificates for a single network port can cause problems, as SSL is initialised (including sending a certificate to the client) before the HTTP request that specifies the target virtual server name. This works ok for clients which support “TLS Server Name Indication” (SNI) - which is all modern browsers. Such browsers send the virtual hostname as part of the TLS handshake so the server can return the matching certificate. However older browsers which do not support this will instead get just the first cert, and may then warn about ‘cert not matching address’; the only solution that also supports such setups is to use a single cert with aliases for all desired subhosts, or to have the webserver listen on multiple ports.

References