Using the Cloud Functions Extension securely

This is an experience with using Apigee Extensions with Google Cloud Functions.

Google Cloud Functions is Google's serverless compute solution for creating event-driven applications supporting JavaScript (Node.js), Python, and Go.

You can invoke Cloud Functions directly over HTTP(S). Each function is given a dedicated domain and ...

Apigee now has an extension for invoking Cloud Functions, which helps us store our Cloud Function configuration details and gives us Apigee Platform benefits such as traffic management, authentication and analytics.

Looking at the process of putting together my first cloud function and exposing it through Apigee step by step we have

1. Creating and configuring your Cloud Function

From the Google Cloud Console, we create a new Cloud Function. This should be configured with a HTTP trigger and this example is using the default hello world node.js code on the selected node.js runtime.

8702-screenshot-2019-06-10-at-144244.png

2. Securing your Cloud Function

When I created this Cloud Function, by default any public internet user is able to invoke this function over http as it's open to public. Since we only want it to be consumed via an Apigee proxy, this needs to be disabled.

To do this, there are some Google Cloud console commands that are in alpha available.

We can see the current configuration via get-iam-policy, this shows that allUsers is currently a member.

We can then remove allUsers via remove-iam-policy-binding

gcloud alpha functions remove-iam-policy-binding hello-world --member=allUsers --role=roles/cloudfunctions.invoker

Finally, we can then add the service account for our Apigee extension. This involves first having a service account that the Apigee Extension will use, then adding it as a member with the cloud functions invoker role via add-iam-policy-binding.

gcloud alpha functions add-iam-policy-binding hello-world --member=serviceAccount:helloworld-func@apigee-demo-project.iam.gserviceaccount.com --role=roles/cloudfunctions.invoker

Now the cloud function can be accessed from the service account but not other public users.

Attempting to hit the trigger directly over the internet now gives us a 403 response.

3. Configuring your Apigee Extension

We are now ready to create our Apigee Extension. The extensions option is available under admin, where we can create our Cloud Functions Extension. Once this is done, we can then provide our service account credentials in the extension's environment configuration. If we create a JSON key for our service account, we can dump the contents of the key file/response into our Extension's credentials

We can then deploy our extension, and shortly it will be available in the selected environment

4. Configuring your Apigee API Proxy

We're now ready to add our Extension Callout policy to our API Proxy. I will be using it in the response flow so I can configure it to directly return the extension's output as the proxy's response by setting the output parameter. This is where we configure the details of our extension

We can now send a test request to our API Proxy, and we can see we get back the Cloud Function's response in our API Proxy's response body.

We're now ready to apply any other API policies required in your API Proxy such as authentication or traffic management. An example API proxy with this policy is available here

Comments
alexkhimich
Staff

Nice! Thanks for posting the full guide.

davissean
Staff

thanks @dane knezic!!

joel_gauci
Staff

Thanks for sharing @dane knezic , great article!

jeremylagorse
Participant I

Hello,

Is there a way to send params when invoking the the cloud function as we would do it in a GET request ("/endpoint?param1=bob&param2=marley).

Thank you in advance for you help?

Jeremy

dknezic
Staff

Hi Jeremy

Unfortunately there isn't using the extension.

However, I've got an example for another challenge (jwt blacklisting) that uses cloud functions where I interact with it using out of the box policies, and with this approach you have full control over the request to the cloud function ie custom verb, query parameters, path, etc.

https://github.com/dknezicd/memorystore-blacklist

This comes with a shared flow for getting an auth token for a cloud function https://github.com/dknezicd/memorystore-blacklist/tree/master/apigee/sharedflows/auth-gcp

As well as an example shared flow of doing a GET request to a cloud function with a service callout. You could then modify the service callout to suit your use case

https://github.com/dknezicd/memorystore-blacklist/tree/master/apigee/sharedflows/blacklist-lookup

Let me know if this is helpful or if you think a more vanilla example would be useful.

Also if it's helpful, this is the article for this example https://community.apigee.com/articles/79438/jwt-blacklisting-via-memorystore.html. It helps explain how the KVM is used for service accounts and how the shared flows come together etc.

dgoyal
Participant I

Hello,

I tried replicating this example. I am able to invoke cloud function directly.

ConnectorCallout.response is "<h1>Bad Request</h1> <p>The browser (or proxy) sent a request that this server could not understand.".

Please share any suggestion on what could be going wrong.

dgoyal
Participant I

I should add that the function being called is default hello-world python3.7 function. It appears that call from the connector is failing in the cloud function code at request.get_json(). Cloud function works fine if called riectly from the browser.

dgoyal
Participant I

I did further investigation. Cloud function connector is able to call the cloud functions written in Node.js. I get "The browser (or proxy) sent a request that this server could not understand." error from a cloud function written in Python 3.7. I am not sure what is different between calling cloud functions written in two different runtimes.

Both Node.js as well as Python implementation of cloud function work fine when tested without apigee.

Any suggestions on resolving the python function call issue will really be appreciated.

Now two follow-up questions

1. How to pass a request body from apigee proxy to the target function?

2. How to extract response from the target function and return that as part of the apigee proxy response?

Version history
Last update:
‎06-18-2019 05:59 AM
Updated by: