Free SSL with Lets Encrypt on Serverpilot with multiple domains

Posted by & filed under Dev Blog.

SSL used to be expensive and complicated. Let’s Encrypt is a free service from a non-profit group supported by EFF, Cisco, Mozilla and others. They issue free SSL certs, which are now accepted by almost all modern and legacy browsers.

Serverpilot is a popular service for managing VPSs. Most Serverpilot services are free, but wouldn’t you know, enabling SSL is one that requires that extra $10 per month.

If you’re comfortable with just a little commandline, it’s possible to set up Let’s Encrypt on a Serverpilot-managed VPS, and use it to secure multiple domains.

Note: I recommend experimenting on a test server first. Luckily, if you use Serverpilot, you also use Digital Ocean, in which case it’s trivial to spin up a temporary VPS, create a few test installs with domains like test1.yourdomain.com (add those A records with your domain registrar), and wipe it all when finished, at a cost of a few pennies.

Install Letsencrypt

Ssh into your server as the serverpilot user, with something like:

ssh serverpilot@your-ip-or-hostname

Now switch to root, because you’ll need root permissions for most of this, and the serverpilot user isn’t allowed to sudo:

su

Get the letsencrypt package and put it in a sensible place:

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

Before going any further, let’s just check everything’s okay – and at the same time, install any missing packages:

/opt/letsencrypt/letsencrypt-auto --help

Now you may recall that Serverpilot installs both Nginx and Apache webservers. Apache is there mostly as a convenience for familiar editing of .htaccess. However Nginx is the one facing the world, and that includes handling https requests. So although Letsencrypt has a handy setup for Apache, it won’t work for us.

We’re left with several options – the Standalone option installs yet another webserver, which has potential for confusion and requires stopping your existing webserver each time the cert is renewed. Instead I’ve gone for the Webroot option, which creates files in the root of the website folder.

Get Certificate

Enter this line, replacing YOU@YOUREMAIL.com, YOURAPP and  YOURSITE.com with the relevant text. Your email should be the technical contact for the website. YOURAPP means the folder where your website sits – when you first ssh in, it’s at ~/apps/YOURAPP. And YOURSITE.com is simply your domain name. They’re likely similar but not identical.

/opt/letsencrypt/letsencrypt-auto certonly --agree-tos --email YOU@YOUREMAIL.com --webroot -w /srv/users/serverpilot/apps/YOURAPP/public -d YOURSITE.com -d www.YOURSITE.com

If all goes well you should receive the message ” – Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/YOURAPP/fullchain.pem.”

Configure Nginx

Now we need to tell Nginx where to find the certificate. And we need to do this in a way that won’t be overwritten by Serverpilot’s automated updates. Let’s go to the folder where the main existing Nginx configurations are stored:

cd /etc/nginx-sp/vhosts.d

If you ls in this folder, you’ll see a file called YOURAPP.conf. We could edit this file, but in the future Serverpilot could overwrite it with an updated configuration. So instead, create a new file:

nano YOURAPP.ssl.conf

… and paste in the code below, replacing YOURAPP and YOURSITE.com as required.

server {
 listen 443 ssl http2;
 listen [::]:443 ssl http2;
 server_name YOURDOMAIN.com www.YOURDOMAIN.com;
 ssl on;
# letsencrypt certificates
 ssl_certificate /etc/letsencrypt/live/YOURDOMAIN.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/YOURDOMAIN.com/privkey.pem;
#SSL Optimization
 ssl_session_timeout 1d;
 ssl_session_cache shared:SSL:20m;
 ssl_session_tickets off;
# modern configuration
 ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 ssl_prefer_server_ciphers on;
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';
# OCSP stapling 
 ssl_stapling on; 
 ssl_stapling_verify on;
# verify chain of trust of OCSP response 
 ssl_trusted_certificate /etc/letsencrypt/live/YOURDOMAIN.com/chain.pem; #root directory and logfiles root /srv/users/serverpilot/apps/YOURAPP/public;
access_log /srv/users/serverpilot/log/YOURAPP/YOURAPP_nginx.access.log main; 
 error_log /srv/users/serverpilot/log/YOURAPP/YOURAPP_nginx.error.log;
#proxyset 
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Forwarded-SSL on; 
 proxy_set_header X-Forwarded-Proto $scheme;
#includes 
 include /etc/nginx-sp/vhosts.d/YOURAPP.d/*.nonssl_conf; 
 include /etc/nginx-sp/vhosts.d/YOURAPP.d/*.conf; 
}

In order for this addition to take effect, you’ll need to restart Nginx:

service nginx-sp restart

Careful here. If there’s any problem with the config file, Nginx will fail to restart, and all websites on your server will be unavailable. Quickly rename the .conf file to .con, then restart Nginx again while you figure out the problem.

Now try it out! Check https://yoursite.com in your browser. If all is well, your site should simply load as normal, but with the magic green lock symbol in the address bar. For a more detailed checkup, try ssllabs.com/ssltest.

If the http part is not green, and/or your site loads with some issues, missing images etc, it means that some parts of your page are loading without SSL. If you’re using WordPress, see below. For a custom-built site, you might need to manually edit URLs to make sure they all begin with “https”.

Use SSL with WordPress

Now that SSL is working on your website, you’ll probably want to enforce its use. For example, some pages may try to load a mixture of secure and insecure content, which can cause errors for the viewer. And for SEO reasons, you ideally don’t want http: and https: versions of the same page. If you use WordPress, there’s a simple, free and highly rated plugin that takes care of this with no fuss: Really Simple SSL.

Rinse and repeat

For further domains on the same server, repeat the steps from Get Certificate down to here.

Cert Renewals

You may have noticed that your cert is only valid for 90 days. That’s not very useful. Fortunately Letsencrypt expects you to renew your certificates automatically. Older tutorials included rather complex scripts to perform renewal, but the current version of Letsencrypt (0.4.2) now includes a renew command. A one-line shell script in your cron folder checks all Letsencrypt certs on your server, and attempts to renew any that are due to expire in under 30 days.

nano /etc/cron.daily/letsencrypt

Paste this file with the same email address you used earlier:

#!/bin/sh
/opt/letsencrypt/certbot-auto renew --agree-tos --email you@youremail.com

Exit, save, and set permissions:

chmod a+x /etc/cron.daily/letsencrypt

That’s it. You can check up on renewals in the log file, e.g.

tail /var/log/letsencrypt/letsencrypt.log

Why didn’t my Letsencrypt Cert renew automatically?

One possible reason is that previously, the renewal command was letsencrypt-auto – now it’s certbot-auto.

Finally

Is this the perfect setup? Honestly, no, probably not.

  • We’ve created a hidden folder in the website root, owned by root. With an application like WordPress that’s a non-issue, but it’s slightly inelegant.
  • Letsencrypt uses something called SNI, which doesn’t require a separate IP address for every domain, but isn’t supported by Windows XP sp2 or earlier, or Android 2.3.6 or earlier. They’ll get a warning when they visit your site, but that’s something they’ll have to get used to, because SNI is getting pretty popular.
  • Letsencrypt needs to run as root, including in that cron job. (Or you can create a sudo user, but basically same issue.) It’s a security risk, albeit a fairly theoretical one. Sysadmins will be appalled at the idea. In fact one of the Letsencrypt contributors has privately created a script to allow it to work without root access: Let’s Encrypt without sudo.
  • SSL uses extra CPU power. There are optimisations that can be done. In the past I spent way too much time trying to optimise PHP-FPM and MySQL, until Serverpilot came along and made it unnecessary. For heavy users, I suspect Serverpilot’s paid plan is well worth the money.