Free high-security SSL certificates through Let’s Encrypt

Ever tried installing an SSL certificate on your website? Sucks, doesn’t it? The whole process around procuring and installing SSL certificates is so archaic and cumbersome that it sends shudders through the body of anyone facing it.

After Edward Snowden let the world know that everyone is watching everything you do online, we started to realise that we should be able to use the Internet without every benign and every private bit of data being visible to others. The answer to this problem: encryption.

Encryption scrambles data between the provider (say, a website, server or application) and its end user, such that if the data is intercepted anywhere between the two, it can’t be read. If you own a website, the way you encrypt data sent to and from it is through an SSL certificate.

Late last year, several do-gooders came together and agreed that the status quo for producing and installing SSL certificates was terrible. So they set about changing it, and with the vision of allowing anyone to produce and install an SSL certificate with the greatest of ease and with zero cost, they created Let’s Encrypt: a non-profit certificate issuing authority.

Like some of the big names in the business which charge anywhere from $10 to many hundreds of dollars for an SSL certificate, Let’s Encrypt gives out functionally-identical certificates for free in the name of facilitating widespread encryption on the Internet. See, this is a big deal.

Just yesterday, Let’s Encrypt moved into a public beta, meaning that everything isn’t quite perfect yet, but they’re ready for the masses to start testing their service. So with that, I set about using it to encrypt two sites which I hadn’t yet gone as far as securing yet.

How to install SSL certificate on LEMP with Let’s Encrypt

I personally run a LEMP (Linux, nginx, MySQL, PHP) stack on Digital Ocean for the two sites that I added SSL certificates to, so this tutorial will be focused on that specific configuration, though the process in general is the same for other configs.

In general, the process goes like this:

  1. Clone Let’s Encrypt git repo.
  2. Run Let’s Encrypt which creates the certificates.
  3. Modify your server blocks to include SSL directives.
  4. Reload nginx.

Install Let’s Encrypt

Let’s Encrypt is installed by cloning their git repo. In general, these commands will perform that for you:


cd ~
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt

view raw

gistfile1.txt

hosted with ❤ by GitHub

Create SSL certificates with Let’s Encrypt

Let’s Encrypt is now installed and ready to start producing certificates. At this point, the automatic installer doesn’t stop nginx to allow it to bind to port 80, so this needs to be done manually:


sudo service nginx stop

view raw

gistfile1.txt

hosted with ❤ by GitHub

Now you can create the certificates. If you want the certificate to cover the www and non-www version of the domain name, you’ll need to specify both in this command. You can specify as many domains as you want to cover with a single certificate:


./letsencrypt-auto –server https://acme-v01.api.letsencrypt.org/directory auth -d dave.pe -d www.dave.pe

view raw

gistfile1.txt

hosted with ❤ by GitHub

This command will cause Let’s Encrypt to check your environment and install any necessary dependencies. Once all that’s done, it should tell you that it has created your new certificates:

create-lets-encrypt-ssl-certificates

Certificates are valid for 90 days to improve their security (compromised certificates aren’t then active for years).

If you haven’t needed to before, you’ll also need to create a set of DH parameters:


openssl dhparam -out /etc/nginx/dhparam.pem 2048

view raw

gistfile1.txt

hosted with ❤ by GitHub

Add SSL directives to nginx

Now you need to tell nginx to use your new certificates. These config files are located in /etc/nginx/sites-available/. My server block previously looked something like this:


server {
listen 80; ## listen for ipv4; this line is default and implied
listen [::]:80; ## listen for ipv6
server_name dave.pe www.dave.pe;
root /usr/share/nginx/dave.pe/html;
index index.php index.html index.htm;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.html
try_files $uri $uri/ /yourls-loader.php;
# PHP engine
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php5-fpm.sock; # Can be different
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
location /doc/ {
alias /usr/share/doc/;
autoindex on;
allow 127.0.0.1;
deny all;
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
try_files $uri =404;
# fastcgi_split_path_info ^(.+\.php)(/.+)$;
# # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
#
# # With php5-cgi alone:
# # With php5-fpm:
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

view raw

gistfile1.txt

hosted with ❤ by GitHub

My new server block added a whole lot of SSL-related directives. You can use Mozilla’s SSL config generator to help you with your own. Mine looked like this:


server {
listen 443 ssl;
listen [::]:443 ssl;
server_name dave.pe;
ssl_certificate /etc/letsencrypt/live/dave.pe/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dave.pe/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
# openssl dhparam -out dhparam.pem 2048
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security max-age=15768000;
ssl_stapling on;
ssl_stapling_verify on;
## verify chain of trust of OCSP response using Root CA and Intermediate certs
#ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
ssl_trusted_certificate /etc/letsencrypt/live/dave.pe/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=86400;
resolver_timeout 10;
root /usr/share/nginx/dave.pe/html;
index index.php index.html index.htm;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.html
try_files $uri $uri/ /yourls-loader.php;
# PHP engine
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php5-fpm.sock; # Can be different
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
location /doc/ {
alias /usr/share/doc/;
autoindex on;
allow 127.0.0.1;
deny all;
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
try_files $uri =404;
# fastcgi_split_path_info ^(.+\.php)(/.+)$;
# # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
#
# # With php5-cgi alone:
# # With php5-fpm:
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

view raw

gistfile1.txt

hosted with ❤ by GitHub

You can see that lines 2-5 in my original config were replaced with lines 2-28 in the new config.

I wanted to achieve a couple of additional things with my new setup – I wanted to force all traffic trying to use the www subdomain to be redirected to the bare domain and to force all HTTP traffic to HTTPS. So, I added a couple of server blocks before this new server block to redirect traffic accordingly:


# redirect http to https
server {
listen 80; ## listen for ipv4; this line is default and implied
listen [::]:80; ## listen for ipv6
server_name dave.pe www.dave.pe;
return 301 https://dave.pe$request_uri;
}
# redirect https://www to https://
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name www.dave.pe;
ssl_certificate /etc/letsencrypt/live/dave.pe/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dave.pe/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
# openssl dhparam -out dhparam.pem 2048
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security max-age=15768000;
ssl_stapling on;
ssl_stapling_verify on;
## verify chain of trust of OCSP response using Root CA and Intermediate certs
#ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
ssl_trusted_certificate /etc/letsencrypt/live/dave.pe/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=86400;
resolver_timeout 10;
return 302 https://dave.pe$request_uri;
}

view raw

gistfile1.txt

hosted with ❤ by GitHub

With that, everything was in place, ready for use. A quick reload of nginx was all I needed to start loading my sites over HTTPS:


service nginx -s reload

view raw

gistfile1.txt

hosted with ❤ by GitHub

Now, running an SSL test on my site, I was instantly scoring an A+, giving you an idea of just how robust these Let’s Encrypt certificates are.

ssl-test-report

By Dave

Dave is the proud father of Ellie and Jack. There's nothing that makes him happier than spending time with his incredible wife and their amazing children. He's a civil/mechanical engineer and he also builds and maintains WordPress websites.

Leave a Reply