Launching RESTHeart into Production

Published

Now that we've shown you how to build instant RESTFul API's with RESTHeart and secure your RESTHeart installation, there's just one more step to building instant, secure API's from Compose MongoDB: taking RESTHeart into production.

Over the last few weeks, we've looked at how RESTHeart is a great way to build out a REST API from your MongoDB database. We've gone from a database schema to an "instant" API and then looked at how to make that API more secure with SSL and authentication. Now, we've reached the point where we want to put this into production. We want to have this all running up in the cloud so we can have the seamless API we've been dreaming of. In this third article in our the series, we'll take a small back step and use the Database Identity Manager to secure our credentials. Then, we'll use the Bluemix and CloudFoundry CLIs to deploy RESTHeart to the cloud using the IBM Bluemix Container Service.

Identity: What's in a Username?

In our previous article, we used RESTHeart's SimpleFileIdentityManager to store our users' credentials in a flat file. While that might be ok for some simple use cases, this method stores our users' passwords in the open and does not scale nicely. Fortunately, RESTHeart also ships with a DBIdentityManager and, since we're already connecting to MongoDB with RESTHeart anyway, it's a breeze to implement.

To use the Database Identity Manager, first pop open your restheart.yml file, navigate to the Security section, and add the following:

idm:  
    implementation-class: org.restheart.security.impl.DbIdentityManager
    conf-file: ./etc/security.yml
access-manager:  
    implementation-class: org.restheart.security.impl.SimpleAccessManager
    conf-file: ./etc/security.yml

Notice that we're also specifying the settings for the access-manager as well. This will give us a foundation for role-based security, and since this is something we'll likely want to change only during development, it makes sense to use the SimpleAccessManager for now.

Next, open your ./etc/security.yaml file and add the following:

dbim:  
    - db: restheart
      coll: _users
      cache-enabled: false
      bcrypt-hashed-password: true      
      cache-size: 1000
      cache-ttl: 60000
      cache-expire-policy: AFTER_WRITE

This configuration tells RESTHeart to use the Database Identity Manager, which is bundled with your installation automatically. The db field refers to the database that contains your users, and the coll field is the collection that contains your user accounts. The underscore ( _ ) at the beginning of the collection name means that RESTHeart will treat the accounts collection as a special, reserved collection. Reserved collections are not exposed via the API, and that makes sense; you don't want your usernames and passwords exposed to the world. Finally, make sure you set the bcrypt-hashed-password option to true or your password will be stored in plain text.

Next, beneath the previous dbim section, let's specify the access control settings:

# Users with role 'ADMIN' can do anything
permissions:  
    - role: ADMIN
      predicate: path-prefix[path="/"]
    - role: OPERATOR
      predicate: path-prefix[path="/myapp"]
    - role: USER
      predicate: path-prefix[path="/myapp"]

# Not authenticated user can only GET any resource under the /public URI
    - role: $unauthenticated
      predicate: path-prefix[path="/public"] and method[value="GET"]

These settings create 3 roles: ADMIN, OPERATOR, and USER and provide various levels of permission to those users. The reserved $unauthenticated role specifies what public users are able to access within the API. If you're going to have public access to your RESTFul API, it's best to separate the public section of your application from the rest of your data.

Now, let's try running RESTHeart with our new configuration:

docker run -d -p 80:8080 --name restheart -v $PWD/etc:/opt/restheart/etc:ro softinstigate/restheart:3.0.0  

And let's check to logs to make sure it worked:

docker logs restheart  
07:05:36.185 [main] ERROR org.restheart.Bootstrapper - Error configuring Identity Manager implementation org.restheart.security.impl.DbIdentityManager  
java.lang.reflect.InvocationTargetException: null  
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)  

Uh oh - we got an exception.

Digging into the docs, we'll see that we need to create an initial user in the database before we can start up RESTHeart. But before we do create our user, let's take a quick detour to talk about generating encrypted passwords so our first user will be a secure one.

Encrypting Your Password

When we created our security.yml file and set the bcrypt-hashed-password value to true, what we told RESTHeart was that our password would be supplied and stored in an encrypted form. A best practice for security, especially in production, is to NEVER store your users' passwords in plain text.

However, MongoDB doesn't have a BCrypt function built into it. So how can we encrypt our passwords? Fear not!

There are many different utilities available to use BCrypt from the command line. For now, we'll choose the Node.JS version and install it using npm, but in practice any trusted implementation should produce the same results.

Start by installing the command-line wrapper for the bcrypt library:

$ npm install -g bcrypt-cli

Next, let's generate a salted, hashed version of the password for our new user.

$ bcrypt-cli supersecret
$2a$10$5nCQsTp5VuwMzRF.1aZTyewYyBlT3ird3r3M6Rbc3sEsd9ME/itDu

This outputs the salted hash string for our new password. We can now safely store this password in our database, and RESTHeart will know how to authenticate against it.

Let's Create A User

Now that we have a good, strongly encrypted password, we can create our user. We can do this either through the command line interface, or through a script on the server. Since the method we choose doesn't really matter, let's make it simple and use the mongo command line interface:

mongo --ssl --sslAllowInvalidCertificates aws-us-east-1-portal.23.dblayer.com:17249/restheart -u dbuser -p secret  

Next, let's insert a new user with the ADMIN role we created in our access manager.

mongos> db.users.insert({ _id: "admin", password: "$2a$10$5nCQsTp5VuwMzRF.1aZTyewYyBlT3ird3r3M6Rbc3sEsd9ME/itDu", roles: ['ADMIN'] });  
WriteResult({ "nInserted" : 1 })  

Notice two things here: first, that we used our encrypted hashed password that we created in the previous step, and second that our "role" looks exactly how we defined it in the security.yml file. If all was successful, we should see the user and password when we query the database:

mongos> db.users.find({_id: "admin"});  
{ "_id" : "admin", "password" : "$2a$10$5nCQsTp5VuwMzRF.1aZTyewYyBlT3ird3r3M6Rbc3sEsd9ME/itDu", "roles" : [ "ADMIN" ] }

Finally, let's exit the mongo shell and try running RESTHeart one more time:

docker run -d -p 80:8080 --name restheart -v $PWD/etc:/opt/restheart/etc:ro softinstigate/restheart:3.0.0  
19:26:25.429 [main] INFO  org.restheart.Bootstrapper - Starting RESTHeart instance default  
19:26:25.439 [main] INFO  org.restheart.Bootstrapper - version 3.0.0  
19:26:25.452 [main] INFO  org.restheart.Bootstrapper - Logging to console with level INFO  

Alright, that's better. Now that we are using our Database Identity Manager, we can authenticate calls against the database the same as we did before:

curl -i --user admin:supersecret http://localhost/restheart  

Notice that we supply our plaintext password here, rather than the encrypted one. The Database Identity Manager will correctly compare our plaintext password against the encrypted one we stored in the database.

Deploying to Production with IBM Bluemix Container Service

Now that you have your RESTHeart system secured, it's time to deploy them to the open web. Using a service such as the Bluemix Container Service means that we can deploy RESTHeart quickly without having to manage servers or Docker infrastructure ourselves.

Navigate to console.ng.bluemix.net and create an account (or log into your existing account). There are two ways you can deploy your application: in a clustered environment using Kubernetes, or as Docker single and scalable containers. Since our application only has one container, we'll choose the single and scalable containers option.

Follow the instructions on the IBM Bluemix site for creating single and scalable containers. Once you've created the container, download the CloudFoundry CLI in the format appropriate for your platform. We can verify the installation was completed successfully by running the following command:

cf -v  

Next, install the Bluemix CLI for your platform and install the IBM Container Service plugin:

bx plugin install IBM-Containers -r Bluemix  

You can verify the installation by checking out the list of installed plugins:

bx plugin list  

Once you've installed the Bluemix CLI, you'll need to set up your API within the correct region. You'll only need to do this the first time you run the Bluemix CLI:

bx api https://api.ng.bluemix.net  

Now, it's time to log into Bluemix so we can push our RESTHeart image up to it:

bx login  

Pushing Our Local RESTHeart Image to Bluemix Container Service

Next, we'll want to create a private image registry in the Bluemix Container Service to upload our RESTHeart container image bundled with our custom configuration changes. This needs to be private, rather than on a public registry such as DockerHub, because this image will have full access to our database.

The first step is to create a namespace in the bluemix container registry. You can think of a namespace as the base URL of your registry; you'll likely have a different namespace for every organization or project you wish to deploy.

bx ic namespace-set <your namespace here>  

If we wanted a namespace for this project of compose_restheart, we would have the following:

bx ic namespace-set compose_restheart  

And now, we'll initialize the Bluemix Container Service for our namespace and account:

bx ic init  

Once we've initialized the Container Service, we can start to build our local image. First, we'll put together a simple Dockerfile to tell docker how to build our custom RESTHeart image:

FROM softinstigate/restheart

COPY ./etc /opt/restheart/etc  

Finally, let's build our container using the Bluemix CLI, which will automatically upload the build image to our private image repository. The -t flag gives us a tag name that we can use to refer to our image later:

$ bx ic build -t registry.ng.bluemix.net/<your namespace>/restheart .

Remember to replace <your namespace> with your actual namespace. If you used our namespace above, your command would look like the following:

$ bx ic build -t registry.ng.bluemix.net/compose_restheart/restheart .

We can double-check our image actually made it all the way to our images repository typing the following into the terminal:

$ bx ic images
REPOSITORY                                                  TAG                 IMAGE ID            CREATED             SIZE  
registry.ng.bluemix.net/compose_restheart/restheart                          latest              8de059ee71fc        2 minutes ago         317.4 MB  
registry.ng.bluemix.net/ibm-node-strong-pm                  latest              322b9ca7b2dc        2 weeks ago         616.4 MB  
registry.ng.bluemix.net/ibmliberty                          latest              6595ea483bf5        2 weeks ago         552.8 MB  
registry.ng.bluemix.net/ibmnode                             latest              b2c351248227        2 weeks ago         472.4 MB  
registry.ng.bluemix.net/ibmnode                             v4                  b2c351248227        2 weeks ago         472.4 MB  
registry.ng.bluemix.net/ibmnode                             v1.1                7d11220193d6        2 weeks ago         449.2 MB  
registry.ng.bluemix.net/ibmnode                             v1.2                84efce0c747b        2 weeks ago         465.2 MB  

You should see your new image listed there.

Building and Deploying Our Container

Now that we've built our RESTHeart container and made it available to the Bluemix Container Service in our private registry, there's just one step left: it's time to run the container!

$ bx ic run --name restheart registry.ng.bluemix.net/compose_restheart/restheart

If all goes well, you should now have a container running in the IBM Bluemix Container Service. You can double-check that your container is running by running the following command:

$ bx ic ps
CONTAINER ID        IMAGE                                           COMMAND             CREATED             STATUS                  PORTS                         NAMES  
68f6536a-82f        registry.ng.bluemix.net/compose_restheart/restheart:latest   ""                  3 minutes ago          Running 6 seconds ago   8080/tcp                      restheart  

Your service is now running in the cloud, but we still need to attach it to a public IP address. To find out the IP addresses available to you, run the following command:

$ bx ic ips
43.222.342.122  

NOTE: If you don't see any IP addreses in this list, run the following command to request one.

$ bx ic ip-request

Then, run the previous ips command to see the IP addresses leased to you.

Finally, let's bind one of those ip addresses to our running container:

bx ic ip-bind <your ip address> restheart  

Now, just load up a web browser and navigate to your IP address and port. You can test this now by acessing the built-in data browser by navigating to http://<your ip address>:8080/browser.

For more information on using the Bluemix Container Service, check out the running single containers tutorial on the Bluemix site.

Summary

In this article, we put together the final pieces for a production RESTHeart deployment, allowing you to safely and securely expose MongoDB on the public Internet. You can now create dynamic API's that are automatically generated from your MongoDB data and that can be accessed only by those with the correct roles and permissions. We'll continue to explore the IBM Bluemix Container Service and ways to integrate other Bluemix services in future articles.


If you have any feedback about this or any other Compose article, drop the Compose Articles team a line at articles@compose.com. We're happy to hear from you.

attribution Bayu Rivaldy

John O'Connor
John O'Connor is a code junky, educator, and amateur dad that loves letting the smoke out of gadgets, turning caffeine into code, and writing about it all. Love this article? Head over to John O'Connor’s author page to keep reading.

Conquer the Data Layer

Spend your time developing apps, not managing databases.