Managing secrets securely is crucial for any application, especially in cloud environments. Google Cloud Secret Manager provides a centralized and secure way to store and manage sensitive information like API keys, passwords, and certificates. This post will guide you through creating and managing secrets using Python and the Google Cloud Secret Manager API.

Prerequisites

Before you start, ensure you have the following:

  • A Google Cloud Platform (GCP) project.
  • The Google Cloud SDK (gcloud) installed and configured.
  • Python 3.6 or higher.
  • The Google Cloud Secret Manager Python library installed. You can install it using pip:

    pip install google-cloud-secret-manager
    
  • Ensure you have enabled the Secret Manager API for your project in the Google Cloud Console.

Setting Up Authentication

You need to authenticate your Python script with Google Cloud to access the Secret Manager API. The recommended approach is to use a service account.

  1. Create a Service Account: In the Google Cloud Console, navigate to “IAM & Admin” -> “Service Accounts” and create a new service account.
  2. Grant Permissions: Grant the service account the necessary permissions. At a minimum, it needs the “Secret Manager Secret Accessor” ( roles/secretmanager.secretAccessor) and “Secret Manager Admin” (roles/secretmanager.admin) roles. The Secret Manager Admin role is required to create, update and delete secrets. The Secret Manager Secret Accessor is required to read secret values. For production environments, it is recommended to create custom roles with only the minimal required permissions.
  3. Download the Key File: Download the JSON key file for the service account.
  4. Set the Environment Variable: Set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of the key file:

    export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-key.json"
    

Python Code Examples

Here’s how to perform common Secret Manager operations using Python.

Creating a Secret

from google.cloud import secretmanager

def create_secret(project_id, secret_id):
    """
    Creates a new secret in Google Cloud Secret Manager.
    """
    client = secretmanager.SecretManagerServiceClient()
    parent = f"projects/{project_id}"

    try:
        secret = client.create_secret(
            request={
                "parent": parent,
                "secret_id": secret_id,
                "secret": {"replication": {"automatic": {}}},
            }
        )
        print(f"Successfully created secret: {secret.name}")
        return secret.name
    except Exception as e:
        print(f"Error creating secret: {e}")
        return None

# Example usage:
project_id = "your-gcp-project-id"  # Replace with your GCP project ID
secret_id = "my-super-secret"
secret_name = create_secret(project_id, secret_id)

Replace "your-gcp-project-id" with your actual Google Cloud project ID. The replication policy {"replication": {"automatic": {}}} configures the secret to be replicated automatically across Google’s global network.

Adding a Secret Version

from google.cloud import secretmanager

def add_secret_version(project_id, secret_id, payload):
    """
    Adds a new version to an existing secret.
    """
    client = secretmanager.SecretManagerServiceClient()
    parent = f"projects/{project_id}/secrets/{secret_id}"

    payload_bytes = payload.encode("UTF-8")

    try:
        version = client.add_secret_version(
            request={"parent": parent, "payload": {"data": payload_bytes}}
        )
        print(f"Successfully added secret version: {version.name}")
        return version.name
    except Exception as e:
        print(f"Error adding secret version: {e}")
        return None

# Example usage:
project_id = "your-gcp-project-id"  # Replace with your GCP project ID
secret_id = "my-super-secret"
payload = "This is my secret value!"
version_name = add_secret_version(project_id, secret_id, payload)

This function adds a new version of the secret with the provided payload. The payload is encoded as UTF-8 before being sent to the API.

Accessing a Secret Version

from google.cloud import secretmanager

def access_secret_version(project_id, secret_id, version="latest"):
    """
    Accesses the payload of a specific secret version.
    """
    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/{project_id}/secrets/{secret_id}/versions/{version}"

    try:
        response = client.access_secret_version(request={"name": name})
        payload = response.payload.data.decode("UTF-8")
        print(f"Secret value: {payload}")
        return payload
    except Exception as e:
        print(f"Error accessing secret version: {e}")
        return None

# Example usage:
project_id = "your-gcp-project-id"  # Replace with your GCP project ID
secret_id = "my-super-secret"
secret_value = access_secret_version(project_id, secret_id)

This function retrieves the payload of a specific secret version. By default, it accesses the “latest” version.

Disabling a Secret Version

from google.cloud import secretmanager

def disable_secret_version(project_id, secret_id, version_id):
    """Disables a secret version."""

    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"

    try:
        response = client.destroy_secret_version(request={"name": name})
        print(f"Disabled secret version: {name}")
        return True
    except Exception as e:
         print(f"Error disabling secret version: {e}")
         return False

# Example Usage
project_id = "your-gcp-project-id"
secret_id = "my-super-secret"
version_id = "1"  # Replace with the version to disable
disable_secret_version(project_id, secret_id, version_id)

This disables a secret version. Disabled versions cannot be accessed.

Deleting a Secret

from google.cloud import secretmanager

def delete_secret(project_id, secret_id):
    """Deletes a secret."""

    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/{project_id}/secrets/{secret_id}"

    try:
        client.delete_secret(request={"name": name})
        print(f"Deleted secret: {name}")
        return True
    except Exception as e:
        print(f"Error deleting secret: {e}")
        return False

# Example Usage
project_id = "your-gcp-project-id"
secret_id = "my-super-secret"
delete_secret(project_id, secret_id)

This deletes an entire secret and all its versions. Use with caution!

IAM Permissions

The following IAM roles are relevant to Secret Manager:

  • roles/secretmanager.secretAccessor: Allows access to secret values. This is the minimum permission required to read secrets.
  • roles/secretmanager.secretVersionManager: Allows managing secret versions (adding, disabling, destroying).
  • roles/secretmanager.admin: Provides full administrative access to Secret Manager, including creating, deleting, and managing secrets and their versions, as well as setting IAM policies.

Remember to grant the least privilege necessary to service accounts and users. Avoid granting broad roles like roles/owner when more restrictive roles are sufficient.

Best Practices

  • Use Service Accounts: Authenticate your applications using service accounts with the necessary permissions.
  • Principle of Least Privilege: Grant only the minimum required permissions to service accounts and users.
  • Secret Rotation: Implement a secret rotation policy to regularly update your secrets.
  • Monitoring and Auditing: Monitor Secret Manager access and audit logs to detect any suspicious activity.
  • Avoid Storing Secrets in Code: Never hardcode secrets directly in your code or configuration files.

Conclusion

Google Cloud Secret Manager provides a robust and secure way to manage your application’s secrets. By using the Python client library and following best practices, you can ensure that your sensitive information is protected and properly managed. Remember to carefully configure IAM permissions and implement a secret rotation policy for enhanced security.