Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use a LetsEncrypt SSL cert in my Heroku Node Express app?

I have a Node Express app running on Heroku that I want to encrypt with a free-of-charge SSL cert from LetsEncrypt. However, the methods I've seen require opening up ports 443 and 80 to allow the ACME process to work.

Heroku only gives you one port, and doesn't let you choose which port. So how can I use LetsEncrypt?

I spent a bunch of time figuring this out yesterday. First time in a long time there were no answers on StackOverflow for something I was trying to do!

like image 257
stone Avatar asked Oct 23 '16 04:10

stone


2 Answers

Update:

Heroku now supports LetsEncrypt natively! So this workaround is no longer needed.

Instructions here:

https://devcenter.heroku.com/articles/automated-certificate-management

For new apps, you don't have to do anything, it's turned on by default. For apps created before March 21 2017, you can turn it on with this Heroku cli command: heroku certs:auto:enable

Thanks @Spain Train


Background

Ideally, LetsEncrypt allows for an automated certificate renewal process. That's harder to do on Heroku, so this answer describes how to use a manual process. Using a Heroku environment var, you'll be able to update your certs manually fairly easily going forward - no code changes.

Credit for this answer goes largely to two nice blog posts: https://medium.com/@franxyzxyz/setting-up-free-https-with-heroku-ssl-and-lets-encrypt-80cf6eac108e#.67pjxutaw
and
https://medium.com/should-designers-code/how-to-set-up-ssl-with-lets-encrypt-on-heroku-for-free-266c185630db#.ldr9wrg2j

There's a GitHub project which apparently supports automated certs updates on Heroku. I'll update this answer when I've tried it out:
https://github.com/dmathieu/sabayon

Using LetsEncrypt on Heroku with a Node Express app

Get the Express server ready:

Add this middleware to your Express app. Be sure to add it BEFORE any middleware that redirects http to https, because this endpoint must be http.

// Read the Certbot response from an environment variable; we'll set this later:

const letsEncryptReponse = process.env.CERTBOT_RESPONSE;

// Return the Let's Encrypt certbot response:
app.get('/.well-known/acme-challenge/:content', function(req, res) {
  res.send(letsEncryptReponse);
});

Create the certificate files using certbot:

  1. Start certbot: sudo certbot certonly --manual
    Enter the site url when prompted (www.example.com)
    certbot will display a Challenge Response string in the format
    xxxxxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyyyy
    LEAVE CERTBOT WAITING IN THIS STATE. Do not press enter yet or exit.
  2. Go to the Heroku dashboard and view app settings:
    https://dashboard.heroku.com/apps/your-heroku-app-name/settings
    Under Config Variables, click 'Reveal Config Vars'
    Edit the CERTBOT_RESPONSE var's value to match the Challenge Response from step a.
  3. Wait for the heroku app to restart.
  4. Test the setting by visiting http://www.example.com/.well-known/acme-challenge/whatever
    NOTE THE HTTP, NOT HTTPS
    It should display the Challenge Response string. If this happens, go on to the next step. If not, do whatever it takes to get that URL to return the CR string before proceeding, or you will need to repeat this entire process.
  5. Return to Certbot and press Enter to continue.
    If all goes as planned, certbot will tell you everything worked and display the location of the created certs. You'll use this location in the next step. Note that you might not be able to inspect the contents of the folder due to os permissions. If in doubt, sudo ls /etc/letsencrypt/live/www.example.com to see if the files exist.

Update the Heroku instance to use the new certs:

Run heroku certs:add if your site doesn't have a cert. If updating, run heroku certs:update.
sudo heroku certs:update --app your-heroku-app-name /etc/letsencrypt/live/www.example.com/fullchain.pem /etc/letsencrypt/live/www.example.com/privkey.pem

like image 67
stone Avatar answered Nov 13 '22 17:11

stone


You can also validate your domain ownership to Let's Encrypt with DNS instead of HTTP.

With certbot, specify DNS as your preferred challenge:

sudo certbot certonly --manual --preferred-challenges dns

After a couple of prompts, certbot will tell you to deply a DNS TXT record to validate your domain:

Please deploy a DNS TXT record under the name
_acme-challenge.www.codesy.io with the following value:

CxYdvM...5WvXR0

Once this is deployed,
Press ENTER to continue

Your domain registrar probably has its own docs for deploying a TXT record. Do that, and go back to certbot and press ENTER - Let's Encrypt will check the TXT record, sign the cert, and certbot will save it for you to upload to heroku.

See my detailed blog post for more.

like image 24
groovecoder Avatar answered Nov 13 '22 16:11

groovecoder