Creating a secure Bluemix hybrid app using TLS encryption

In this article I will demonstrate how to secure a hybrid IBM Bluemix application using the Secure Gateway and the Mutual TLS encryption option.

Introduction

In the previous article I demonstrated how to create a sample hybrid app which was unsecure because you could just call the gateway URL and access the application behind the firewall. While this worked well as a concept demo, it is not a production feasible set up. In this article we will look at how to set up a secure tunnel to the gateway URL and then on to our application.

The basis for this article comes from this developerworks article.

https://developer.ibm.com/bluemix/2015/04/17/securing-destinations-tls-bluemix-secure-gateway/

It took me a long time (relatively) to figure out how to make this work in my environment as I did not understand what was being accomplished by the example. I hope to provide a greater level of detail and explanation in this article.

Creating a secure gateway

Following the steps described previously we can set up a Secure Gateway within our Bluemix app. This time we are going to create a gateway which is secured with TLS encryption. As you can see from the image below, when you select the “TLS Mutual Auth” option a grey section appears underneath the form fields.

  • Select Auto Generate cert and Private key

s1

 

Click on the + Icon at the end of the fields and you will see the new gateway created

s2

Click on the gear icon at the end of the line and you will see an option to “Download Keys”. On selecting that a zip file will be downloaded. You will notice that I have not blurred out the port or Destination ID this time. This is because the point of this is that without those TLS Keys, knowing this information will be of no use to you. (You’re welcome).

Once the Keys are downloaded you need to add them to your node application (in my case in the root)

s3

 

As you can see from the image below, the .pem files are just text files which are the key files used as part of the encryption handshake when we connect to the gateway.

s4

Once the keys have been added to the project we are then able to create our basic app.

Basic node app 101

The following code creates a basic node website

var express = require('express');
var app = express();
var http = require('http');

app.get('/', function(req, res) {
    res.write("Hi I am the root")
    res.end();
});

var host = (process.env.VCAP_APP_HOST || 'localhost');
var port = (process.env.VCAP_APP_PORT || 4000);
app.listen(port, host);

 

s5

 

Secure tunnel

We are going to create a secure tunnel to the Bluemix Secure gateway. We do this using the following code which should be saved as tunnel.js.

//tunnel.js
var tls = require('tls');
var fs = require('fs');
var net = require('net');

var options = {
    host: 'cap-sg-prd-5.integration.ibmcloud.com',
    port: '15101',
    key: fs.readFileSync('Hqb17PFJ9Oe_5lm_key.pem'),
    cert: fs.readFileSync('Hqb17PFJ9Oe_5lm_cert.pem'),
    ca: fs.readFileSync('DigiCertCA2.pem')
};

var creations = 0;
var server;

//In this case the port value is the port of the tunnel created on the local server
//to the secure gateway - this is NOT the 15xxx port of the gateway
exports.create = function(port, callback) {
    if(creations == 0){
        creations++;
        //server not currently running, create one
        server = net.createServer(function (conn) {
            connectFarside(conn, function(err, socket) {
                if (err){
                    console.log(err);
                }
                socket.pipe(conn);
                conn.pipe(socket);
            });
        });
        server.listen(port, function(){
            console.log('tunnel on port: '+port);
            callback();
        });
    } else{
        //server already running
        creations++;
        callback()
    }
};

function connectFarside(conn, callback) {
    try {
        var socket = tls.connect(options, function() {
            console.log('tunnel connected');
            callback(null, socket);
        });
        socket.on('error', function(err){
            console.log('Socket error: ' + JSON.stringify(err));
        });
    } catch(err) {
        console.log(err)
        callback(err);
    }
};

exports.close = function(){
    creations--;
    if(creations == 0){
        //close the server if this was the only connections running on it
        server.close();
    }
}

 

This file needs some explanation. What it is doing is the following:

  • When we call tunnel.create we are going to create a secure tunnel from the current node server to whatever is passed in through the options object
  • The port on which the tunnel is created has NOTHING to do with the port of the secure gateway. This tunnel will connect a specific port on the current server to the port on the gateway server.
  • The tunnel itself connects to the secureGateway on port 15101 (in this case)
  • The port is created as part of the connection to the gateway. When the connection is complete the port is closed. This prevents someone from guessing the new port on the server and using it!
  • This is not the best way of doing it, it is not very flexible for a reusable, in production, service with multiple connections. The port and server should not be hard coded. They are for this example so it is easier to understand. We will look at making it generic it later.

