GCP Cloud Run
Route traffic from Solo Enterprise for kgateway to a Google Cloud Run service that requires authentication.
The gateway proxy automatically obtains a GCP identity token and injects it into the Authorization header of each request, so that the gateway can call Cloud Run services that require authentication.
About
With the GCP Backend resource, you can route traffic to Google Cloud Run services that are hosted in your Google Cloud project. Solo Enterprise for kgateway uses Workload Identity Federation for GKE to authenticate to Google Cloud APIs. The gateway proxy automatically obtains and injects a GCP identity token into the Authorization header of each request, so that your Cloud Run service can verify the caller’s identity.
Before you begin
Create or select an existing GKE cluster.
Follow the Get started guide to install Solo Enterprise for kgateway.
Set up a gateway proxy with an HTTP listener.
Set environment variables that the rest of this guide uses. Replace the placeholder values with your own. The other values are sample defaults that you can change.
export PROJECT_ID=<your-project-id> export REGION=<your-region> export CLUSTER_NAME=<your-gke-cluster-name> export CLOUD_RUN_SERVICE=helloworld # GSA = Google IAM service account, the GCP-side identity that calls Cloud Run. # You create the GSA later in the guide. The name is your choice. export GSA_NAME=kgateway-cloud-run-invoker export GSA_EMAIL=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com # KSA = Kubernetes service account that the gateway proxy pod runs as. # The KSA is bound to the GSA through Workload Identity, so the proxy can # mint GCP identity tokens without a static credential. export NAMESPACE=kgateway-system export KSA_NAME=httpGet the external address of the gateway and save it in an environment variable.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n ${NAMESPACE} http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSkubectl port-forward deployment/http -n ${NAMESPACE} 8080:8080
Set up your Google Cloud environment
Verify that your Google Cloud user account has the IAM roles required for the sections of this guide that you plan to complete. Each section requires different roles, so you only need the roles for the work you actually do. For example, if you only plan to apply Kubernetes resources in Set up routing to your Cloud Run service, you do not need any GCP IAM roles.
To enable Workload Identity Federation on the GKE cluster, which is a one-time, cluster-wide prerequisite that the rest of this guide requires to be in place. Often, a cluster admin has already done this work; if so, you do not need these roles.
Role Why it’s needed roles/serviceusage.serviceUsageAdminEnable the run.googleapis.com,iam.googleapis.com, andiamcredentials.googleapis.comAPIs on the project.roles/container.clusterAdminEnable Workload Identity Federation on the GKE cluster and update node pools to use the GKE metadata server. To complete Set up Cloud Run and link service accounts:
Role Why it’s needed roles/run.adminDeploy a Cloud Run service and grant the service-scoped roles/run.invokerIAM binding. Only required if you don’t already have a Cloud Run service to route to.roles/iam.serviceAccountAdminCreate a Google IAM service account and grant the roles/iam.workloadIdentityUserbinding on it.roles/iam.serviceAccountUserDeploy a Cloud Run service that runs as a service account ( actAspermission on the runtime SA). Only required if you deploy a new Cloud Run service.To complete Set up routing to your Cloud Run service, you do not need any GCP IAM roles. You only need Kubernetes permissions to apply
BackendandHTTPRouteresources in thekgateway-systemnamespace. This section assumes that Workload Identity Federation is already enabled on the cluster and that a Google IAM service account is already linked to the gateway proxy’s Kubernetes service account from the earlier sections.
Verify that your GKE cluster has Workload Identity Federation for GKE enabled on the cluster and node pools. This is the cluster-wide prerequisite that lets pods request GCP tokens at all, and both later sections of this guide rely on it. The Set up Cloud Run and link service accounts section uses Workload Identity to bind a specific Google IAM service account to the gateway proxy’s Kubernetes service account, and the Set up routing to your Cloud Run service section then assumes both the cluster-level setup and that service account binding are in place.
If enabled, the following command returns your project’s workload pool in the format
<project-id>.svc.id.goog. An empty result means that the feature is not enabled on the cluster.gcloud container clusters describe ${CLUSTER_NAME} --region ${REGION} --project ${PROJECT_ID} --format='value(workloadIdentityConfig.workloadPool)'Example output:
<project-id>.svc.id.googIf the feature is not enabled, enable it on the cluster and then on each node pool. Both updates are long-running and the CLI shows a spinner the whole time. Do not cancel either command.
Cluster enablement: The cluster update can take 10–20 minutes or more, because GKE updates the control plane.
gcloud container clusters update ${CLUSTER_NAME} --region ${REGION} --project ${PROJECT_ID} \ --workload-pool=${PROJECT_ID}.svc.id.googNode pool enablement: The node-pool update triggers a rolling node replacement, typically a few minutes per node, so that the new nodes run with the GKE metadata server. Without this, pods on those nodes cannot mint Workload Identity tokens, and Cloud Run rejects requests from the gateway proxy with a
403.# "default-pool" is the name that gcloud uses for the initial pool. If your # cluster has different or multiple pools, list them with # `gcloud container node-pools list` and add each name to POOL_NAMES, # separated by spaces. The loop updates each pool in turn. export POOL_NAMES="default-pool" for POOL_NAME in ${POOL_NAMES}; do gcloud container node-pools update ${POOL_NAME} --cluster=${CLUSTER_NAME} --region=${REGION} --project=${PROJECT_ID} \ --workload-metadata=GKE_METADATA done
Set up Cloud Run and link service accounts
Create or identify a Cloud Run workload in your Google Cloud project, then link a Google IAM service account (GSA) to the gateway proxy’s Kubernetes service account (KSA) so that the gateway proxy can authenticate to the Cloud Run service. This service-account binding builds on the cluster-level Workload Identity Federation that you enabled in Set up your Google Cloud environment; you are not enabling Workload Identity again, just wiring the specific GSA/KSA pair that the gateway proxy uses.
If you do not have one, deploy a Cloud Run application in the same Google Cloud project as your GKE cluster. The following command deploys the Cloud Run
hellosample container with authentication required. The gateway proxy automatically injects the GCP token into theAuthorizationheader and passes that along to the Cloud Run instance to be successfully authenticated. If you deploy the instance with--allow-unauthenticatedinstead, the gateway proxy still injects an Authorization header, but Cloud Run does not enforce it.gcloud run deploy ${CLOUD_RUN_SERVICE} \ --image=us-docker.pkg.dev/cloudrun/container/hello \ --region=${REGION} --project=${PROJECT_ID} \ --no-allow-unauthenticatedExample output:
Deploying container to Cloud Run service [helloworld] in project [<project-id>] region [<region>] ✓ Deploying new service... Done. ✓ Creating Revision... ✓ Routing traffic... Done. Service [helloworld] revision [helloworld-00001-xxx] has been deployed and is serving 100 percent of traffic. Service URL: https://helloworld-<project-hash>.<region>.run.appGet the hostname that was assigned to your Cloud Run instance and save it as an environment variable. The hostname includes a project hash that the
gcloudcommand generates, so query it rather than construct it.export CLOUD_RUN_HOST=$(gcloud run services describe ${CLOUD_RUN_SERVICE} --region ${REGION} --project ${PROJECT_ID} --format='value(status.url)' | sed 's|^https://||') echo $CLOUD_RUN_HOSTExample output:
helloworld-<project-hash>.<region>.run.appVerify that the service is reachable and requires authentication. Send an unauthenticated request to the hostname. A
403response confirms that the service is private and requires a valid identity token. A200response means that the service allows unauthenticated invocations.curl -i "https://${CLOUD_RUN_HOST}"Output:
HTTP/2 403 content-type: text/html; charset=UTF-8 server: Google Frontend content-length: 295 ...In your cluster, link the
httpKubernetes service account in the${NAMESPACE}namespace to a Google IAM service account by using Workload Identity Federation for GKE. The gateway proxy uses this link to obtain GCP identity tokens that it injects into requests to your Cloud Run service.- At a minimum, the IAM service account must include the run.invoker and iam.serviceAccountUser roles.
- For the full steps, see the Kubernetes ServiceAccounts to IAM guide in the Google Cloud docs.
Optional: Verify that Workload Identity is set up correctly before sending traffic to your Cloud Run instance. The gateway proxy image does not include
curl, so run a temporary test pod under the samehttpKubernetes service account and ask the GKE metadata server which identity the pod represents.kubectl run wi-test -n ${NAMESPACE} --rm -it --restart=Never \ --overrides='{"spec":{"serviceAccountName":"'"${KSA_NAME}"'"}}' \ --image=curlimages/curl -- \ curl -sH "Metadata-Flavor: Google" \ http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/emailIf you see the node’s default service account or an error, Workload Identity is not wired. Check both bindings:
Verify that the Kubernetes service account is annotated with the GSA email.
kubectl get serviceaccount ${KSA_NAME} -n ${NAMESPACE} -o jsonpath='{.metadata.annotations}'The output must contain
iam.gke.io/gcp-service-account=<gsa-email>.Verify that the gateway proxy pod’s node pool has
GKE_METADATAset.for POOL_NAME in ${POOL_NAMES}; do gcloud container node-pools describe ${POOL_NAME} --cluster=${CLUSTER_NAME} --region=${REGION} --project=${PROJECT_ID} --format='value(config.workloadMetadataConfig.mode)' doneEach line must print
GKE_METADATA. If you seeGCE_METADATAor empty output, re-run the node-pool update from Set up your Google Cloud environment.
Set up routing to your Cloud Run service
Create a Backend and an HTTPRoute resource to route requests to the Cloud Run service.
Create a Backend resource that represents your Cloud Run service.
kubectl apply -f - <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: Backend metadata: name: gcp-backend namespace: ${NAMESPACE} spec: type: GCP gcp: host: ${CLOUD_RUN_HOST} EOFExample output:
backend.gateway.kgateway.dev/gcp-backend createdThe
gcpsection supports the following fields:Field Required Description hostYes The hostname of the GCP service. Used for SNI and as the target address. audienceNo The GCP service account audience URL. Defaults to https://<host>when omitted. Used by the GCP authentication filter to request the appropriate identity token.Create an HTTPRoute that matches incoming traffic on the
/gcppath and routes it to the Cloud Run Backend. The GCP Backend automatically rewrites the HTTPHostheader on outbound requests to match the Backend’shostfield, which is the value Cloud Run requires.kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: gcp-route namespace: ${NAMESPACE} spec: parentRefs: - name: http namespace: ${NAMESPACE} rules: - matches: - path: type: PathPrefix value: /gcp backendRefs: - name: gcp-backend group: gateway.kgateway.dev kind: Backend EOFExample output:
httproute.gateway.networking.k8s.io/gcp-route createdSend a request to verify that Solo Enterprise for kgateway correctly routes traffic to your Cloud Run service.
curl -v $INGRESS_GW_ADDRESS:8080/gcpcurl -v localhost:8080/gcpLook for a
200HTTP response code and the response body from your Cloud Run service. The defaulthellosample app responds with the Cloud Run welcome page:<!doctype html> <html lang=en> <head> <meta charset=utf-8> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="robots" content="noindex,nofollow"> <title>Congratulations | Cloud Run</title> ...If you receive a
403withwww-authenticate: Bearer error="insufficient_scope", the gateway proxy is sending a token that Cloud Run rejects. Common causes:- The GKE node pool was not updated with
--workload-metadata=GKE_METADATA(see Set up your Google Cloud environment), so pods on those nodes cannot mint Workload Identity tokens. - The GSA does not have
roles/run.invokeron the Cloud Run service. - The gateway proxy pod started before the Workload Identity binding was complete. Restart it.
kubectl rollout restart deployment/http -n ${NAMESPACE}` - The Backend’s
hostdoes not match the Cloud Run hostname.
- The GKE node pool was not updated with
Cleanup
You can optionally remove the resources that you set up as part of this guide.
Delete the HTTPRoute and Backend resources.
kubectl delete httproute gcp-route -n ${NAMESPACE} kubectl delete backend gcp-backend -n ${NAMESPACE}If you don’t need Workload Identity for any other workloads, remove the in-cluster annotation and the GCP-side bindings.
- Remove the GSA annotation from the
httpKubernetes service account. The trailing-removes the annotation.kubectl annotate serviceaccount ${KSA_NAME} -n ${NAMESPACE} iam.gke.io/gcp-service-account- - Remove the Workload Identity binding on the GSA.
gcloud iam service-accounts remove-iam-policy-binding ${GSA_EMAIL} \ --member="serviceAccount:${PROJECT_ID}.svc.id.goog[${NAMESPACE}/${KSA_NAME}]" \ --role="roles/iam.workloadIdentityUser" \ --project ${PROJECT_ID} - Remove the GSA’s
roles/run.invokerbinding on the Cloud Run service.gcloud run services remove-iam-policy-binding ${CLOUD_RUN_SERVICE} \ --member="serviceAccount:${GSA_EMAIL}" \ --role="roles/run.invoker" \ --region ${REGION} --project ${PROJECT_ID} - Optional: Delete the GSA.
gcloud iam service-accounts delete ${GSA_EMAIL} --project ${PROJECT_ID}
- Remove the GSA annotation from the
If you no longer need the Cloud Run service, delete it.
gcloud run services delete ${CLOUD_RUN_SERVICE} --region ${REGION} --project ${PROJECT_ID}Optional: Revert the Workload Identity Federation setup from Set up your Google Cloud environment. Only do this if you don’t need Workload Identity for any other workloads in the cluster, because re-enabling it later is slow.
Run the node-pool revert to trigger a rolling node replacement (a few minutes per node).
# Reset each node pool to the pre-Workload-Identity metadata server. for POOL_NAME in ${POOL_NAMES}; do gcloud container node-pools update ${POOL_NAME} --cluster=${CLUSTER_NAME} --region=${REGION} --project=${PROJECT_ID} \ --workload-metadata=GCE_METADATA doneRun the cluster revert to update the control plane (10–20 minutes or more).
# Disable Workload Identity on the cluster. gcloud container clusters update ${CLUSTER_NAME} --region ${REGION} --project ${PROJECT_ID} --workload-pool=""