Using certbot to add Let's Encrypt certificate on Heroku

July 26, 2020

Ever since Google announced in 2017 that they would be prioritizing sites with HTTPS for SEO, I knew I had to make sure this blog had the necessary setup. I initially procrastinated a bit because I thought I was going to have to do some research to find the latest and greatest in terms of trusted certificate authorities. Once I decided to put this in place my research wasn’t long at all.

Enter Let’s Encrypt, a

free, automated, and open certificate authority… provided by the ISRG.

Well that gives me peace of mind. Further investigation then pointed me to certbot, a

free, open source software tool for automatically using Let’s Encrypt certificates on manually-administrated websites to enable HTTPS.

Nice 😌, that’s perfect.

Let’s get started. Assuming you have Homebrew installed, then:

  1. Install certbot

    brew install certbot
    
  2. Generate the certificate for your website, fill in everything it asks you (I had to use sudo in front of this command even though it seems to Just Work™ for other people in the interwebz; not sure why but hey, it works 🤷🏻‍♂️):

    sudo certbot certonly --manual -d <www.your-domain.com>
    

    Note that I put the fully qualified domain name above. This does not issue wildcard certificates.

  3. Now, it’ll ask you to create a new file to verify you actually own the domain. Something like this:

    Create a file containing just this data:
    
    <some-characters>.<more-characters>
    
    And make it available on your web server at this URL:
    
    http://<www.your-domain.com>/.well-known/acme-challenge/<some-characters>
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Press Enter to Continue
    
  4. In another terminal tab, create the file and commit it:

    echo "<some-characters>.<more-characters>" > source/.well-known/acme-challenge/<some-characters>
    git add -A source/.well-known
    git commit -m 'Add SSL certificate'
    
  5. Make it available in your app:

    git remote add heroku git@heroku.com:<your-app-name>.git
    git push heroku master
    
  6. Back in the certbot prompt, press Enter. You should see something like this:

    IMPORTANT NOTES:
     - Congratulations! Your certificate and chain have been saved at:
       /etc/letsencrypt/live/<www.your-domain.com>/fullchain.pem
       Your key file has been saved at:
       /etc/letsencrypt/live/<www.your-domain.com>/privkey.pem
       Your cert will expire on <YYYY-MM-DD>. To obtain a new or tweaked
       version of this certificate in the future, simply run certbot
       again. To non-interactively renew *all* of your certificates, run
       "certbot renew"
     - If you like Certbot, please consider supporting our work by:
    
       Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
       Donating to EFF:                    https://eff.org/donate-le
    
  7. On to the Heroku part of the puzzle. Make sure you have the heroku CLI installed:

    brew tap heroku/brew
    brew install heroku
    # Or if you already have it, might as well upgrade it:
    brew upgrade heroku/brew/heroku
    
  8. List your certificates (you should have nothing here the first time):

    heroku certs:info -a <your-app-name>
    
  9. Add your new certificate:

    sudo heroku certs:add /etc/letsencrypt/live/<www.your-domain.com>/fullchain.pem /etc/letsencrypt/live/<www.your-domain.com>/privkey.pem -a <your-app-name>
    
  10. You’re good to go! You are now a certified ~~hacker~~ domain owner 🔒

Renewing your certificate

Something interesting happened to me here, probably because I manually generated the certificate via certbotcertbot renew didn’t work. Thankfully, I found an easy solution - just run through steps 2–7 from before and then:

  1. List your certificates (you should have something expiring soon here):

    heroku certs:info -a <your-app-name>
    # Fetching SSL certificate <certificate-name> info for ⬢ <your-app-name>... done
    # Certificate details:
    # Common Name(s): <www.your-domain.com>
    # Expires At:     2020-07-31 04:40 UTC
    # Issuer:         /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
    # Starts At:      2020-05-02 04:40 UTC
    # Subject:        /CN=<www.your-domain.com>
    # SSL certificate is verified by a root authority.
    
  2. Update your certificate (yep - it’ll be on the same path as the previous one but I swear it will be the new one!):

    sudo heroku certs:update /etc/letsencrypt/live/<www.your-domain.com>/fullchain.pem /etc/letsencrypt/live/<www.your-domain.com>/privkey.pem -a <your-app-name>
    # Resolving trust chain... done
    #  ▸    Potentially Destructive Action
    #  ▸    This command will change the certificate of endpoint diplodocus-55595 from ⬢ <your-app-name>.
    #  ▸    To proceed, type <your-app-name> or re-run this command with --confirm <your-app-name>
    
    # > (type in your-app-name)
    # Updating SSL certificate diplodocus-55595 for ⬢ <your-app-name>... done
    # Updated certificate details:
    # Common Name(s): <www.your-domain.com>
    # Expires At:     2020-10-24 19:51 UTC
    # Issuer:         /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
    # Starts At:      2020-07-26 19:51 UTC
    # Subject:        /CN=<www.your-domain.com>
    # SSL certificate is verified by a root authority.
    
  3. Done! You are, once again, certified 🏅