How to setup SSL certificate and SPDY on ubuntu nginx using «Let’s Encrypt»

How to setup SSL certificate and SPDY on ubuntu nginx using «Let’s Encrypt»

You might say «Why do we need to do that at all?» Good question!

  1. All passwords and credit cards data entered by users on your websites can be exposed to a third party so it would be wise to encrypt your connection using ssl certificate.
  2. Nowadays it is good practice to use SPDY (speedy) protocol which is an extension to HTTPS protocol and removes the limit of 6 concurrent connection from one browser that persists in HTTP & HTTPS. With that limit removed all static data like images, scripts and styles is loaded simultaneously (much faster).

Thanks cap!

You are welcome! Now let’s move on to what the hell «Let’s encrypt» is.

Let’s Encrypt is a free, automated, and open certificate authority brought to you by the Internet Security Research Group (ISRG).

Internet Security Research Group (ISRG) is a California public benefit corporation. ISRG’s mission is to reduce financial, technological, and education barriers to secure communication over the Internet.

It is sponsored by renowned companies, so you can trust it:

letsencrypt-sponsors

In other words, you get for free ssl certificate that costed hundreds of dollars earlier. Even better you can set everything up to renew certificate automatically!

Prerequisites

In this tutorial we assume that

  • you have your nginx server installed and set up to serve your sites.
  • you have set up some virtual host example.com, that has alias www.example.com.
  • all given nginx configs redirect www.example.com traffic to example.com

Disclaimer: this tutorial is based upon this one, but a little bit improved. Unlike it’s original it tells how to get certificate without stopping nginx and renewal script grabs all the needed data from nginx configs so it is easier to automate!

Ok, let’s encrypt!

Step 1 — Install Let’s Encrypt Client

The first step to using Let’s Encrypt to obtain an SSL certificate is to install the letsencrypt software on your server. Currently, the best way to install Let’s Encrypt is to simply clone it from the official GitHub repository. In the future, it will likely be available via a package manager.

Install Git and bc

Let’s install Git and bc now, so we can clone the Let’s Encrypt repository.

Update your server’s package manager and then install the git and bc packages with apt-get:

sudo apt-get update
sudo apt-get -y install git bc

Clone Let’s Encrypt

We can now clone the Let’s Encrypt repository in /opt with this command:

sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt

Step 2 — Obtain a Certificate

Let’s Encrypt provides a variety of ways to obtain SSL certificates, through various plugins. Unlike the Apache plugin, most of the plugins will only help you with obtaining a certificate which you must manually configure your web server to use. Plugins that only obtain certificates, and don’t install them, are referred to as “authenticators” because they are used to authenticate whether a server should be issued a certificate.

The original tutorial I was using myself proposes to use standalone plugin initially. That is pretty straightforward:

service nginx stop
/opt/letsencrypt/letsencrypt-auto certonly --standalone
service nginx start

Just follow instructions and you are good to go!

Instead you can use the webroot plugin to obtain an SSL certificate. And we will show you how.

How To Use the Webroot Plugin

The webroot plugin works by placing a special file in the /.well-known directory within your document root, which can be opened (through your web server) by the Let’s Encrypt service for validation. Depending on your configuration, you may need to explicitly allow access to the /.well-known directory. To ensure that the directory is accessible to Let’s Encrypt for validation, let’s make a quick change to our Nginx configuration. Open it for editing:

sudo nano /etc/nginx/sites-enabled/example.com

Add this location block to your active server block:

location ~ /.well-known {
        allow all;
}

Save and exit (ctrl+o, ctrl+x)

Restart nginx:

service nginx restart

Now everything is ready to get our brand new free ssl certificate!

To get that you’ll need to specify your email address to register and then get notifications from let’s encrypt. As well you’ll need the webroot – root site path.

Assuming that:

  • user@example.com – your email address
  • /var/www/example.com/html/ – the root path of example.com

… your magic spell to get ssl certificate will be:

/opt/letsencrypt/letsencrypt-auto certonly \
--non-interactive --text \
--agree-tos --email user@example.com \
--webroot --webroot-path /var/www/example.com/html \
-d example.com -d www.example.com

If everything was successful, you should see an output message that looks something like this:

IMPORTANT NOTES:
 - If you lose your account credentials, you can recover through
   e-mails sent to sammy@digitalocean.com
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/example.com/fullchain.pem. Your
   cert will expire on 2016-03-15. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.
 - Your account credentials have been saved in your Let's Encrypt
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Let's
   Encrypt so making regular backups of this folder is ideal.
 - If like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Heads up: Note that this message contains path where your certificates are stored and certificate expiration date.

If the operation was NOT successful, read error messages carefully and fix problems. Here are the ones I’ve encountered:

  • InsecurePlatformWarning – solution is here https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning
  • Failed authorization procedure. example.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://example.com/.well-known/acme-challenge/ksYH5LF9O28de_1–WIRYPQnRFOs67_YQQ1da__Yoys. Solution: check if «location» config block mentioned above is working right, check permissions on .well-known directory, I suppose your current ssh user and nginx sever both should have writable access to it.

Certificate Files

After obtaining the cert, you will have the following PEM-encoded files:

  • cert.pem: Your domain’s certificate
  • chain.pem: The Let’s Encrypt chain certificate
  • fullchain.pem: cert.pem and chain.pem combined
  • privkey.pem: Your certificate’s private key

It’s important that you are aware of the location of the certificate files that were just created, so you can use them in your web server configuration. The files themselves are placed in a subdirectory in /etc/letsencrypt/archive.

However, Let’s Encrypt creates symbolic links to the most recent certificate files in the /etc/letsencrypt/live/your_domain_name directory. Because the links will always point to the most recent certificate files, this is the path that you should use to refer to your certificate files.

Step 3 — Configure TLS/SSL on Web Server (Nginx)

Now that you have an SSL certificate, you need to configure your web server to use it.

We’ll demonstrate how to configure the Nginx web server to use the certificate.

Now you must edit the Nginx configuration that contains your server block. Since you are trying to setup let’s encrypt with nginx you are geek enough to know where to find your virtual host nginx config. So let’s assume it is located in /etc/nginx/sites-available/example.com. We’ll use nano to edit it:

sudo nano /etc/nginx/sites-available/example.com

Find the server block and comment out or delete the lines that configure this server block to listen on port 80. Something like these:

listen 80;
listen [::]:80 ipv6only=on;

We are going to configure this server block to listen on port 443 with SSL and SPDY enabled instead.

Within your server block, add the following lines:

listen 443 ssl spdy;
 
server_name example.com www.example.com;
 
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

This enables your server to use SSL and SPDY, and tells it to use the Let’s Encrypt SSL certificate that we obtained earlier.

Yeah, yeah, that’s right, all you need to get SPDY support is to add this keyword in config… and install SSL certificate :)

To allow only the most secure SSL protocols and ciphers, add the following lines to the same server block:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';

Lastly, outside of the original server block (that is listening on HTTPS, port 443), add this server block to redirect HTTP (port 80) to HTTPS. Be sure to replace the highlighted part with your own domain name:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

Save and exit (ctrl+o, ctrl+x).

Now put the changes into effect by restarting Nginx:

sudo service nginx restart

The Let’s Encrypt TLS/SSL certificate is now in place.

My nginx site config looks this way now:

server {
	listen 80;
	server_name example.com www.example.com;
	return 301 https://example.com$request_uri;
}
server {
	listen 443 ssl spdy;
	listen [::]:443 ipv6only=on;
 
	ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
 
	ssl_session_cache shared:SSL:10m;
	ssl_session_timeout  5m;
 
	ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
	ssl_prefer_server_ciphers   on;
 
	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 
	if ($host = "www.example.com") {
		return 301 https://example.com$request_uri;
	}
 
	server_name example.com www.example.com;
	root /var/www/example.com/html;
 
	location ~ /.well-known {
                allow all;
	}
 
	index index.php;
 
	# Additional rules go here.
	include global/wordpress.conf;
}

At this point, you should test that the TLS/SSL certificate works by visiting your domain via HTTPS in a web browser.

