Unlocking Secrets: Navigating Cloud Run’s Secret Access Methods

Ming Yao (Michael) Loh
3 min readMar 12, 2024

--

In today’s rapidly evolving cloud computing landscape, ensuring the security of sensitive data is more important than ever.

Google Cloud Platform’s (GCP) Secret Manager provides a reliable solution for securely storing, managing, and accessing secrets within GCP applications like Compute Engine, Cloud Function, and Cloud Run.

In Cloud Run, there are three distinct methods for accessing secrets. This article will delve into how these methods can be implemented using Python and deployed via YAML files, examining their respective advantages and limitations.

Environment Variables

To share a secret via environment variables, it’s important to note that the resolution of these variables happens during the instance startup. Google recommends binding the secret to a specific version instead of relying on the latest one. However, if you choose to use the latest version, it will continue to return the initially resolved version.

Example to deploy cloud run Service in YAML representation.

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: SERVICE
spec:pyth
template:
metadata:
name: REVISION
spec:
containers:
- image: IMAGE_URL
env:
- name: "my_secret"
valueFrom:
secretKeyRef:
key: "latest"
name: "my_secret"

Getting secret is as easy as reading the environment variable,

def get_secret_from_env_var() -> str:
return os.getenv('my_secret')

Pros and Cons

  1. This method is the simplest and most straightforward to implement among the three options.
  2. It’s beneficial for static, unchanging secrets because it avoids hitting the Secret Manager API every time you need to access the secret.
  3. It’s advantageous for Cloud Run jobs with short execution times.
  4. Implementing secret rotation can be challenging. You might find yourself in a situation where you have to wait for the Cloud Run service to restart, potentially taking hours or even days, or trigger a redeployment, resulting in downtime and additional overhead.

Volume Mount

Mounting each secret as a volume allows the secret to be accessed within the container as files. When reading from a volume, the secret value is consistently retrieved from Secret Manager, ensuring it’s always utilised with the latest version.

Similar to the previous method, you can incorporate it within the YAML deployment file.

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: SERVICE
spec:
template:
metadata:
name: REVISION
spec:
containers:
- image: IMAGE_URL
volumeMounts:
- mountPath: "/secrets" #filepath
name: VOLUME_NAME
volumes:
- name: VOLUME_NAME
secret:
items:
- key: "latest"
path: "my_secret" #filename
secretName: "my_secret"

Read from the file’s name and its corresponding path,

def get_secret_from_volume_mount() -> str:
with open("/secrets/my_secret", "r") as file:
file_contents = file.read()
return file_contents

Pros and Cons

  1. For secret rotation, accessing the latest secret version is facilitated by the Secret Manager API, which is automatically invoked by the backend each time the “file” is read.
  2. Local development is simplified as you can mount your local version of the secret as a Docker volume.
  3. Conversely, in scenarios of high traffic load where the file needs to be read for every request, there’s a risk of hitting the Secret Manager API quota limit. Implementing a cache mechanism like time-to-live cache from the Python cachetools package could mitigate this issue.

Secret Manager API

While accessing GCP Secret Manager’s secrets via the Python Cloud Client library is the conventional approach that can be implemented across various platforms and services, it is not the primary focus of this article.

def get_secret_from_api() -> str:
project_id = os.getenv("PROJECT_ID")
secret_id = "my_secret"
version_id = "latest"

# Create the Secret Manager client.
client = secretmanager.SecretManagerServiceClient()

# Build the resource name of the secret.
name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"

# Access the secret.
response = client.access_secret_version(request={"name": name})
payload = response.payload.data.decode("UTF-8")

return payload

Pros and Cons

  1. Just like the Volume Mount method, inefficient implementation may lead to hitting the Secret Manager API quota under high traffic usage.
  2. This approach offers greater flexibility and control, such as choosing the service account used. In contrast, the other two options necessitate assigning the appropriate roles to the Cloud Run service identity.
  3. Implementing this method in local development can be challenging without direct access to Secret Manager or the creation of customised code to manage local traffic.

In conclusion, there isn’t a one-size-fits-all solution for accessing secrets in Cloud Run. The choice of method should depend on your project’s specific requirements and, at times, your company’s security policies. It’s essential to make an informed decision based on these factors. For a comprehensive understanding and practical examples of all three implementations, you can refer to my GitHub repository!

--

--