Using Secrets in Google Cloud Functions

Dustin Ingram - Apr 25 '19 - - Dev Community

Google Cloud Functions makes it easy to build serverless Python programs. This post will show you how you can use the Google Secret Manager to safely and securely use secrets in your function.

Hard-coding or using environment variables to store plain-text strings that should be "secret", like API keys, secret tokens for cookies, etc. generally isn't recommended. Any third-party dependency or library you use has access to these same environment variables.

However, it's very convenient to store secrets along side your function. You can still do it, as long as you store these secrets safely and securely.

We'll do this by storing our secret with the Google Secret Manager, and accessing our secrets at the application layer. Doing so will limit access to the secret to just members of your team who have access to the secret (and, of course, the function when it's running on Google Cloud).

Setup

I'll assume that you've already created a Google Cloud project with a name (mine is my_cloud_project). If you haven't, go ahead and do that now.

Storing your secret

Now that you've created a project, the next step is to create a secret in it. We can do this on the command line with the gcloud tool! Also, we'll do it via Cloud Shell, so no credentials ever have to leave the cloud.

First, launch Cloud Shell for your project.

You should see a prompt that looks like this:

Welcome to Cloud Shell! Type "help" to get started.
Your Cloud Platform project in this session is set to my_cloud_project.
Use “gcloud config set project [PROJECT_ID]” to change to a different project.
di@cloudshell:~ (my_cloud_project)$
Enter fullscreen mode Exit fullscreen mode

Next, we'll enable the Secret Manager API (and the Cloud Functions API, while we're at it):

$ gcloud services enable secretmanager.googleapis.com cloudfunctions.googleapis.com
Enter fullscreen mode Exit fullscreen mode

Next, we'll create a new secret with the gcloud command line tool:

$ echo -n "The chickens are in the hayloft." | \
    gcloud secrets create my-secret \
      --data-file=- \
      --replication-policy automatic
Enter fullscreen mode Exit fullscreen mode

The --data-file=- flag allows us to pipe the secret to the gcloud command from the output of the previous command, which just echos the secret (with no newline).

Our secret is now securely stored. If we want to verify this, we can use the following command to access our secret on the command line:

$ gcloud secrets versions access 1 --secret="my-secret"
The chickens are in the hayloft.
Enter fullscreen mode Exit fullscreen mode

Using your secret

Now we'll write our function. Here's our whole function that we'll deploy, that will live in a file called main.py:

import os
from google.cloud import secretmanager

client = secretmanager.SecretManagerServiceClient()
secret_name = "my-secret"
project_id = "my-gcp-project"
request = {"name": f"projects/{project_id}/secrets/{secret_name}/versions/latest"}
response = client.access_secret_version(request)
secret_string = response.payload.data.decode("UTF-8")


def secret_hello(request):
    return secret_string
Enter fullscreen mode Exit fullscreen mode

This function is determining the resource name for the secret based on the function's environment, accessing the latest version of our secret, and then decoding the response, and finally returning the original string.

Also note how the function is doing this work outside of the Python function itself. This will speed up our function by only accessing the secret once when the Cloud Function is instantiated, and not once for every single request.

We'll also need to tell our function to install the google-cloud-secret-manager library, so we can access the secret from Python. In a file called requirements.txt, add the line:

google-cloud-secret-manager==2.0.0
Enter fullscreen mode Exit fullscreen mode

At this point, your directory structure should look like this:

.
├── main.py
└── requirements.txt
Enter fullscreen mode Exit fullscreen mode

Applying Permissions

We'll also need to give our function permission to access the secret:

$ gcloud secrets add-iam-policy-binding my-secret \
    --role roles/secretmanager.secretAccessor \
    --member serviceAccount:my-cloud-project@appspot.gserviceaccount.com
Enter fullscreen mode Exit fullscreen mode

The serviceAccount here is the default service account for Cloud Functions.

Deploying your app

The last step is to deploy our function with the gcloud tool:

$ gcloud functions deploy secret_hello \
    --runtime python38 \
    --trigger-http
Enter fullscreen mode Exit fullscreen mode

Now we can test our function once it's deployed:

$ gcloud functions call secret_hello
executionId: 4856xc8an4fa
result: The chickens are in the hayloft.
Enter fullscreen mode Exit fullscreen mode

Next steps

There's lots more you can do with Cloud Functions! Follow the links below to learn how to:

All code © Google w/ Apache 2 license

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .