Authenticating Cloud Run on GKE end users using Istio and Identity Platform

This tutorial shows how to authenticate end users to applications deployed to Cloud Run on GKE using Istio authentication policies and Identity Platform. Using Istio to authenticate means that authentication logic doesn't need to be part of the application code. This separation lets different teams be responsible for application code and authentication policy, and authentication policies can apply across multiple applications or services.

Introduction

Cloud Run on GKE provides a developer-focused experience for deploying and serving applications and functions running on GKE. It offers development teams a serverless experience, supporting autoscaling based on demand, routing and traffic management for blue-green deployments, and more. Cloud Run on GKE builds on the open source projects Istio and Knative, and it integrates with Google Cloud Platform (GCP) products such as Stackdriver.

Istio can authenticate incoming requests by validating JSON Web Tokens (JWT) according to authentication policies. The authentication policies can apply to all services in a namespace, or to specific named services. A policy can include and exclude specific HTTP request paths, for example, to allow unauthenticated access to public website assets and health check endpoints. In this tutorial, the Istio Ingress Gateway enforces the authentication policy.

The following diagram shows the authentication flow that this tutorial is based on.

Istio authenticates incoming requests

This tutorial uses Identity Platform to sign in end users, but you can adapt the solution to other providers that support OpenID Connect, such as Google Sign-In, Firebase Authentication, third-party offerings such as Auth0, Gluu, Okta, Ping Identity, or your own deployment of an OpenID Connect implementation.

Objectives

  • Create a GKE cluster with the Cloud Run add-on.
  • Set up Identity Platform.
  • Deploy a sample application that consists of a public frontend and backend API.
  • Add an authentication policy for the backend API.
  • Verify authentication.

Costs

This tutorial uses the following billable components of Google Cloud Platform:

You can use the pricing calculator to generate a cost estimate based on your projected usage. New GCP users might be eligible for a free trial.

When you finish this tutorial, you can avoid continued billing by deleting the resources you created. For more information, see Cleaning up.

Before you begin

  1. Sign in to your Google Account, or if you don't have one, sign up for a new account.
  2. In the GCP Console, go to the project selector page.

    Go to the project selector page

  3. Select or create a GCP project.

  4. Make sure that billing is enabled for your Google Cloud Platform project.

    Learn how to enable billing

  5. Enable the Cloud Build, Cloud Run, Container Analysis, and Google Kubernetes Engine APIs, and the Cloud APIs meta service.

    ENABLE THE APIS

Initializing the environment

In this section, you set environment variables and gcloud defaults that you use later in the tutorial.

  1. In the GCP Console, from the Select a project drop-down, select the project you want to use.

  2. Open Cloud Shell:

    GO TO Cloud Shell

    You use Cloud Shell to run all the commands in this tutorial.

  3. Define environment variables and gcloud defaults for the Compute Engine zone and GKE cluster name you want to use for this tutorial:

    ZONE=us-central1-c
    CLUSTER=cloud-run-gke-auth-tutorial
    
    gcloud config set compute/zone $ZONE
    gcloud config set run/cluster $CLUSTER
    gcloud config set run/cluster_location $ZONE
    

    You can change the zone and the cluster name to suit your needs.

Create GKE cluster with Cloud Run enabled

  • Create a GKE cluster with the Cloud Run and Istio add-ons:

    gcloud beta container clusters create $CLUSTER \
        --addons HttpLoadBalancing,Istio,CloudRun \
        --cluster-version 1.13 \
        --enable-ip-alias \
        --enable-stackdriver-kubernetes \
        --machine-type n1-standard-4
    

Find the public IP address

