Google Cloud SQL with IAM Authentication for GKE
Most of the instructions in this guide are specific to Google Cloud Platform (GCP) infrastructure setup rather than Zymtrace itself. We've documented our own experience setting up Cloud SQL with GKE to make it easier for users who want to use this configuration. The zymtrace-specific configuration is minimal - it's primarily about connecting to your PostgreSQL database once it's properly configured in GCP.
The Cloud SQL Proxy acts as a secure tunnel, handling IAM authentication and encryption transparently while providing Zymtrace with a standard PostgreSQL connection. This guide demonstrates how to set up Google Cloud SQL PostgreSQL with IAM authentication for use with GKE clusters. This configuration provides secure, password-less database access using Google Cloud's Workload Identity.
This document assumes you're setting up Cloud SQL from scratch. If you already have an existing Cloud SQL instance or GKE cluster, feel free to skip to the relevant sections that apply to your setup.
Overviewβ
What You'll Accomplishβ
By following this guide, you will:
- Create a Cloud SQL PostgreSQL instance with IAM authentication
- Set up Workload Identity between GKE and Cloud SQL
- Configure automatic database creation (optional)
- Deploy Zymtrace that securely connects to Cloud SQL without managing passwords
Architectureβ
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β GKE Pod βββββΆβ Cloud SQL Auth βββββΆβ Cloud SQL β
β β β Proxy β β PostgreSQL β
β zymtrace β β β β β
β K8s SA β β IAM Auth β β IAM User β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β β β
ββββββββββββββββββββββββββΌββββββββββββββββββββββββ
Workload Identity Binding
Prerequisitesβ
Before you begin, ensure you have:
| Requirement | Description |
|---|---|
| GKE Cluster | Running with Workload Identity enabled (or ability to enable it) |
| CLI Tools | gcloud and kubectl installed and authenticated |
| Permissions | IAM roles for Cloud SQL, GKE, and service account management |
Required IAM Rolesβ
Your user account needs these roles:
roles/cloudsql.admin- To create and manage Cloud SQL instancesroles/container.admin- To manage GKE clusters and node poolsroles/iam.serviceAccountAdmin- To create and manage service accountsroles/resourcemanager.projectIamAdmin- To bind IAM policies
Configurationβ
Environment Setupβ
First, set up your environment variables. Replace the example values with your actual project details:
# Project and instance configuration
export PROJECT_ID="your-project-id"
export REGION="us-central1"
export INSTANCE_NAME="zymtrace-postgres"
export DATABASE_VERSION="POSTGRES_17"
# GKE configuration
export CLUSTER_NAME="your-gke-cluster"
export NAMESPACE="your-namespace"
# Service account configuration
export SA_NAME="cloudsql-proxy-sa"
export K8S_SA_NAME="cloudsql-k8s-sa"
Implementation Stepsβ
Step 1: Create Cloud SQL Instanceβ
Create a new PostgreSQL instance with recommended settings for production use:
gcloud sql instances create $INSTANCE_NAME \
--database-version=$DATABASE_VERSION \
--region=$REGION \
--cpu=2 \
--memory=7680MB \
--storage-size=50GB \
--storage-type=SSD \
--storage-auto-increase \
--enable-bin-log \
--maintenance-release-channel=production \
--deletion-protection \
--project=$PROJECT_ID
Set a password for the default postgres user (required for initial setup):
gcloud sql users set-password postgres \
--instance=$INSTANCE_NAME \
--password="$(openssl rand -base64 32)"
The postgres password is only used for initial database setup. Zymtrace will use IAM authentication.
Step 2: Configure Service Accountsβ
Create a Google Cloud service account for Cloud SQL access:
# Create the service account
gcloud iam service-accounts create $SA_NAME \
--display-name="zymtrace Cloud SQL Proxy Service Account" \
--description="Enables zymtrace IAM authentication to Cloud SQL from GKE"
# Grant required Cloud SQL permissions
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/cloudsql.instanceUser"
Step 3: Database Setupβ
Create IAM Database Userβ
Create a database user that corresponds to your service account:
gcloud sql users create $SA_NAME@$PROJECT_ID.iam \
--instance=$INSTANCE_NAME \
--type=cloud_iam_service_account
Configure Database Permissionsβ
There are two approaches for setting up database permissions, depending on whether you want to use automatic database creation or manual database creation.
Option 1: Using autoCreateDB Mode (recommended, if possible)β
If you plan to use autoCreateDBs: true in your Helm configuration, you only need to grant the CREATEDB permission. The databases will be created automatically by Zymtrace on startup.
Connect to your instance:
# Connect as postgres user
gcloud sql connect $INSTANCE_NAME --user=postgres --database=postgres
Run this SQL command:
-- Grant CREATEDB permission (allows zymtrace to create databases automatically)
ALTER USER "your-sa-name@your-project.iam" CREATEDB;
-- Exit psql
\q
That's it! When you deploy Zymtrace with autoCreateDBs: true, the required databases (zymtrace_identity, zymtrace_symdb, and zymtrace_web) will be created automatically with the correct permissions.
Option 2: Using Manual Database Creation Processβ
If you prefer to manually create and manage databases (using autoCreateDBs: false), follow these steps:
Connect to your instance:
# Connect as postgres user
gcloud sql connect $INSTANCE_NAME --user=postgres --database=postgres
Run these SQL commands:
-- Step 1: Create the databases manually
CREATE DATABASE zymtrace_identity OWNER your-sa-name@your-project.iam;
CREATE DATABASE zymtrace_symdb OWNER your-sa-name@your-project.iam;
CREATE DATABASE zymtrace_web OWNER your-sa-name@your-project.iam;
-- Step 2: Alter schema
\c zymtrace_identity
ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam";
\c zymtrace_symdb
ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam";
\c zymtrace_web
ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam";
-- Exit psql
\q
The above SQL commands don't always work consistently in Cloud SQL with IAM users. If you encounter permission errors when creating databases with the IAM user as owner, try this:
Run this command first as a superuser (postgres or admin user):
GRANT "your-sa-name@your-project.iam" TO postgres;
Then proceed with the original database creation commands.
Create the databases first, then change ownership:
-- Create databases without specifying owner
CREATE DATABASE zymtrace_identity;
CREATE DATABASE zymtrace_symdb;
CREATE DATABASE zymtrace_web;
-- Then alter ownership
ALTER DATABASE zymtrace_identity OWNER TO "your-sa-name@your-project.iam";
ALTER DATABASE zymtrace_symdb OWNER TO "your-sa-name@your-project.iam";
ALTER DATABASE zymtrace_web OWNER TO "your-sa-name@your-project.iam";
-- Then alter schema ownership
\c zymtrace_identity
ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam";
\c zymtrace_symdb
ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam";
\c zymtrace_web
ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam";
If these approaches still don't work, consider contacting GCP support.
Important**: Replace your-sa-name@your-project.iam with your actual service account name (e.g., cloudsql-proxy-sa@your-project.iam).
Step 4: Configure GKE Workload Identityβ
Verify Node Pool Scopesβ
Check if your node pool has the required OAuth scopes:
gcloud container node-pools describe default-pool \
--cluster=$CLUSTER_NAME \
--region=$REGION \
--format="value(config.oauthScopes)"
The output should include https://www.googleapis.com/auth/cloud-platform.
If your existing node pool doesn't have the cloud-platform scope, you cannot update it after creation. Node pool scopes are immutable. You'll need to create a new node pool with the correct scopes.
Create Node Pool with Required Scopes (if needed)β
If your existing node pool lacks the required scopes, create a new node pool specifically for the Cloud SQL proxy:
# Create a new node pool with cloud-platform scope
gcloud container node-pools create cloudsql-pool \
--cluster=$CLUSTER_NAME \
--region=$REGION \
--scopes=https://www.googleapis.com/auth/cloud-platform \
--num-nodes=1 \
--machine-type=e2-medium \
--enable-autorepair \
--enable-autoupgrade
Add the node selector to your custom-values.yaml to ensure the proxy pod is scheduled on this new node.
postgres:
mode: "gcp_cloudsql"
nodeSelector:
cloud.google.com/gke-nodepool: cloudsql-pool
Enable Workload Identityβ
If not already enabled, enable Workload Identity on your cluster:
# Check current status
gcloud container clusters describe $CLUSTER_NAME \
--region=$REGION \
--format="value(workloadIdentityConfig.workloadPool)"
# Enable if needed (takes 10-30 minutes)
gcloud container clusters update $CLUSTER_NAME \
--workload-pool=$PROJECT_ID.svc.id.goog \
--region=$REGION
Set Up Identity Bindingβ
Create Kubernetes resources and bind them to Google Cloud:
# Create namespace
kubectl create namespace $NAMESPACE
# Create Kubernetes service account
kubectl create serviceaccount $K8S_SA_NAME -n $NAMESPACE
# Annotate for Workload Identity
kubectl annotate serviceaccount $K8S_SA_NAME -n $NAMESPACE \
iam.gke.io/gcp-service-account=$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com
# Create the binding
gcloud iam service-accounts add-iam-policy-binding \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:$PROJECT_ID.svc.id.goog[$NAMESPACE/$K8S_SA_NAME]" \
$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com