Connecting to our backend service (simpleHTTP.js)

In this case I am demonstrating connecting to a web page, but there is no reason why you cannot connect to mysql, mongo or anything else. I have a simple http connection module which will connect to the specified webpage on the back end and return the page as a buffered string back to the original app.get(‘/secureTunnel’).

//simpleHTTP.js
var http = require('http')
exports.run = function(options, callback){
    var response = {};
    var body = "";

    var req = http.get(options, function(res) {
        // Buffer the body entirely for processing as a whole.
        var bodyChunks = [];
        res.on('data', function(chunk) {
            // You can process streamed parts here...
            bodyChunks.push(chunk);
        }).on('end', function() {
            body = Buffer.concat(bodyChunks);
            //put the response into a format which can be easily passed to the callback
            response = {'body': body, 'resHeaders': res.headers}
            callback(response)
        })
    });
}

Building out our node app

Building out the rest of the app.js it looks like this:

  • Create the route for app.get(“secureTunnel”, function()……..)
  • When called the route does two things
    • Calls tunnel.create
    • Passes the connection request to simpleHTTP
    • Closes the tunnel

 


var express = require('express');
var app = express();
var http = require('http');
var tunnel = require('./tunnel.js');      //code used to create and manage the secure tunnel
var simpleHTTP = require('./simpleHTTP'); //code used to create the request to the http service (web page)

app.get('/secureTunnel', function(req, res) {
    tunnel.create('8888', function(){

        var options = {
            //host is not necessary in this case because it binds directly this server if blank
            port: '8888',          //The tunnel port
            path: '/xomino/jQinX.nsf/Marky?readform' //the path of the test page on my laptop
        };

        var callback = function(obj){
            res.writeHead(200, {"Content-Type": "text/html"});
            res.write(JSON.stringify(obj.resHeaders)+"<hr/>")
            res.end(obj.body);
        }
        //make the http call and display the results out on the page.
        var obj = simpleCopper.run(options, callback);
        tunner.close()
    });
});

var host = (process.env.VCAP_APP_HOST || 'localhost');
// The port on the DEA for communication with the application:
var port = (process.env.VCAP_APP_PORT || 4000);
// Start server
app.listen(port, host);

Putting it all together

Here is the process for the connection laid out in bullet points:

  • Request comes in to /secureTunnel
  • Create a secure tunnel on port 8888
  • The tunnel is created on port 8888 by setting:
    • The secureGateway URL
    • The secureGateway port
    • The secure gateway keys
    • and then opening the new tunnel to the secureGateway
  • At this point the host website (bluemix) on port 8888 is not connected to the secure gateway on port 15101
  • The connection is made and the request to the service is made
    • The request to the connection is not made over port 80 or 4000 (or whatever you are using for the host), it is actually made to the newly created tunnel port 8888
    • The connection to 8888 routes out to secureGateway port 15101 – this is now permitted
    • The secure gateway in turn connects back into the hybrid environment (192.168.0.2:80) and gets the desired information from within the firewall
    • The firewalled service responds back through the secure gateway and back to the host port 8888
    • The response is packaged up and returned to the user’s screen (for this demo the HI Message)
    • The tunnel on port 8888 is then closed and cannot be accessed by anyone any more.

And we have a result locally

s6

Which is my node server locally, connecting to the secure gateway to come BACK to my local domino server

s7

This of course would look WAAAY more impressive it was Bluemix making the call. So I committed the code and pushed it up the the xominoKnox Bluemix repository…….et voilà

s8

Conclusion

What I hoped to achieve in this article is a step by step explanation of how a secure tunnel is created to facilitate the secure hybrid environment. As I mentioned this hard coded version is not ideal for production yet because the keys are hard coded to the connection. With a little effort the code could be genericised to use the connection tunnel multiple secure gateways within BlueMix.

The code for this project can be found here – https://hub.jazz.net/git/mdroden/xominoKnox but I assure you the gateway is not longer open 😉

Very Cool 🙂

 

3 thoughts on “Creating a secure Bluemix hybrid app using TLS encryption

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s