Pragmatism in the real world

Passing secrets to your OpenWhisk action

There is only one way to pass data into your OpenWhisk action: parameters. Your function receives a dictionary of parameters and returns one. Let’s explore how you can set these parameters.

Parameters passed to the action

You can see this by creating this action:

test.swift

func main(args: [String:Any]) -> [String:Any] {
    return args
}

Add the action to OpenWhisk with: wsk action update params params.swift -a web-export true and test:

$ wsk action invoke -b -r test -p a 1 -p b 2
{
    "a": 1,
    "b": 2
}

If you use a Web Action (unauthenticated HTTP requests), then OpenWhisk will add some additional parameters related to the request:

$ curl "https://openwhisk.ng.bluemix.net/api/v1/experimental/web/19FT_dev/default/test.json?a=1&b=2"
{
  "a": "1",
  "b": "2",
  "__ow_path": "",
  "__ow_method": "get",
  "__ow_headers": {
    "accept": "*/*",
    "user-agent": "curl/7.51.0",
    "x-forwarded-proto": "https",
    "host": "10.155.72.21:10001",
    "cache-control": "no-transform",
    "via": "1.1 AQAAAIJ7CBE-",
    "x-global-transaction-id": "302279417",
    "connection": "close",
    "x-forwarded-for": "86.138.48.38"
  }
}

From now on, I’ll skip the __ow_ parameters from the output I display as they are just noise in the context of this article.

Default parameters

You can also set default parameters that are always passed to an action. This is done when you update or create an action by using the -p flag:

$ wsk action update test test.swift -a web-export true -p foo one

We have now added the default parameter foo to our list of args as you can see:

$ curl "https://openwhisk.ng.bluemix.net/api/v1/experimental/web/19FT_dev/default/test.json?a=1"
{
    "a": "1",
    "foo": "one"
}

We can therefore use default parameters to pass secrets such as Redis credentials to our action which is a very useful thing to be able to do! You can also add default parameters to packages using the same -p flag which is really useful when we have a group of actions that need the same credentials.

Overriding default parmeters

One problem however is that the defaults can be overridden:

$ curl "https://openwhisk.ng.bluemix.net/api/v1/experimental/web/19FT_dev/default/test.json?a=1&foo=overridden-by-me"
{
    "foo": "overridden-by-me"
}

Sometimes, this could be really useful behaviour, for example the canonical hello world action could have a default paramter of name set to world which is then overridden when calling the action. However, if the default parameters contain credentials or other operational data, then we don’t want them overridden as it lead to some unexpected behaviour.

The “final” annotation

To prevent overriding of default parameters, we use the final annotation.

OpenWhisk actions can be annotated using the -a flag when creating and updating. We’ve already used the web-export annotation to inform OpenWhisk that this action is accessible to the WebActions system and so can be called without authorisation.

The final annotation makes all the parameters that are already defined on the action immutable. This means that they cannot be overridden.

To do this, we pass in -a final true when we update/create our action:

$ wsk action update params params.swift -a web-export true -p foo one -a final true

When we now call our action, we get an error:

$ curl "https://openwhisk.ng.bluemix.net/api/v1/experimental/web/19FT_dev/default/test.json?a=1&foo=overridden-by-me"
{
  "error": "Request defines parameters that are not allowed (e.g., reserved properties).",
  "code": 606919
}

Problem solved!

There’s only one caveat. As of the time of writing, if you invoke the action using the authenticated API (i.e. via the wsk tool or a POST request with the Authorisation header), then the final annotation is ignored. I understand that this will change in the future.

Fin

The argument dictionary that you receive into your action is the only way to pass credential data into the action and so the best way to do this is with default parameters on the action or package and then setting the final annotation to true.

You may also want to consider using a JSON dictionary for all your config data, so that there’s only one parameter in the argument’s list that will clash with anything passed in by the action’s callers.

4 thoughts on “Passing secrets to your OpenWhisk action

  1. Is it all or nothing with `final`? You can't have some parameters `final` and some not?

    I guess you can work around that using the single parameter dictionary for your config, as you suggested, and handle the final or not final yourself in the code?

    1. When final is enabled, all pre-configured parameters cannot be overridden by query/post parameters. Hence, you can't set defaults for query/post parameters by pre-configuring them.

  2. Interesting approach. I myself was wondering how you generally should provision configuration to OpenWhisk actions and came to no good solution. This here is the best approach so far. However I don't think it is suitable if the given configuration is really meant to be secret, i.e. should not be in danger of popping up somewhere. In the end OpenWhisk does not assume final default parameters to be "classified". Or am I wrong here?

    1. I think for truly secret information you'd be looking at using Hashicorp Vault or similar in a preceding action within a sequence.

Comments are closed.