Skip to content
You are viewing the latest documentation for Solo Enterprise for kgateway. To access the documentation for older versions, use the version switcher.

GCP Cloud Run

Page as Markdown

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

  1. Create or select an existing GKE cluster.

  2. Follow the Get started guide to install Solo Enterprise for kgateway.

  3. Set up a gateway proxy with an HTTP listener.

  4. 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=http
  5. Get 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_ADDRESS
    kubectl port-forward deployment/http -n ${NAMESPACE} 8080:8080

Set up your Google Cloud environment

  1. 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.

      RoleWhy it’s needed
      roles/serviceusage.serviceUsageAdminEnable the run.googleapis.com, iam.googleapis.com, and iamcredentials.googleapis.com APIs 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:

      RoleWhy it’s needed
      roles/run.adminDeploy a Cloud Run service and grant the service-scoped roles/run.invoker IAM 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.workloadIdentityUser binding on it.
      roles/iam.serviceAccountUserDeploy a Cloud Run service that runs as a service account (actAs permission 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 Backend and HTTPRoute resources in the kgateway-system namespace. 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.

  2. 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.goog
    

    If 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.

    1. 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.goog
    2. Node 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.

  1. 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 hello sample container with authentication required. The gateway proxy automatically injects the GCP token into the Authorization header and passes that along to the Cloud Run instance to be successfully authenticated. If you deploy the instance with --allow-unauthenticated instead, 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-unauthenticated

    Example 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.app
    
  2. Get 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 gcloud command 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_HOST

    Example output:

    helloworld-<project-hash>.<region>.run.app
    
  3. Verify that the service is reachable and requires authentication. Send an unauthenticated request to the hostname. A 403 response confirms that the service is private and requires a valid identity token. A 200 response 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
    ...
    
  4. In your cluster, link the http Kubernetes 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.

  5. 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 same http Kubernetes 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/email

    If you see the node’s default service account or an error, Workload Identity is not wired. Check both bindings:

    1. 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>.

    2. Verify that the gateway proxy pod’s node pool has GKE_METADATA set.

      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)'
      done

      Each line must print GKE_METADATA. If you see GCE_METADATA or 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.

  1. 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}
    EOF

    Example output:

    backend.gateway.kgateway.dev/gcp-backend created
    

    The gcp section supports the following fields:

    FieldRequiredDescription
    hostYesThe hostname of the GCP service. Used for SNI and as the target address.
    audienceNoThe GCP service account audience URL. Defaults to https://<host> when omitted. Used by the GCP authentication filter to request the appropriate identity token.
  2. Create an HTTPRoute that matches incoming traffic on the /gcp path and routes it to the Cloud Run Backend. The GCP Backend automatically rewrites the HTTP Host header on outbound requests to match the Backend’s host field, 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
    EOF

    Example output:

    httproute.gateway.networking.k8s.io/gcp-route created
    
  3. Send a request to verify that Solo Enterprise for kgateway correctly routes traffic to your Cloud Run service.

    curl -v $INGRESS_GW_ADDRESS:8080/gcp
    curl -v localhost:8080/gcp

    Look for a 200 HTTP response code and the response body from your Cloud Run service. The default hello sample 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 403 with www-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.invoker on 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 host does not match the Cloud Run hostname.

Cleanup

You can optionally remove the resources that you set up as part of this guide.

  1. Delete the HTTPRoute and Backend resources.

    kubectl delete httproute gcp-route -n ${NAMESPACE}
    kubectl delete backend gcp-backend -n ${NAMESPACE}
  2. If you don’t need Workload Identity for any other workloads, remove the in-cluster annotation and the GCP-side bindings.

    1. Remove the GSA annotation from the http Kubernetes service account. The trailing - removes the annotation.
      kubectl annotate serviceaccount ${KSA_NAME} -n ${NAMESPACE} iam.gke.io/gcp-service-account-
    2. 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}
    3. Remove the GSA’s roles/run.invoker binding 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}
    4. Optional: Delete the GSA.
      gcloud iam service-accounts delete ${GSA_EMAIL} --project ${PROJECT_ID}
  3. If you no longer need the Cloud Run service, delete it.

    gcloud run services delete ${CLOUD_RUN_SERVICE} --region ${REGION} --project ${PROJECT_ID}
  4. 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.

    1. 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
      done
    2. Run 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=""
Was this page helpful?