We’re pretty big fans of Laravel products. We particularly enjoy Laravel Valet and Laravel Forge.

This blog post is going to talk about Laravel Forge and LetsEncrypt. In particular, we’ll be talking about Forge’s “LetsEncrypt (Beta)” button, and how to transition to a place with more control of your free (as in beer) SSL certificates.

Knowledge of SSH and your server’s root password is preferable, though much of this can be achieved in the Forge web interface using recipes.


The Basics

Laravel Forge

This is a great way to spin up and manage websites in an interface. You can point and click your way adding providers like DigitalOcean or AWS, provisioning new servers, adding/removing sites, scheduling cron jobs, modifying config files, restarting services, etc.

LetsEncrypt

Remember the days when you had to pay money for SSL certificates? And hunt down clients’ WHOIS information, and hope your provider doesn’t get blacklisted by a major browser? Good news, things are a lot better now. You can get free SSL certs from the good people supporting LetsEncrypt, and in recent months the tools to do so have become dead simple.


 

How does Forge do it?

Once you’ve created a site, there’s an SSL link with buttons.

You can click the button, follow the instructions, and presto: your site now has an SSL cert. This is amazing. But let’s remember this is a Beta feature created in the early days of LetsEncrypt when implementation was less clear.

My site now has SSL, so what’s the problem?
There may be no problem, unless you decide you’d like to spin up dozens of sites in that manner.

For instance, let’s take a hypothetical. You land a handful of sites and suddenly your Forge dashboard looks like this:

If you keep creating sites like this, you’ll eventually find that you’ve reached the limit by LetsEncrypt, getting an error similar this this:

  
+ Requesting certificate...
  + ERROR: An error occurred while sending post-request to https://acme-v01.api.letsencrypt.org/acme/new-cert (Status 429)
Details: { "type": "urn:acme:error:rateLimited", "detail": "Error creating new cert :: too many certificates already issued for: untold.example", "status": 429 }

The key to note here is the rateLimited error type. For more information on the limits in LetsEncrypt, check out their page on the subject. But the main takeaway here is that, at the time of this writing, it’s 20 certs per week.

But wait, I haven’t added that many sites this week, what’s happening?
Forge’s Beta feature is essentially re-registering each site once a week using a cron job and a repository called dehydrated.

This approach is mentioned in (the amazing) Taylor Otwell’s news story on LetsEncrypt Beta improvements. A nice tool for seeing this is this Certificate Search site. It will look like this:

In the above screenshot you can click on the links and see there’s a new cert every seven days. This is how I discovered they’re not being renewed but instead re-registered weekly.

(If you want to dig further into the WHY, follow the trail on the server’s cron jobs in the /etc/cron.d/ directory.)

Transitioning to certbot

There has been a ton of progress to make LetsEncrypt easy. Forge’s Beta option was a good approach early on, but we can now leverage the latest tools available. The primary change we’re going to make is using renewals instead of registering. This will help us avoid the rate limiting that you may run into with Forge’s LetsEncrypt Beta.

Certbot is the command-line utility by the (amazing) people at the EFF. Using the link above, they’ve provided instructions based on your OS and webserver.

Most of our servers are using Ubuntu 16.04 and Nginx. If you’re unsure on the linux flavor/version, remember the ol’ trusty: cat /etc/*-release

Note: if you’re searching the web on this topic, you’ll also see certbot-auto and letsencrypt which are essentially synonymous. letsencrypt was the old name of the CLI util.

Before transitioning from Forge’s Beta approach to certbot, I recommend doing the following steps.

  1. Simple audit of the sites and SSL expiration dates
  2. Backup and remove existing cron jobs created by the Beta approach
  3. Create certs using certbot
  4. Modify our server config to point to the new certs
  5. Create a cron job to renew certs with a --post-hook option


1. Simple audit

I’ve created a public Forge Recipe here that will output a table with the site and SSL expiration date.

When you run this Forge Recipe, it will look in the usual places in Nginx and email a table like this: SITE | EXPIRES ----------------------------------------------------------------------------- seinfeld-dev.untold.example | Jan 6 15:32:15 2018 GMT seinfeld-staging.untold.example | Jan 6 15:29:10 2018 GMT seinfeld-qa.untold.example | Jan 6 15:43:15 2018 GMT ...

2. Backup cron jobs

There are a few ways to set up cron tasks. When you click Forge’s LetsEncrypt Beta button it sets up a cron job that exists as a file in /etc/cron.d/letsencrypt-renew-SOME_ID

Let’s back these up with something like:

  
mkdir -p ~/backup/letsencrypt-beta-cron
mv /etc/cron.d/letsencrypt-renew* ~/backup/letsencrypt-beta-cron/
  

3. Create cert(s) with certbot!

In the case of dev/staging/qa sites, you might consider combining domains into a single cert. We’re going to tell certbot, “hey, don’t automatically change our Nginx settings, just create the cert and we’ll do that.”

sudo certbot -d seinfeld-dev.untold.example -d seinfeld-staging.untold.example -d seinfeld-qa.untold.example --nginx certonly

This will do some magic and output the location of the certificate and key files.

4. Point config to new cert/key

For this step, you can use the Forge interface if that’s more comfortable. Edit the Nginx (or other webserver) settings from the bottom of the site:

Change the lines pointing to the cert and key files to the locations of the new files created by certbot:

When you click Save in the Forge interface, it’s going to reload the Nginx settings behind the scenes and take effect.

5. Create cron job for renewal

Now we’re going to set up a periodic renewal. We do this with a cron job. This can be done on in the Forge interface by visiting the Scheduler tab for the server.

In the forums I see a certbot engineer recommending to check for renewals twice daily.

Here’s an example of what that would look like in Forge to add this cron entry: cerbot renew --post-hook "service nginx reload"

Above you can see the post-hook flag. Most of the time, certbot will try to renew certificates and basically return, “you don’t have to renew yet.” When it is time to renew, it will, and the post hook fires off, reloading nginx.

If you’d like to see a summary of the domains and certificates associated with certbot, you may use certbot certificates.

And that’s it! Now you can easily transition your Forge sites away from the older (Beta) method and into using certbot. Once you’ve set your cron job, you can rest easy that your SSL certificates will renew automatically and cost zero dollars. What a time to be alive.