Michael Trojanek (relativkreativ) — Bootstrapper and creator of things

This article was published on April 11th 2016 and received its last update on January 22nd 2019. It takes about 5 minutes to read.

How to remotely generate a Let's Encrypt certificate for your production server

Generating an SSL certificate for your application with Let's Encrypt is easy and straight forward, but it's dependencies will pollute your production machine. Let me show you a way to generate a certificate for your application from another machine and keep your production box clean.

I this article I will assume that you have a domain pointing to a webserver running a Rails application. To serve your application encrypted via SSL, you have to generate a SSL certificate together with a private key and configure your webserver to use it.

By using Let's Encrypt, getting a certificate for your server is really easy. It can even install the certificate automatically in certain environments but its automated process is not well suited for production environments:

  • It either needs its own webserver
    In order to validate your domain, Let's Encrypt expects to either run its own webserver on port 80 (which means you have to shut down your application temporarily) or to save a file in your webroot.
  • Or it unnecessarily taints your system
    It installs quite a lot of dependencies which are only needed for certificate generation, but you probably want to keep the number of installed packages low on your production machine.

A way to work around this is to use Let's Encrypt's manual plugin:

By using the manual plugin, we basically execute the same steps the webroot plugin does — except we are generating the file to be placed in our webroot on a completely different machine so we do not have to install any packages on our production server.

A lot of Rails developers use a Mac for development, so it would be great if we could generate the file and request the certificate on a Mac but unfortunately, Let's Encrypt's Mac support is highly experimental as I write this (you even have to use the --debug flag to make it do anything).

So instead of working on a solution which may be valid only for a short period of time, we will generate said file and request our certificate from a virtual linux box.

Prepare a virtual machine

Spinning up a virtual Linux server is quick and easy if you are using Vagrant, so if it is not already installed on your development machine, head over to its website and install it.

When you are done, create an empty directory (I'll create mine in my home directory) and initialize Vagrant:

cd
mkdir letsencrypt
cd letsencrypt
vagrant init

Now edit the Vagrantfile to look like this:

Vagrant.configure(2) do |config|
  config.vm.box = "relativkreativ/centos-7-minimal"
end

I like to use my own Vagrant boxes but it does not really matter which box you use as we will install Let's Encrypt and its dependencies anyway. This is possible on any Linux box.

Run vagrant up to provision and start the virtual machine.

On the Vagrant box

Login via SSH (using the command vagrant ssh) and install git which we will need to clone Let's Encrypt's Github repository:

sudo yum install git

Then clone the repository:

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

Now we can change into the cloned directory and make the letsencrypt-auto executable install all necessary dependencies:

cd ~/letsencrypt
./letsencrypt-auto --help

Let's Encrypt is now ready and we can run the following command to request a certificate which is valid for both myapp.com and www.myapp.com:

./letsencrypt-auto certonly --manual --manual-public-ip-logging-ok --email you@yourdomain.com --agree-tos --domain yourdomain.com --domain www.yourdomain.com --rsa-key-size 2048

Specifying all these flags on the command line makes the command truly non-interactive. Alternatively you can run just ./letsencrypt-auto certonly --manual and have Let's Encrypt ask for all needed information.

Either way, the command will generate a response similar to this:

...
Make sure your web server displays the following content at
http://yourdomain.com/.well-known/acme-challenge/6pMQbZoXRive8KcHVGjHeggd94jHwRKSFcNd4gCRAC0 before continuing:

6pMQbZoXRive8KcHVGjHeggd94jHwRKSFcNd4gCRAC0.sEiMVt_ACN56-OlTkCffOB3xt28qM3LdWZ6xTKf1bWI
...
Press ENTER to continue

Before we press Enter we have to make sure that our webserver actually displays the file.

On the production server

If your Rails application is served only over plain HTTP at the moment, it is easy to display the requested file. We just have to create the appropriate directories and file under our application's public directory.

Assuming that your application's codebase lies under /var/www/myapp, open a new terminal window, log into your production machine and use the following commands to create the file requested above:

mkdir -p /var/www/myapp/public/.well-known/acme-challenge
echo "6pMQbZoXRive8KcHVGjHeggd94jHwRKSFcNd4gCRAC0.sEiMVt_ACN56-OlTkCffOB3xt28qM3LdWZ6xTKf1bWI" > /var/www/myapp/public/.well-known/acme-challenge/6pMQbZoXRive8KcHVGjHeggd94jHwRKSFcNd4gCRAC0

You can verify that everything works as expected by requesting the URL with your browser. When you are sure that the webserver serves the file properly, press Enter in our first terminal window.

When everything works out, Let's Encrypt will create the directory /etc/letsencrypt/live/myapp.com on our virtual machine which will hold 4 files, with these two being important for us:

  • fullchain.pem (our certificate)
  • privkey.pem (our private key)

Let's Encrypt will require the same steps for www.myapp.com and will create this domain's certificate and private key in the directory /etc/letsencrypt/live/www.myapp.com respectively.

Pave the way for certificate renewals

Once you install your freshly generated certificate and proudly serve all requests to your application via SSL, you will notice that you can no longer use the routine described above to renew your certificates.

In order to do that, you have to allow requests to the well known directory via plain HTTP — this means something along these lines (if you are using nginx, the Apache counterpart will look similar):

server {
  ...
  server_name myapp.com;

  location /.well-known/acme-challenge/ {
    root /var/www/myapp/application/public;
  }

  location / {
    return 301 https://myapp.com$request_uri;
  }
}

...

The location block will make sure that requests going to /.well-known/acme-challenge/ will be allowed over plain HTTP while all other requests are redirected to their SSL-counterpart.

You will need to restart your webserver after you have installed your certificate.

Clean up

Once you are done, a simple vagrant destroy will completely remove the virtual machine we used to generate our certificate. Since you will want to renew your certificates every few months, it will probably pay to automate the task of certificate generation — it may get tedious depending on the number of applications you want to serve encrypted.

Get in the loop

Join my email list to get new articles delivered straight to your inbox and discounts on my products.

No spam — guaranteed.

You can unsubscribe at any time.

Got it, thanks a lot!

Please check your emails for the confirmation request I just sent you. Once you clicked the link therein, you will no longer see these signup forms.