Step 4 — Set Up Auto Renewal

Let’s Encrypt certificates are valid for 90 days, but it’s recommended that you renew the certificates every 60 days to allow a margin of error. At the time of this writing, automatic renewal is still not available as a feature of the client itself, but you can manually renew your certificates by running the Let’s Encrypt client again.

A practical way to ensure your certificates won’t get outdated is to create a cron job that will automatically handle the renewal process for you.

To renew certificates you need to cast the following spell:

/opt/letsencrypt/letsencrypt-auto certonly \
--agree-tos --renew-by-default \
-a webroot —-webroot-path=/var/www/example.com/html \
-d example.com -d www.example.com

Once that succeeds, you will need to reload your Nginx service to use the renewed certificate:

sudo service nginx reload

Now that we know the commands that we need to renew our certificate, we can automate this process using scripts and a cron job.

Renewal script

In the original tutorial I was using myself the author proposed a script that checks if certificate is getting too old and updates it if necessary. The original script took site root path and domain name as it’s arguments. I modified that script to take the name of nginx site config file and extract all the required information from it.

Here’s my script:

#!/bin/bash
 
le_path='/opt/letsencrypt'
exp_limit=30;
 
nginx_site_config="/etc/nginx/sites-available/$1"
 
if [ ! -f $nginx_site_config ]; then
        echo "[ERROR] config file does not exist: $nginx_site_config"
        exit 1;
fi
 
domain=`grep -m 1 "^s*server_name" $nginx_site_config | sed 's/s+server_names+//' | sed 's/(s+.*;|;)s*$//'`
site_root=`grep -m 1 "^s*root" $nginx_site_config | sed "s/^s+roots+//" | sed 's/(s+.*;|;)s*$//'`
cert_file="/etc/letsencrypt/live/$domain/fullchain.pem"
 
echo "domain: [$domain]"
echo "site_root: $site_root"
echo "cert_file: $cert_file"
 
if [ ! -f $cert_file ]; then
	echo "[ERROR] certificate file not found for domain $domain."
fi
 
exp=$(date -d "`openssl x509 -in $cert_file -text -noout|grep "Not After"|cut -c 25-`" +%s)
datenow=$(date -d "now" +%s)
days_exp=$(echo ( $exp - $datenow ) / 86400 |bc)
 
echo "Checking expiration date for $domain..."
 
if [ "$days_exp" -gt "$exp_limit" ] ; then
	echo "The certificate is up to date, no need for renewal ($days_exp days left)."
	exit 0;
else
	echo "The certificate for $domain is about to expire soon. Starting webroot renewal script..."
	/opt/letsencrypt/letsencrypt-auto certonly -a webroot --agree-tos --renew-by-default --webroot-path=$site_root -d $domain
	echo "Reloading nginx"
	service nginx reload
	echo "Renewal process finished for domain $domain"
	exit 0;
fi

Go ahead, grab it, store it to /usr/local/bin/letsencrypt-renew and set execution attribute:

sudo chmod +x /usr/local/bin/letsencrypt-renew

Assuming that your site configuration is stored in /etc/nginx/sites-enabled/example.com.conf you can call:

sudo letsencrypt-renew example.com.conf

output:

Checking expiration date for example.com...
The certificate is up to date, no need for renewal (89 days left).

Next, we will edit the crontab to create a new job that will run this command every week. To edit the crontab for the root user, run:

sudo crontab -e

Include the following content, all in one line:

30 2 * * 1 letsencrypt-renew example.com.conf >> /var/log/le-renewal.log

Save and exit. This will create a new cron job that will execute the letsencrypt-renew command every Monday at 2:30 am. The output produced by the command will be piped to a log file located at /var/log/le-renewal.log.

Conclusion

That’s it! Your web server is now using a free Let’s Encrypt TLS/SSL certificate to securely serve HTTPS content. Even more, now your server is SPDY powered!

{{"Show older comments..."|nls}}{{comments.length}} {{"of"|nls}} {{total}} {{"shown" | nls}}

{{'Comment'|nls}}:

{{(dialog.replies.length > 1?'Replies':'Reply')|nls}}: