By Ran Bar-Zik | 2/14/2019 | General |Beginners

Node.js & Express with HTTPS in Docker

Node.js & Express with HTTPS in Docker

In our last article on Docker, I showed and demonstrated for you how to run Docker containers with WordPress. Today, I’ll show you how to set up a Node.js/Express machine and get it ready to go with HTTPS in order to fully clone a Node.js server. Of course, you can use the principals to any other kind of server and get it working with HTTPS.

 

So first off, let’s set up an Express server. How do we do this? Simple—with a compose file that has a container. Which container you ask? Well, I chose a popular Express one and made a few alterations—like getting rid of Mongo which I don’t need here.

 

version: '2'

services:
 myapp:
   tty: true # Enables debugging capabilities when attached to this container.
   image: 'bitnami/express:latest'
   labels:
     kompose.service.type: nodeport
   command: npm run development
   environment:
     - PORT=3000
     - NODE_ENV=development

     - SKIP_DB_WAIT=0
     - SKIP_DB_MIGRATION=0
     - SKIP_NPM_INSTALL=0
     - SKIP_BOWER_INSTALL=0
   ports:
     - 80:3000
     - 443:8000
     - 3000:3000
   volumes:
     - .:/app

There are a few important things to note here. The most important is that I have mappings to ports. There’s the mapping 443 which I’m mapping to port 8000, which is the port of my application. When we have Node/Express, it works behind a router that brings everything from the various ports (80, 8080, and 433) to port 3000 or any other port I choose. Port 443 is special because it’s the port of the HTTPS protocol and I route it to the internal port 8000.

docker-compose-port-forward

Port mapping in Docker: port 80 goes to port 3000 and port 443, which has the HTTPS protocol, goes to port 8000.

 

I save the file in an empty folder and run it in the Docker terminal:

docker-compose up

The application will be built directly in the app folder and I’ll be able to see it if I go to the IP. But I want full HTTPS support. How do we achieve this? From the Docker end of things, I did all that I can do. Now we need to take care of thing on the application side.

 

As opposed to Apache/NginX and the like that know how to work with HTTPS straight out of the box, with Node, I need to take steps to get HTTPS working. How? First things first I need to make a key.

 

For those of you in the know, HTTPS communication works with RSA (the initial connection works like this and the communication itself works with symmetric encryption) which requires a public and private key. It’s not so complicated, really. I’ll make a folder in my project and call it sslcert. Then I’ll run the following command in it:

openssl req -newkey rsa:2048 -new -nodes -keyout key.pem -out csr.pem

 

What this command does is create both a public and private encryption key. The private key is key.pem and when we open it we can see that it has inside “public key.” Then the encrypted public key is csr.pem which serves as our certificate.  That’s all we need.

 

Now we just need to get the server working with HTTPS which is also pretty easy. There are a few methods we can work with. We’ll focus on the easiest method—creating another HTTPS server in the boot file that is an exact replica of the HTTP. It listens to port 8000 and will catch all the secured connections. Sound terrifying? It’s really not. Here’s how it looks:

 

#!/usr/bin/env node

/**
* Module dependencies.
*/

var app = require('../app');
var appSecured = require('../app'); // Creating appSecured
var debug = require('debug')('app:server');
var http = require('http');
var https = require('https'); // require native node's native https module
var fs = require('fs');

/**
* Get port from environment and store in Express.
*/
var privateKey  = fs.readFileSync('/app/sslcert/key.pem', 'utf8');
var certificate = fs.readFileSync('/app/sslcert/server.crt', 'utf8');

/**
* Prepare the credentials
*/
var credentials = {key: privateKey, cert: certificate};

/**
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || '3000');
var portSecured = normalizePort(process.env.PORT_SECURED || '8000'); // defining port secured
app.set('port', port);
appSecured.set('port', portSecured); // appSecured listen to 8000 port

/**
* Create HTTP server.
*/

var server = http.createServer(app);

/**
* Create HTTPS server.
*/
var serverSecured = https.createServer(credentials, appSecured);

/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
* HTTPS Listen on provided port, on all network interfaces.
*/
serverSecured.listen(portSecured);
serverSecured.on('error', onError);
serverSecured.on('listening', onListening);

/**
* Normalize a port into a number, string, or false.
*/

function normalizePort(val) {
 var port = parseInt(val, 10);

 if (isNaN(port)) {
   // named pipe
   return val;
 }

 if (port >= 0) {
   // port number
   return port;
 }

 return false;
}

/**
* Event listener for HTTP server "error" event.
*/

function onError(error) {
 if (error.syscall !== 'listen') {
   throw error;
 }

 var bind = typeof port === 'string'
   ? 'Pipe ' + port
   : 'Port ' + port;

 // handle specific listen errors with friendly messages
 switch (error.code) {
   case 'EACCES':
     console.error(bind + ' requires elevated privileges');
     process.exit(1);
     break;
   case 'EADDRINUSE':
     console.error(bind + ' is already in use');
     process.exit(1);
     break;
   default:
     throw error;
 }
}

/**
* Event listener for HTTP server "listening" event.
*/

function onListening() {
 var addr = server.address();
 var bind = typeof addr === 'string'
   ? 'pipe ' + addr
   : 'port ' + addr.port;
 debug('Listening on ' + bind);
}

So there’s really no need to be scared. It’s the normal settings file that comes with the container and I simply added to it the code that creates the secured server which listens to port 8000. This is all the code that I added:

 

var appSecured = require('../app'); // Creating appSecured
var https = require('https'); // require native node's native https module
var fs = require('fs');

/**
* Get port from environment and store in Express.
*/
var privateKey  = fs.readFileSync('/app/sslcert/key.pem', 'utf8');
var certificate = fs.readFileSync('/app/sslcert/server.crt', 'utf8');

/**
* Prepare the credentials
*/
var credentials = {key: privateKey, cert: certificate};
var portSecured = normalizePort(process.env.PORT_SECURED || '8000'); // defining port secured
appSecured.set('port', portSecured); // appSecured listen to 8000 port

/**
* Create HTTPS server.
*/
var serverSecured = https.createServer(credentials, appSecured);

/**
* HTTPS Listen on provided port, on all network interfaces.
*/
serverSecured.listen(portSecured);
serverSecured.on('error', onError);
serverSecured.on('listening', onListening);

If I go to the address of my machine with HTTPS, I’ll get an HTTPS error that of course shows that everything is fine. Why? Because that is a connection. My browser doesn’t know or trust my certificate, but if I proceed I can start working.

dockerNodeHTTPS

I can make that annoying message disappear and authorize my browser to accept it. But that’s a whole other story for a whole other article.

Last, I need to say something important: this article is only covering installing a basic local site or server. It is not an environment that is suitable for production.

 

Previous article: Docker & WP Cli

Next article: coming soon!

 

About the author: Ran Bar-Zik is an experienced web developer whose personal blog, Internet Israel, features articles and guides on Node.js, MongoDB, Git, SASS, jQuery, HTML 5, MySQL, and more. Translation of the original article by Aaron Raizen.

By Ran Bar-Zik | 2/14/2019 | General

{{CommentsModel.TotalCount}} Comments

Your Comment

{{CommentsModel.Message}}

Recent Stories

Top DiscoverSDK Experts

User photo
3355
Ashton Torrence
Web and Windows developer
GUI | Web and 11 more
View Profile
User photo
3220
Mendy Bennett
Experienced with Ad network & Ad servers.
Mobile | Ad Networks and 1 more
View Profile
User photo
3060
Karen Fitzgerald
7 years in Cross-Platform development.
Mobile | Cross Platform Frameworks
View Profile
Show All
X

Compare Products

Select up to three two products to compare by clicking on the compare icon () of each product.

{{compareToolModel.Error}}

Now comparing:

{{product.ProductName | createSubstring:25}} X
Compare Now