OSVauco/docs/SECRETS-SETUP.md

4.2 KiB

Google Cloud Secret Manager Setup Guide

This guide provides the gcloud commands to set up secrets in Google Cloud Secret Manager and configure the Cloud Run service to use them.

1. Create Secrets in Secret Manager

First, create the CHAT_WEBHOOK_URL and GOOGLE_API_KEY secrets.

# Create CHAT_WEBHOOK_URL secret
echo -n "your-chat-webhook-url" | gcloud secrets create CHAT_WEBHOOK_URL --data-file=-

# Create GOOGLE_API_KEY secret
echo -n "your-google-api-key" | gcloud secrets create GOOGLE_API_KEY --data-file=-

Replace "your-chat-webhook-url" and "your-google-api-key" with your actual secret values.

2. Grant Secret Accessor Role

Grant the Secret Manager Secret Accessor role to the service account that your Cloud Run service uses. By default, this is the default compute service account.

# Get your project number
PROJECT_NUMBER=$(gcloud projects describe <YOUR_PROJECT_ID> --format="value(projectNumber)")

# Construct the service account email
SERVICE_ACCOUNT_EMAIL="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"

# Grant access to CHAT_WEBHOOK_URL
gcloud secrets add-iam-policy-binding CHAT_WEBHOOK_URL 
    --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" 
    --role="roles/secretmanager.secretAccessor"

# Grant access to GOOGLE_API_KEY
gcloud secrets add-iam-policy-binding GOOGLE_API_KEY 
    --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" 
    --role="roles/secretmanager.secretAccessor"

Replace <YOUR_PROJECT_ID> with your Google Cloud project ID.

3. Update bridge/server.py to Use Secrets

Modify bridge/server.py to read secrets from Secret Manager when it's not running in a local development environment.

Here is the code patch to apply to bridge/server.py. This patch adds a function to retrieve secrets and modifies the post_to_chat function to use it.

# Add this import at the top of bridge/server.py
from google.cloud import secretmanager

# Add this function to bridge/server.py
def get_secret(secret_id: str, project_id: str, version_id: str = "latest") -> str:
    """
    Get a secret from Google Cloud Secret Manager.
    """
    try:
        client = secretmanager.SecretManagerServiceClient()
        name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"
        response = client.access_secret_version(name=name)
        return response.payload.data.decode("UTF-8")
    except Exception as e:
        print(f"Error accessing secret {secret_id}: {e}")
        return None

# The following is a patch for the lifespan context manager in bridge/server.py
# --- old
# from dotenv import load_dotenv
# load_dotenv()
# --- new
from dotenv import load_dotenv
from google.cloud import secretmanager

load_dotenv()

def get_secret(secret_id: str, project_id: str, version_id: str = "latest") -> str:
    """
    Get a secret from Google Cloud Secret Manager.
    """
    try:
        client = secretmanager.SecretManagerServiceClient()
        name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"
        response = client.access_secret_version(name=name)
        return response.payload.data.decode("UTF-8")
    except Exception as e:
        print(f"Error accessing secret {secret_id}: {e}")
        return None

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    if os.getenv("GAE_ENV", "").startswith("standard"):
        project_id = os.getenv("GOOGLE_CLOUD_PROJECT")
        if project_id:
            os.environ["CHAT_WEBHOOK_URL"] = get_secret("CHAT_WEBHOOK_URL", project_id)
            os.environ["GOOGLE_API_KEY"] = get_secret("GOOGLE_API_KEY", project_id)

    if not os.path.exists(STATE_DIR):
        os.makedirs(STATE_DIR)
    
    watcher_task = asyncio.create_task(state_watcher_with_approval_check())
    print("Bridge server started, watcher with approval check is running.")
    yield
    # Shutdown
    watcher_task.cancel()
    try:
        await watcher_task
    except asyncio.CancelledError:
        print("Watcher task cancelled.")

Apply this patch to your bridge/server.py file. This code checks for the GAE_ENV environment variable, which is set in App Engine and Cloud Run environments. If it's present, it fetches the secrets from Secret Manager and sets them as environment variables for the application to use.