Looking for good programming challenges?

Use the search below to find our solutions for selected questions!

How to setup Let’s Encrypt (SSL) Certificate on OpenShift

Sharing is caring!

In previous posts I have described how to deploy a Node.js application to OpenShift. Now its time to add a custom alias to our Node.js application so that it is accessible through a custom domain, like test.testnode.com. Currently it is accessible only through testnode-lukesnode.rhcloud.com. Off course we also want valid SSL certificates for our custom domain testnode.com. For that we need to get a certificate (a type of file) from a Certificate Authority (CA). Let’s Encrypt is a free, automated, and open certificate authority brought to you by the non-profit Internet Security Research Group (ISRG). So obviously we will use that.

Create alias via OpenShift web console
From the Applications section choose your application (e.g. testnode) and then click on change alias. For Domain Name enter your custom domain. Mine is test.testnode.com. Leave the rest of the fields blank and click Save.

To successfully use this alias, you must have an active CNAME record with your DNS provider. The alias is test.testnode.com and the destination app is testnode-lukesnode.rhcloud.com.

My provider is united-domains.de. So I went ahead, logged in and under Subdomains -> New Sub Domain I have created a new subdomain test.testnode.com. Then under DNS Configuration for test.testnode.com, I was able to set the CNAME record to rtcrandom-lukesnode.rhcloud.com for *.test.testnode.com (test.testnode.com included).

And thats it!

Create certificates
We will need a valid certificate and its corresponding private key to upload to OpenShift for the new domain test.testnode.com. Under Mac OS X I have used certbot. So go ahead and install certbot:

$ brew install certbot

Once installed run:

$ sudo certbot certonly
Password:
Saving debug log to /var/log/letsencrypt/letsencrypt.log

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Apache Web Server plugin - Beta (apache)
2: Place files in webroot directory (webroot)
3: Spin up a temporary webserver (standalone)
-------------------------------------------------------------------------------
Select the appropriate number [1-3] then [enter] (press 'c' to cancel): 2
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
to cancel):test.testnode.com
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for test.testnode.com

Select the webroot for test.testnode.com:
-------------------------------------------------------------------------------
1: Enter a new webroot
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel): 1
Input the webroot for test.testnode.com: (Enter 'c' to cancel):/
Waiting for verification...
Cleaning up challenges
Failed authorization procedure. test.testnode.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://test.testnode.com/.well-known/acme-challenge/p1zEUvrrpAuTgj-b1bBk0zt9ypOn-BeLJWmxDi2xWXQ [54.173.248.172]: 404

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: test.testnode.com
   Type:   unauthorized
   Detail: Invalid response from
   http://test.testnode.com/.well-known/acme-challenge/p1zEUvrrpAuTgj-b1bBk0zt9ypOn-BeLJWmxDi2xWXQ
   [54.173.248.172]: 404

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A record(s) for that domain
   contain(s) the right IP address.

OK, that didn’t work. Obviously Let’s Encrypt wants us to prove that we are the truthful owners of test.testnode.com. The way it verifies ownership is trying to load the above URL (http://test.testnode.com/.well-known/acme-challenge/p1zEUvrrpAuTgj-b1bBk0zt9ypOn-BeLJWmxDi2xWXQ) and compare the received result with the expected result.

We need to modify our Node.js application to return the hash Let’s Encrypt requires when the above URL is GET. In my router.js I have added the below code snippet:

...
app.get('/.well-known/acme-challenge/:challengeHash', function(req, res) {
    var hash = req.params.challengeHash;
    res.send(hash);
});
...

The above code reads the hash from the requested URL and returns it. OK, lets try it one more time.

$ sudo certbot certonly
Password:
Saving debug log to /var/log/letsencrypt/letsencrypt.log

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Apache Web Server plugin - Beta (apache)
2: Place files in webroot directory (webroot)
3: Spin up a temporary webserver (standalone)
-------------------------------------------------------------------------------
Select the appropriate number [1-3] then [enter] (press 'c' to cancel): 2
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
to cancel):test.testnode.com
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for test.testnode.com

Select the webroot for test.testnode.com:
-------------------------------------------------------------------------------
1: Enter a new webroot
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel): 1
Input the webroot for test.testnode.com: (Enter 'c' to cancel):/
Waiting for verification...
Cleaning up challenges
Failed authorization procedure. test.testnode.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: The key authorization file from the server did not match this challenge [4O6L9LRIPXYOMNvKF18MNLdFFfh1GAhE5CZxruqS-G8.im3xdwOnE4siuDOLKh9D_aLGIuKulPmTzzJgkvhCO5E] != [4O6L9LRIPXYOMNvKF18MNLdFFfh1GAhE5CZxruqS-G8]

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: test.testnode.com
   Type:   unauthorized
   Detail: The key authorization file from the server did not match
   this challenge
   [4O6L9LRIPXYOMNvKF18MNLdFFfh1GAhE5CZxruqS-G8.im3xdwOnE4siuDOLKh9D_aLGIuKulPmTzzJgkvhCO5E]
   != [4O6L9LRIPXYOMNvKF18MNLdFFfh1GAhE5CZxruqS-G8]

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A record(s) for that domain
   contain(s) the right IP address.

Hmmmm, that didn’t work either. At this point I should probably read the manual. But apparently when the URL http://test.testnode.com/.well-known/acme-challenge/xxxxxxxxxxx is requested, it expects xxxxxxxxxxx.yyyyyyyyyyy as a result. So I went and modified my router.js again:

app.get('/.well-known/acme-challenge/:challengeHash', function(req, res) {
    var hash = req.params.challengeHash+'.im3xdwOnE4siuDOLKh9D_aLGIuKulPmTzzJgkvhCO5E';
    res.send(hash);
});

Giving it a try again, I finally got my certificates:

$ sudo certbot certonly

Saving debug log to /var/log/letsencrypt/letsencrypt.log

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Apache Web Server plugin - Beta (apache)
2: Place files in webroot directory (webroot)
3: Spin up a temporary webserver (standalone)
-------------------------------------------------------------------------------
Select the appropriate number [1-3] then [enter] (press 'c' to cancel): 2
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
to cancel):test.testnode.com
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for test.testnode.com

Select the webroot for test.testnode.com:
-------------------------------------------------------------------------------
1: Enter a new webroot
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel): 1
Input the webroot for test.testnode.com: (Enter 'c' to cancel):/
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/test.testnode.com/fullchain.pem. Your cert
   will expire on 2017-06-09. 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

The generated certificate and private key is located under /etc/letsencrypt/archive/test.testnode.comc/cert1.pem and /etc/letsencrypt/archive/test.testnode.com/privkey1.pem respectively.

Upload certificates to OpenShift
For this we will use the OpenShift client tools:

$ sudo rhc alias update-cert testnode test.testnode.com --certificate /etc/letsencrypt/archive/test.testnode.com/cert1.pem  --private-key /etc/letsencrypt/archive/test.testnode.com/privkey1.pem
SSL certificate successfully added.

Thats it! Our application is now accessible through https://test.testnode.com.