Cloud Run on GKE exposes external services on the public IP address of the Istio Ingress Gateway.

  1. Check the status of creating an external IP address for the istio-ilbgateway Kubernetes Service:

    kubectl get services istio-ilbgateway -n istio-system --watch
    

    Wait until the EXTERNAL-IP value changes from <pending> to an IP address. If you get a NotFound error, wait a minute and run the command again. To stop waiting, press Control+C.

  2. Create an environment variable to store the public IP address of the Istio Ingress Gateway:

    export EXTERNAL_IP=$(kubectl get services istio-ingressgateway \
        --namespace istio-system \
        --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
  3. Display the public IP address of the Istio Ingress Gateway. You need this address later.

    echo $EXTERNAL_IP
    

Note: In this tutorial, you access the services using an IP address, and using unencrypted HTTP. For a production setup, we recommend that you do both of the following:

  • Create a DNS A record for your domain name and set its value to the public IP address of the Istio Ingress Gateway. If you do so, replace $EXTERNAL_IP in the rest of this tutorial with the domain name of the DNS A record. If you use Cloud DNS, follow the Quickstart guide. If you use another provider, refer to its documentation.
  • Enable HTTPS for Cloud Run on GKE services.

Set up Identity Platform

  1. Leave Cloud Shell open and visit GCP Marketplace to enable Identity Platform in a new web browser window.

    Go to Identity Platform on GCP Marketplace

  2. From the Select a project drop-down, select the GCP project where you want to set up Identity Platform. You can set up the GKE cluster and Identity Platform in separate GCP projects. For simplicity, use the same project in this tutorial.

  3. Click Enable Identity Platform.

    You are now on the Identity Platform > Providers page in the GCP Console.

  4. On the Providers page, click Add a provider.

  5. From the Select a provider drop-down, scroll down and select Email / Password. For your own application, you can choose the providers you want to enable.

  6. Ensure that Enabled is selected.

  7. Clear Allow passwordless login.

  8. Click Save.

  9. Navigate to the Identity Platform > Settings page.

  10. Click Add domain. This opens the Add authorized domain dialog.

  11. In the Domain box, enter the public IP address of the Istio Ingress Gateway from the previous section ($EXTERNAL_IP).

  12. Click Add. This closes the dialog. The IP address you entered is in the Authorized Domains table.

  13. On the Identity Platform > Settings page, click Save.

Create a test user

  1. In the GCP Console, navigate to the Identity Platform > Users page.
  2. Click Add user to add a test user for this tutorial. This opens the Add user dialog.
  3. In the Email box, enter the email address of an end user. For testing, this email address doesn't have to be real. For this tutorial, use user@example.com.
  4. In the Password box, enter a password for the test user. Remember this password, because you need it later.
  5. Click Add to finish adding the user. This closes the dialog and takes you back to the Identity Platform > Users page.

Create a sample application

Deploy a sample application that has two services. One service is a public frontend user interface; the other service is a backend API.

  1. In the Identity Platform > Users page, click the Application setup details link on the right side of the window. This opens the Configure your application dialog.

  2. Highlight and copy the value of apiKey to your clipboard (Control+C on Chrome OS/Linux/Windows, Cmd+C on macOS).

  3. Click Close to close the Configure your application dialog.

  4. In Cloud Shell, create an environment variable to store apiKey, where api-key is the apiKey from the Configure your application dialog:

    export AUTH_APIKEY=api-key
    
  5. Create an environment variable for authDomain:

    export AUTH_DOMAIN=$GOOGLE_CLOUD_PROJECT.firebaseapp.com
    
  6. Clone the Cloud Run samples repository from GitHub:

    git clone https://github.com/GoogleCloudPlatform/cloud-run-samples.git
    
  7. Switch to the directory containing the files for this tutorial:

    cd cloud-run-samples/identity-platform/gke
    
  8. Substitute the Identity Platform variables in the frontend JavaScript file:

    envsubst < frontend/index.template.js > frontend/index.js
    

Deploy the sample application

  1. Use Cloud Build to create two container images for the sample application, one for the frontend and one for the backend:

    gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-frontend frontend
    
    gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-backend backend
    

    Cloud Build stores the images in Container Registry.

  2. In the GKE cluster, create two namespaces called public and api:

    kubectl create namespace public
    
    kubectl create namespace api
    
  3. Deploy the frontend container image to Cloud Run on GKE as a service in the public namespace:

    gcloud beta run deploy frontend \
        --namespace public \
        --image gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-frontend \
        --platform gke
    
  4. Deploy the backend container image to Cloud Run on GKE as a service in the api namespace:

    gcloud beta run deploy backend \
        --namespace api \
        --image gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-backend \
        --platform gke
    
  5. Create an Istio virtual service that routes requests by URI path:

    kubectl apply -f istio/virtualservice.yaml
    

    This virtual service routes requests where the URI path starts with /api/ to the backend API, and routes all other requests to the frontend user interface.

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: cloud-run-gke-auth
    spec:
      gateways:
      - knative-ingress-gateway.knative-serving.svc.cluster.local
      hosts:
      - '*'
      http:
      - match:
        - uri:
            prefix: /api/
        rewrite:
          authority: backend.api.svc.cluster.local
        route:
        - destination:
            host: istio-ingressgateway.istio-system.svc.cluster.local
      - match:
        - uri:
            prefix: /
        rewrite:
          authority: frontend.public.svc.cluster.local
        route:
        - destination:
            host: istio-ingressgateway.istio-system.svc.cluster.local
    # [ENV run_gke_auth_identityplatform_virtualservice]
    
  6. Verify that unauthenticated requests to the backend API succeed:

    curl -si $EXTERNAL_IP/api/secure.json | head -n1
    

    If the output is not HTTP/1.1 200 OK, wait a minute and try again.

Add an Istio authentication policy

  1. Create an Istio authentication policy:

    envsubst < istio/authenticationpolicy.template.yaml | kubectl apply -f -
    
    apiVersion: authentication.istio.io/v1alpha1
    kind: Policy
    metadata:
      name: api-origin-auth
      namespace: istio-system
    spec:
      targets:
      - name: istio-ingressgateway
        ports:
        - number: 80
        - number: 443
      origins:
      - jwt:
          issuer: "https://securetoken.google.com/$GOOGLE_CLOUD_PROJECT"
          audiences:
          - "$GOOGLE_CLOUD_PROJECT"
          jwksUri: "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"
          trigger_rules:
          - excluded_paths:
            - exact: /api/healthz
            included_paths:
            - prefix: /api/
      principalBinding: USE_ORIGIN

    This policy authenticates requests where the URI path starts with /api/, except the path /api/healthz. Because of the routing rules in the Istio virtual service deployed in the previous section, this policy authenticates requests to the backend API.

  2. It can take a moment for the policy to take effect. Run the following command and wait until you see HTTP/1.1 401 Unauthorized printed to the console:

    while sleep 2; do
      curl -si $EXTERNAL_IP/api/secure.json | head -n1
    done
    

    Initially, you might see the output alternate between HTTP/1.1 200 OK and HTTP/1.1 401 Unauthorized. This is due to eventual consistency in Istio and Envoy Proxy.

    When you see only HTTP/1.1 401 Unauthorized being printed to the console, press Control+C to stop waiting.

Test the solution

  1. Open a browser window to the address http://$EXTERNAL_IP/api/secure.json, where $EXTERNAL_IP is the public IP address of the Istio Ingress Gateway you found in the section Find the public IP address.

    This is a request directly to the backend API. The browser window shows Origin authentication failed because the request didn't include credentials that satisfied the Istio authentication policy.

  2. Open a browser window to the address $EXTERNAL_IP. You should see a sign-in form.

  3. Sign in as the test user you created in the section Create a test user.

    The page shows the test user email address and the text The secret message is: Hello World. It can take a moment for the message to appear.

    The browser fetches the message with an HTTP request to the backend API (/api/secure.json), using a token obtained from Identity Platform when signing in. Inspect the file frontend/index.js to view the implementation, and refer to the FirebaseUI library for further documentation.

Troubleshooting

If you run into problems with this tutorial, we recommend that you review these documents:

Cleaning up

To avoid incurring charges to your GCP account for the resources used in this tutorial:

  1. In the GCP Console, go to the Projects page.

    Go to the Projects page

  2. In the project list, select the project you want to delete and click Delete .
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Delete the resources

If you want to keep the GCP project you used in this tutorial, delete the individual resources:

  1. Delete the GKE cluster:

    gcloud container clusters delete $CLUSTER --async --quiet
    
  2. Delete the sample application container images in Container Registry:

    gcloud container images delete gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-frontend \
        --force-delete-tags --quiet
    
    gcloud container images delete gcr.io/$GOOGLE_CLOUD_PROJECT/cloud-run-gke-auth-backend \
        --force-delete-tags --quiet
    
  3. Delete the Email / Password identity provider in Identity Platform.

    1. In the GCP Console, go to the Identity Platform > Providers page:

      GO TO THE PROVIDERS PAGE

    2. Find the Email / Password identity provider in the table of providers and click .

    3. In the dialog that appears, click Delete to confirm.

  4. Delete the test user.

    1. Go to the Identity Platform > Users page:

      GO TO THE USERS PAGE

    2. Find user@example.com in the table of users and click .

    3. In the dialog that appears, click Delete to confirm.

  5. Delete the authorized domain.

    1. Go to the Identity Platform > Settings page:

      GO TO THE SETTINGS PAGE

    2. In the table of authorized domains, find the IP address you added in the section Set up Identity Platform ($EXTERNAL_IP) and click .

    3. Click Save.

What's next

Оцените, насколько информация на этой странице была вам полезна:

Оставить отзыв о...

Текущей странице