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)$
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
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
The --data-file=-
flag allows us to pipe the secret to the gcloud
command from the output of the previous command, which just echo
s 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.
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
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
At this point, your directory structure should look like this:
.
├── main.py
└── requirements.txt
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
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
Now we can test our function once it's deployed:
$ gcloud functions call secret_hello
executionId: 4856xc8an4fa
result: The chickens are in the hayloft.
Next steps
There's lots more you can do with Cloud Functions! Follow the links below to learn how to:
- Write "background" trigger functions that respond to events
- Monitor functions at a high level with Stackdriver
- See additional tips & tricks for writing Cloud Functions
All code © Google w/ Apache 2 license