Building Secure Instant API's with RESTHeart and Compose
PublishedWhen you need to turn your Mongo database into a RESTFul API, RESTHeart can get you up-and-running quickly and securely. Following up on our previous article on using RESTHeart to expose a RESTFUL API directly from a Mongo database on Compose, in this article, we'll show you how to secure your RESTHeart API by adding authentication and role-base access control, as well as enabling SSL encryption.
When we explored how to create instant RESTFul API's on Compose with RESTHeart, we left out one key important feature: Securing those API's. By default, RESTHeart exposes your API's via HTTP with authentication turned off. Now it's time to make our Instant API's more production-ready by adding authentication and SSL encryption to our RESTHeart API endpoints.
Setup
We'll pick up where we left off in the previous article, and in particular, you'll want to have RESTHeart already connected to Compose MongoDB running in a Docker container. You should also have a folder at the root of your project called etc
and inside that etc
folder you should have a restheart.yml
starter file with a mongo-uri
key that points to your Compose MongoDB database.
Authentication
The first step to securing our RESTHeart application is to require a username and password to access the API. RESTHeart uses a pluggable identity management system that allows users to authenticate using a variety of authentication mechanisms. We'll start out by storing our credentials in a file and cover database-stored credentials in a future article.
File-based Authentication
The easiest way to get started is to store your users' credentials in a file and use the built-in file-based identity manager to authenticate your users. To set this up, first, create a new file in the etc
directory of your project (NOT your system /etc
folder) called security.yml
. Next, open restheart.yml
and add the following line of code to the Identity Management section:
idm:
implementation-class: org.restheart.security.impl.SimpleFileIdentityManager
conf-file: ./etc/security.yml
Then, open the ./etc/security.yml
file and add your users' credentials:
users:
- userid: admin
password: changeme
- userid: client
password: changeme
This provides us with 2 users, an admin user and a client user. Obviously, this SimpleFileIdentityManager
is less than ideal as it stores our users' passwords in plain text in a file rather than using industry-standard encryption in a database. Fortunately, the Identity Management system in RESTHeart is pluggable and in a future article, we'll prepare RESTHeart for production by creating a more secure custom Identity Manager to address th.
RESTHeart uses Basic Authentication, a web standard which generates a token from user credentials. If you visit the URL for RESTHeart in your browser, you'll be prompted to enter your username and password automatically. Once you've successfully authenticated, an authentication token is added to the header of each subsequent call.
If you're attempting to generate access tokens programmatically, rather than through your web browser, you'll need to perform the Basic Authentication steps. First, the initial credentials are sent via an Authorization:
header of the request, along with the word BASIC
and the username and password separated by a colon (:) encoded using base64 encoding like the following:
base64("admin:changeme")
The final result would look like the following:
Authorization: Basic YWRtaW46Y2hhbmdlbWUK
If the authorization request is successful, an auth token will be returned in the response headers of the call you made. They look something like the following:
Auth-Token: 6a81d622-5e24-4d9e-adc0-e3f7f2d93ac7
Auth-Token-Location: /_authtokens/user@si.com
Auth-Token-Valid-Until: 2017-04-30T11:13:45.031Z
Once you have the auth token, simply include it in the request headers for every subsequent call. They will work until the expiration date in the Auth-Token-Valid-Until
header.
You can use the CURL command to automatically generate the token and include it with every subsequent CURL request by using the following command:
curl -i --user admin:changeme http://localhost/restheart
For other methods, you may have to store the auth header value and include it manually for each request.
Access Management
So far we've looked at authenticating users in RESTHeart, but sometimes we need different users to have access to different parts of the system. Like the authentication system, RESTHeart has a pluggable access management system as well. This will allow us to define multiple roles and allow or deny access to specified routes and methods for those role types.
We'll try this out by using the File-based Access management system. First, open the ./etc/restheart.yml
file and add the following to the security section:
access-manager:
implementation-class: org.restheart.security.impl.SimpleAccessManager
conf-file: ./etc/security.yml
The SimpleAccessManager reads users and roles from a file. Let's open up our ./etc/security.yml
file again and add the following:
permissions:
- role: admins
predicate: path-prefix[path="/"]
- role: $unauthenticated
predicate: path-prefix[path="/public/"] and method[value="GET"]
- role: users
predicate: path-prefix[path="/public/"]
These settings create two roles, admins and users, as well as a built-in default role of $unauthenticated. Access is granted using predicates, and the predicates are define per-role, meaning predicates that affect one role type will not affect the others.
Our first predicate grants users with the role of admin access to any route using any method. The second grants any unauthenticated user permission to access any route with the prefix of /public, but only using the GET HTTP method. Finally, the last rule defines a users role that has access to any route prefixed with /public but using any HTTP method.
Next, we'll update our users to specify roles for each one:
users:
- userid: admin
password: changeme
roles: [admins]
- userid: client
password: changeme
roles: [users]
Note that roles is an array, meaning we can add any user type to multiple different roles. This allows us to create granular roles that define specific behavior, and allows us to create a robust role-based authentication scheme.
Now that we've covered authentication and authorization, there's one more security issue that we need to address: connection encryption.
SSL using Embedded Self-Signed Certificates
Using RESTHeart without SSL exposes your database to serious security risks, including Man-in-the-Middle attacks which can compromise the integrity and security of your database. To solve that problem, let's take a look at how we can set up RESTHeart to use HTTPS and SSL.
RESTHeart comes with an embedded set of self-signed certificates, so adding SSL support can be as simple as enabling these embedded self-signed certificates. To enable SSL, change the following configuration setting in the restheart.yml
file:
### listeners
https-listener: true
https-host: 0.0.0.0
https-port: 443
This enables the HTTPS listener in the RESTHeart service on port 443 (the default for HTTPS). We'll also need to tell RESTHeart which certificates to use for the connection. To use the embedded self-signed certificates that come with RESTHeart use the use-embedded-keystore
configuration option in your restheart.yml
file like the following:
#### SSL configuration
use-embedded-keystore: true
#keystore-file: /path/to/keystore/file
#keystore-password: password
#certpassword: password
Notice that, for now, we have the keystore-file
, keystore-password
, and certpassword
commented out. We'll see how we can use those in a moment.
When we run our container, we'll want to expose port 443 in our RESTHeart docker container and map it to our hosts' 443 port using the -p
command:
docker run -d -p 443:443 --name restheart -v $PWD/etc:/opt/restheart/etc:ro softinstigate/restheart:3.0.0
You can now access RESTHeart by going to https://localhost
. However, you may notice that visiting the site in a web browser will give you a security warning, and using CURL --insecure
flag. This is because the built-in self-signed certificates are not recognized as being valid certificates, and this will be an issue in production as well. In a future article, we'll explore how we can use authoritative certificates, and in particular, we'll look at using LetsEncrypt to generate automatically-renewing authority-signed certificates.
Wrapping up
In this article, we took a look at how we can add SSL and authentication mechanisms to our RESTHeart APIs and this gets us closer to running RESTHeart in production. In a future article, we'll complete the cycle and create a production-quality Custom Identity Manager in Java with hashed passwords, show how to use production-ready certificates using LetsEncrypt, and deploy our RESTHeart API to the public using the IBM Bluemix Container Service.
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.