Note: These instructions are only applicable to web apps with a custom domain name.
You have a custom domain for your Heroku app and now you want an SSL Certificate to Secure/Encrypt all communications between users and your app.
Let's Encrypt offers a Free Automated SSL Certificate Service brought to you by the non-profit Internet Security Research Group (ISRG). see: https://letsencrypt.org/about/
### Instructions Valid for Apps Written in Any Language/Framework!
The instructions in this tutorial/guide are applicable
to an app written in any language or framework.
You will temporarily deploy a Node.js http-server
to your Heroku app
which will allow Let's Encrypt to verify that you "own" the app/domain.
Note: No Node.js knowledge is assumed or required. You won't be writing a single line of JS code.
Once you have set up SSL you can deploy what ever kind of app you like. (in our case the app is written in Elixir/Phoenix! ... node.js is just an easy way to get this working in a generic way.)
"certbot" is the script that automates the certificate creation process.
git clone https://github.com/dwyl/learn-heroku.git
cd learn-heroku
Check what your current origin
remote is:
git remote -v
Set it to what ever the git url is for the app you are setting up SSL for. e.g:
git remote set-url origin [email protected]:healthlocker/healthlocker.git
Push your current branch to the GitHub repo:
git push --set-upstream origin letsencrypt-temporary-server
Change it to the name of your branch e.g:
It should look something like this:
remember to (temporarily) dissable the checkbox Wait for CI to pass before deploy
(we have no tests for this temporary server!).
certbot
installation instructions for various platforms: https://letsencrypt.org/getting-started
brew install certbot
(it might take a few minutes to install on a slower internet connection... be patient...)
Once you've installed certbot
run the following command:
sudo certbot certonly --manual
Remember to use both the domain a www
subdomain. (separated by a space) e.g:
example.com www.example.com
Our app was:
healthlocker.uk www.healthlocker.uk
Follow the steps and pay close attention!
When you reach the screen that looks like this:
DON'T continue
until you have completed Step 5.
Instructions printed by certbot
:
(for reference ONLY see below for sub-set of instructions)
mkdir -p /tmp/certbot/public_html/.well-known/acme-challenge
cd /tmp/certbot/public_html
printf "%s" WgFpodyij_PDzkU0MZ3CzKCI05hjLOcq2tP-1rs6ko0.kURQ5HbILtRXEwJA2QI4W5TdBkjnZNqH2_RHORvmN6w > .well-known/acme-challenge/WgFpodyij_PDzkU0MZ3CzKCI05hjLOcq2tP-1rs6ko0
# run only once per server:
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \
"import BaseHTTPServer, SimpleHTTPServer; \
s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
s.serve_forever()"
You wont be able to run shell commands on a Heroku instance so we need to use a temporary node.js server to achieve our objective.
In your current working directory
(on your localhost)
run the following command to create the .well-known/acme-challenge
directory:
mkdir -p .well-known/acme-challenge
Now copy-paste the printf
command from the certbot
instructions:
they should look something like this:
printf "%s" WgFpodyij_PDzkU0MZ3CzKCI05hjLOcq2tP-1rs6ko0.kURQ5HbILtRXEwJA2QI4W5TdBkjnZNqH2_RHORvmN6w > .well-known/acme-challenge/WgFpodyij_PDzkU0MZ3CzKCI05hjLOcq2tP-1rs6ko0
The tokens will be specific to you so make sure you get the correct tokens. (there is one token per domain)
Make a commit on your local branch so you can push to github (and trigger the heroku build)
git add .
git commit -m 'add letsencrypt verification file'
git push
That will deploy the file you created in Step 4.2 to Heroku.
Visit your app in a web browser to confirm the deploy worked. e.g: http://example.com/.well-known/acme-challenge
The url for our app was: http://healthlocker.uk/.well-known/acme-challenge
It should download a text file to your computer when you visit the endpoint in the browser.
Hit the enter key in the terminal window to continue
the certbot
process:
If if it worked, you should see something like that output in your terminal. If not scroll down to Trouble-Shooting section below
You're amost there, this is the easy part!
Navigate to the settings section of your app on Heroku Dashboard e.g:
Scroll down to the "Domains and certificates" section and click on "Configure SSL" button:
Click on the link to paste the certificate:
Recall from above that the certificate generated by certbot
was saved to /etc/letsencrypt/live/healthlocker.uk/
(your domain will be different)
You can copy the contents of the file (without "leaking" it) by running the following command in your terminal:
sudo cat /etc/letsencrypt/live/healthlocker.uk/fullchain.pem | pbcopy
Note:
sudo
is required because thecertbot
runs as admin
for explanation of thepbcopy
command see:
http://superuser.com/questions/113529/how-can-i-load-a-files-contents-into-the-clipboard
Paste the Public
Key (cert.pem
you copied above)
into the <textarea>
on Heroku:
Click on the "Continue" button.
Repeat steps 7.2 to 7.4 for the Private Certificate:
sudo cat /etc/letsencrypt/live/healthlocker.uk/privkey.pem | pbcopy
You should have already previously set up the DNS settings
(else
step 5 would not have worked!)
Make sure you delete
the Heroku SSL Addon (if you had it enabled...)
Confirm that the certificate was successfully added to the app on Heroku:
e.g: https://www.healthlocker.uk
Restore the default
branch for deployment on Heroku:
Following the instructions in this
guide:
https://certbot.eff.org/docs/using.html#renewing-certificates
Visit the Settings
tab for your App in Heroku.
Scroll down to the Buildpacks
section.
Make a note of any buildpacks you
In the case of our Elixir/Phoenix app these were:
We copy-paste these into some sort of notepad (so that we can restore them later)
- https://github.com/HashNuke/heroku-buildpack-elixir.git
- https://github.com/gjaldon/heroku-buildpack-phoenix-static.git
Here's a quick summary of the steps:
- Re-run the
certbot
CLI tool. - Copy-paste the
printf
command (step 4.2 above) git add . && git commit -m 'adds letsencrypt verification token'
git push
theletsencrypt-temporary-server
branch oflearn-heroku
(to your project's github repo)- Push the
sudo cat /etc/letsencrypt/keys/0002_key-certbot.pem | pbcopy
sudo cat /etc/letsencrypt/live/healthlocker.uk/fullchain.pem | pbcopy
The first time I tried to run the certbot
command, nothing worked!
E.g: the Build failed on Heroku, the cert process failed (see below).
This is a catalog of the Trouble-Shooting we did.
As always, if you get stuck, ask a question we will try our best to help!
because we are using an Elixir "Build Pack" for the app (so deploying a node app won't work!):
So I made a note of the buildpack urls:
- https://github.com/HashNuke/heroku-buildpack-elixir.git
- https://github.com/gjaldon/heroku-buildpack-phoenix-static.git
And then
delete
them (temporarily):
After I delete
the build pack and push another commit, it passes:
When I attempted to continue
it failed:
Output
Waiting for verification...
An unexpected error occurred:
ConnectionError: ('Connection aborted.', error("(60, 'ETIMEDOUT')",))
Please see the logfiles in /var/log/letsencrypt for more details.
IMPORTANT NOTES:
- If you lose your account credentials, you can recover through
e-mails sent to [email protected].
- Your account credentials have been saved in your Certbot
configuration directory at /etc/letsencrypt. You should make a
secure backup of this folder now. This configuration directory will
also contain certificates and private keys obtained by Certbot so
making regular backups of this folder is ideal.
I deleted all the files created in the process and started from scratch ...
Re-trace your steps and make sure you followed the instructions exactly. Also, timing matters. if you take a break between steps you will get a "Time Out Error"... We initially got it wrong, but after re-running the command it works as expected.
If you visit your domain e.g: https://www.healthlocker.uk and takes ages to load and then displays a Certificate warning:
If we inspect the details of the warning we see that the browser is getting an incorrect cert.
I updated the DNS Settings to:
So I re-ran certbot
command for both domains healthlocker.uk www.healthlocker.uk
:
After running certbot
another time, it worked. 🚀
## Background Reading
- An introduction to SSL certificates: https://woocommerce.com/2015/12/ssl-certificates-for-ecommerce
- Public Key Certificate: https://en.wikipedia.org/wiki/Public_key_certificate
- Transport Layer Security: https://en.wikipedia.org/wiki/Transport_Layer_Security
- Certbot Manual mode: https://certbot.eff.org/docs/using.html#manual
- Inspiration tutorial (Ruby-on-Rails focussed): https://collectiveidea.com/blog/archives/2016/01/12/lets-encrypt-with-a-rails-app-on-heroku