Security of cloudant query from OpenWhisk

323 views Asked by At

I'm building an Angular SPA with a Cloudant data store on Bluemix.

Since the Bluemix implementation of OpenWhisk doesn't use VCAP services, I see 3 options to use OpenWhisk as my api provider for cloudant queries for my Angular app:

  1. Follow the pattern of passing credentials as seen here: https://github.com/IBM-Bluemix/openwhisk-visionapp (very interesting approach btw)
  2. Include the credentials as though I'm running locally as seen here: https://github.com/IBM-Bluemix/nodejs-cloudant/blob/master/app.js
  3. Use the http API as seen here: https://docs.cloudant.com/api.html (which highlights the security problem passing credentials.

Since my service is not intended for publishing (it's intended for my own app) I'm thinking option 2 is my "least of all evils" choice. Am I missing something? My thinking is such that while fragile to changes it would be the most secure since credentials aren't passed in the open. The serverless infrastructure would have to be hacked...

Thanks in advance!

(lengthy) Update: (apologies in advance)

I've gotten a little farther along but still no answer - stuck in execution right now.

To clarify, my objective is for the app to flow from Angular Client -> OpenWhisk -> Cloudant.

In this simplest use case, I want to pass a startTime parameter and an endTime parameter, have OpenWhisk fetch all the records in that time range with all fields, and passing back selected fields. In my example, I have USGS earthquake data in a modified GeoJSON format.

Following information from the following articles below, I've concluded that I can invoke the wsk command line actions and use the bindings I've setup from within my Javascript function and therefore not pass my credentials to the database. This gives me a measure of security (still question the rest endpoint of my OpenWhisk action) but I figure once I get my sample running I think through that part of it.

My command line (that works): wsk action invoke /[email protected]_mybluemixspace/mycfAppName/exec-query-find --blocking --result --param dbname perils --param query {\"selector\":{\"_id\":{\"$gt\":0},\"properties.time\":{\"$gt\":1484190609500,\"$lt\":1484190609700}}}

This successfully returns the following:
{ "docs": [ { "_id": "eq1484190609589", "_rev": "1-b4fe3de75d9c5efc0eb05df38f056a65", "dbSaveTime": 1.484191201099e+12, "fipsalpha": "AK", "fipsnumer": "02", "geometry": { "coordinates": [ -149.3691, 62.5456, 0 ], "type": "Point" }, "id": "ak15062242", "properties": { "alert": null, "cdi": null, "code": "15062242", "detail": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/ak15062242.geojson", "dmin": null, "felt": null, "gap": null, "ids": ",ak15062242,", "mag": 1.4, "magType": "ml", "mmi": null, "net": "ak", "nst": null, "place": "45km ENE of Talkeetna, Alaska", "rms": 0.5, "sig": 30, "sources": ",ak,", "status": "automatic", "time": 1.484190609589e+12, "title": "M 1.4 - 45km ENE of Talkeetna, Alaska", "tsunami": 0, "type": "earthquake", "types": ",geoserve,origin,", "tz": -540, "updated": 1.484191127265e+12, "url": "http://earthquake.usgs.gov/earthquakes/eventpage/ak15062242" }, "type": "Feature" } ] }

The action I created in OpenWhisk (below) returns an Internal Server Error. I'm passing the input value as
{ "startTime": "1484161200000", "endTime": "1484190000000" }

Here's the code for my action:

`var openWhisk = require('openwhisk');
var ow = openWhisk({
    api_key:'im really a host'
});

function main(params) {

  return new Promise(function(resolve, reject) {
  ow.actions.invoke({
    actionName:'/[email protected]_mybluemixspace/mycfAppName/exec-query-find',
    blocking:true,
    parameters:{
      dbname: 'perils',
      query:  {
        "selector": {
          "_id": {
            "$gt": 0
          },
          "properties.time": {
            "$gt": params.startTime,
            "$lt": params.endTime
          }
        }
      }
    }
    }).then(function(res) {
            //get the raw result
            var raw = res.response.result.rows;
            //lets make a new one
            var result = [];
            raw.forEach(function(c) {
                result.push({id:c.docs._id, time:c.docs.properties.time, title:c.docs.properties.title});
            });
            resolve({result:result});
        });
    });
}`

Here are the links to my research: http://infrastructuredevops.com/08-17-2016/news-openwhisk-uniq.html
Useful because of the use of the exec-query-find and selector syntax usage but also cool for the update function I need to build for populating my data!

https://www.raymondcamden.com/2016/12/23/going-serverless-with-openwhisk
The article referenced by @csantanapr

Am I overlooking something?

Thanks!

1

There are 1 answers

0
csantanapr On

I'm assuming what you are trying to do is to access your Cloudant DB directly from your angular client side code from the Browser.

If you don't need any business logic, or you can get away by using Cloudant features (design docs, views, map, reduce, etc..) and you are generating Cloudant API keys with certain access (i.e. write vs. read), then you don't need a server or serveless middlewear/tier.

But now let's get real, most people need that tier, and if you are looking a OpenWhisk, then you are in good luck this is very easy to do.

OpenWhisk on Bluemix support VCAP service credentials, but in a different way. Let's name you have a Bluemix Org [email protected] and space dev that would translate to OpenWhisk namespace [email protected]_dev

If you add a Cloudant service under the space dev in Bluemix, this will generate service key credentials for this Cloudant Account. This credentials give you super power access meaning you are admin.

If you want to use this Cloudant credentials in OpenWhisk, you can use the automatic binding generated with the cloudant package. To do this using the OpenWhisk CLI run wsk package refresh this will pull the Cloudant credentials and create you a new package with the credentials binded as default parameter for all the cloudant actions under that package. This is modified version of #1 above

Another alternative is to bind the credentials manually to a package or an action as default parameters, this makes sense when you don't want to use the super power admin credentials, and you generated a Cloudant API key for a specific database. This is option #1 above.

I would not recommend to put the credentials in source code #2

For option #3, what's insecure is to pass your credentials as part of the URL like https://username:[email protected], but passing the username and password in the Authorization header over https is secured. This is because even if you are using secure transport https everything in the URI/URL is not encrypted anyone can see that value, but passing secrets in body or header is standard practice as this is transfer after secure connection is established.

Then you create actions that use the credentials as parameters in your OpenWhisk actions to build your business logic for your backend.

Then how to do you access this backend from the Browser, well OpenWhisk has a API Gateway feature in experimental that allows your to expose your actions as public APIs with CORS enable.

Only a url is expose, your credentials as default parameters are never expose.

If you want to see an example on check out Raymond Camden Blog posts where he show Ionic/Angular App accessing his Cloudant Database of Cats https://www.raymondcamden.com/2016/12/23/going-serverless-with-openwhisk