Stop Paying for SSL Certificates!

Stop Paying for SSL Certificates! [Photo by Jason Dent on Unsplash]

6/4/2020 UPDATE: This post if about getting free SSL certificates for your websites. If you’re looking for the information on how to get a certificate for your domain (aka “wildcard certificate”), please refer to this post: “How to Get Free Wildcard Certificates

Having your website served via HTTPS (as opposed to HTTP) is the new normal in the 21st century and companies like namecheap.com and ssls.com (with the ultimate effort of Comodo) made the revolution several years ago by dropping the cost of a quality SSL certificate from hundreds of dollars (as offered by Verisign/Symantec) to just a few bucks. Nowadays there are extremely few reasons to pay ridiculous money to Symantec and you can get your website visitors protected with TLS for a very low price. At the moment ssls.com is offering a single domain certificate starting from $3.77/yr (when payed for 4 years in advance). The only question remaining is “Why pay for this at all?”

A few years ago, in 2015 folks from the Internet Security Research Group (ISRG) found the right answer to this question: there is no reason for this at all. They founded a non-profit certificate authority called “Let’s Encrypt” (https://letsencrypt.org/) offering SSL certificates for free in delivering them in a more flexible and secure way than most other SSL certificate vendors do (even SSLs which did an amazing job making this process as easy and friendly as possible).

And before we get to the technicalities and implementation details here is more great news: the “Let’s Encrypt” certificates are not only supported and recognized by virtually all modern browsers, but they are also more secure than standard certificates because they are only issued for 90 days, not 1-2 years like most traditional certificates are. And if you are anticipating the headache associated with reinstalling the certificates to all your sites every 90 days or so, here is the good news: a companion solution called CertBot (https://certbot.eff.org/) will do this for you automatically. So let’s get to it …

First, let’s get Certbot. Since it’s 2020, instead of installing certbot on your server, let’s use docker instead. The process of obtaining the certificate consists of three parts:

  1. Validating that you’re the actual owner of the webserver (or the whole domain)
  2. Obtaining the certificate and storing on your server
  3. Configuring your http proxy to use them

Certbot offers ton of various methods for validation, each implemented as a plugin. You can find the full list here: (https://certbot.eff.org/docs/using.html#getting-certificates-and-choosing-plugins). In this article I assume that you are already running a web server, for this reason you should probably choose the validation method called “webroot”. If you remember going through the process of proving that you’re the actual owner of the website in the case of google or ssls, the process typically includes placing a file with a werd looking name and weird looking contents somewhere on your server. “webroot” plugin does the same for you automatically. Here is how you run it:

# set the parameters
WROOT=/www
ROOT=${WROOT}/htdocs
EMAIL=test@example.com
SITE=test.example.com

# run the command
docker run -it --rm --name certbot -v ${WROOT}/certbot/etc:/etc/letsencrypt -v ${WROOT}/certbot/var:/var/lib/letsencrypt -v ${ROOT}:/www certbot/dns-webroot certonly --webroot --noninteractive --agree-tos --email ${EMAIL} -d ${SITE} -w /www

Here is how it works:

  1. We set bunch of environment variables just to make this process more convenient. You can always just enter the command replacing the variables with their values
    1. WROOT is the directory which stores everything related to your website. This is NOT the directory published to the internet, NOT the one you list as the value of “root” parameter in nginx.
    2. ROOT is the directory where the root of your website is stored, i.e. this is the directory you enter as the “root” parameter in nginx. NOTE: if you’re using nginx as the reverse proxy, i.e. serving your site via proxy_pass, then you need to point ROOT to whatever place that will be served as the root directory of your website. Certbot will try to place a file called /.well-known/acme-challenge/<random file name> in that folder in order to validate the site ownership.
    3. EMAIL is your email. Let’s Encrypt will use it to contact you when they need (no spam, just information about your certificates)
    4. SITE is the name of your website that you want to serve via SSL
  2. Once the environment variables are set, just run the command. It will talk to Let’s Encrypt’s servers, place the random file in place, wait for Let’s Encrypt to verify that the file is in place, get the certificate and store it in the folder called ${WROOT}/certbot/etc/live/${SITE}. You will find the README file as well as your private key (privkey.pem), the certificate (cert.pem), the chain (chain.pem) and the certificate concatenated with the chain (fullchain.pem).

IMPORTANT: if you’re getting the certificate for a new website, then you’re running into the chicken-egg problem. The problem is that the webroot method requires the HTTP (unsecure) website to be already in place in order to validate your site ownership. If you do not have it running yet, then you need to create one. Here is a sample nginx configuration:

server {
    listen 80;
    location /.well-known/ {
      root /www/htdocs;
    }
    server_name rtfms.com www.rtfms.com;
}

Now, once you have your certificates in place it’s time to configure your server. Here is the nginx configuration (typically you place it in file called like /etc/nginx/vhosts.d/${SITE}.conf. Note that we keep the “/.well-known/” part for the HTTP configuration so that the certificate can renew in the future.

# This will redirect your old non-secure HTTP URLs to the new secure
# HTTPS URLs. Note that the HTTP code used here is 301, use it for 
# your production configuration. If you're just testing the new 
# configuration you may find it beneficial to use 302 instead
server {
    listen 80;
    location /.well-known/ {
      root /www/htdocs;
    }
    location / {
      return 301 https://$host$request_uri;
    }
    server_name rtfms.com www.rtfms.com;
}

server {
    listen 443 ssl;
    ssl_prefer_server_ciphers On;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;

    ssl_certificate /www/certbot/etc/live/test.example.com/fullchain.pem;
    ssl_certificate_key /www/certbot/etc/live/test.example.com/privkey.pem;

    server_name <enter your site name here, i.e. test.example.com>;
}

In this config you will need to verify that the “ssl_certificate” and “ssl_certificate_key” are pointing to the right places where you certificate files are stored. I’m using test.example.com as an example of the website name, but your, obviously, will be different.

And that’s pretty much it. Now verify your nginx configuration with “nginx -t”, make sure that you didn’t miss a semicolumn and that nginx can find the certificate and the key and once you fix all typos you can restart nginx (or just run somthing like “killall -HUP nginx”) and check your website, it should be fully operational. Note that the browser should show the “lock” icon next to your site URL. If it doesn’t then check the console for the error messages. One common error is when you serve you site via HTTPS, but still have some resources (images, scripts etc) loaded via HTTP. For instance in WordPress you will need to go to the admin console, Settings->General and fix the WordPress and Site addresses. There could some other issues specific to your site, but once you have all URLs filed you will see that all modern browsers recognize that your website’s users are now properly protected with SSL.

Once last thing is the certificate renewal. As I mentioned before, Let’s Encrypt’s certificates are only good for 90 days. The good news is that rerunning the same command listed above for obtaining the initial certificate will renew it if you run it some time after 60 days since the initial issue. So just add this command to your crontab and set it to run every two months or so.

Leave a Reply