Edit on GitHub
Report issue
Page history

Control access for a Cloud Run application with conditional IAM roles

Author(s): @mike-ensor ,   Published: 2020-09-14

Mike Ensor | Solutions Architect | Google

Contributed by Google employees.

This tutorial shows you how to control access to resources for a Cloud Run application using a Google service account and conditional IAM roles.

Applications that use Google Cloud services such as Pub/Sub, Cloud Storage, and Cloud SQL require authentication. Authentication and authorization are provided using Cloud Identity and Access Management (IAM) through a combination of roles and accounts. Google service accounts are often used to provide an authentication and authorization mechanism for Google Cloud resources. For more information, see the official documentation.

Authentication is often tied to a series of conditional parameters such as time of day or day of the week to enhance security measures, preventing applications from accessing resources outside of compliance or governance rules. Within Google Cloud, this concept is implemented as IAM conditional role bindings. Managed Cloud Run has an advanced option allowing a Google service account to act as the user authenticating with other Google Cloud resources such as Cloud Storage.

This tutorial demonstrates how to set up service accounts with conditional IAM role bindings on Cloud Run using a Cloud Storage bucket, as illustrated in the following diagram.

Architectural overview

Objectives

  1. Create a simple application that lists the contents of a Cloud Storage bucket.
  2. Create a service account to manage the application's Google service interactions.
  3. Bind the service account with a conditional IAM role, storage.objectViewer.

Costs

The Cloud Run portion of this tutorial fits into the always free tier of Google Cloud if implemented using the selected regions with the suggested application. For more information, see Cloud Run pricing criteria.

You can use the pricing calculator to estimate your costs.

Cloud Storage usage for this tutorial fits within the always free limits (below 5GB of regional storage) by storing small text files.

Prerequisites and setup

This tutorial requires a Google Cloud project and an associated billing account. Though the costs are within the always free tier, a billing account is required. We recommend that you create a new project for this tutorial, so that cleaning up the tutorial's resources at the end is easiest.

You execute commands in this tutorial in Cloud Shell. If you prefer to execute the commands on your local machine, you must install the Cloud SDK and gsutil.

The gcloud user must have the following permissions for this tutorial:

  • roles/editor or roles/owner
  • roles/iam.serviceAccountAdmin (enables the user to create and manage the life cycle of Google service accounts)
  • roles/storage.admin (enables the user to create and manage the lifecycle of Cloud Storage buckets)
  • roles/cloudbuild.builds.editor (enables the user to create and manage the life cycle of Cloud Build instances)

Solution

Summary of the workflow

  1. Set variables and enable APIs and services.
  2. Create a protected Cloud Storage bucket and add files to it.
  3. Create an application container to access the Cloud Storage bucket.
  4. Create a Google service account.
  5. Create a managed Cloud Run instance using the service account.
  6. Set up conditional IAM permissions.

Set environment variables

  1. Set the project ID of the Google Cloud project where the managed Cloud Run instance is to be deployed to:

    export PROJECT_ID=$(gcloud config get-value core/project)
    
  2. Set the region to deploy the managed Cloud Run application into:

    export REGION=us-west1
    

    Check the list of regions in the always free tier to ensure that costs are included.

  3. Set the name of the Google service account to be created and used for the Cloud Run instance:

    export GSA_NAME=bucket-list-gsa
    

    Use alphanumeric characters and dashes, for a name between 5 and 20 characters.

Enable APIs and services

Run the following command to enable the necessary APIs and services:

gcloud services enable \
    cloudbuild.googleapis.com \
    storage-component.googleapis.com \
    run.googleapis.com \
    containerregistry.googleapis.com

Create a Cloud Storage bucket and add files to it

  1. Generate a random lowercase alphanumeric suffix:

    export SUFFIX=$(head -3 /dev/urandom | tr -cd '[:alnum:]' | cut -c -5 | awk '{print tolower($0)}')
    
  2. Set a variable to contain the name of the bucket:

    export BUCKET="cloud-run-tutorial-bucket-${SUFFIX}"
    
  3. Create the bucket:

    gsutil mb gs://${BUCKET}
    
  4. Add text documents to the bucket:

    for i in {1..5}; do echo "task $i" > item-$i.txt; done
    gsutil cp *.txt gs://${BUCKET}
    
  5. List the bucket contents to verify that they were created:

    gsutil ls gs://${BUCKET}
    

The output should look similar to the following:

gs://cloud-run-tutorial-bucket-*****/item-1.txt
gs://cloud-run-tutorial-bucket-*****/item-2.txt
gs://cloud-run-tutorial-bucket-*****/item-3.txt
gs://cloud-run-tutorial-bucket-*****/item-4.txt
gs://cloud-run-tutorial-bucket-*****/item-5.txt

Create and deploy an application container

In this section, you build and push a simple app written in the Go programming language to the project's private Google Container Registry. The app used for this tutorial (Cloud Run Bucket App) lists the contents of a Cloud Storage bucket.

  1. Clone the repository:

    git clone https://gitlab.com/mike-ensor/cloud-run-bucket-app
    
  2. Build and push the image:

    gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID}
    
  3. Verify that the image was pushed:

    gcloud container images list --filter="name:(*cloud-run-bucket-app)" --repository=gcr.io/${PROJECT_ID}
    

    If the output says Listed 0 items, then the build was not successful.

Create and set up a Google service account

Creating a Google service account for an application allows granular and specific access to Google-managed resources, thus reducing the security threat.

gcloud iam service-accounts create ${GSA_NAME} \
    --description="Service account for Cloud Storage bucket listing app" \
    --display-name="${GSA_NAME}"

The output should be similar to the following:

Created service account [bucket-list-gsa].

Create a managed Cloud Run service with the Google service account

Run the following commands to create a Cloud Run service with a Google service account:

  1. Set the image name:

    export IMAGE_NAME=$(gcloud container images list --filter="name:(*cloud-run-bucket-app)" --repository=gcr.io/${PROJECT_ID} --format="value(name)")
    
  2. Deploy the app:

    gcloud run deploy ${SERVICE} \
        --platform managed \
        --region ${REGION} \
        --allow-unauthenticated \
        --image ${IMAGE_NAME} \
        --service-account ${GSA_NAME}
    

Verify that the app has been deployed

  1. Open a browser and go to the URL given in the output.

  2. Get the bucket name:

    echo $BUCKET
    
  3. Add the ?bucket=[YOUR_BUCKET_NAME] query parameter to the end of the URL: https://${PROJECT_ID}...run.app/?bucket=[YOUR_BUCKET_NAME]

    This should display an error indicating a 403 (access forbidden) error.

  4. Keep this browser window open, because you will use it in a later step to verify conditional IAM role bindings.

Set up conditional IAM role bindings

In this section, you give the Google service account permission to view the bucket contents.

  1. Set time zone to UTC for date calculations:

    export TIME_ZONE=GMT/UTC
    
  2. Set a time interval in seconds equal to 5 minutes:

    export MINUTES_IN_FUTURE=$((60*5))
    
  3. Create a human-readable description and title for the condition:

    export DESCRIPTION="Example conditional that is true until ${MINUTES_IN_FUTURE} minutes from the time of execution"
    export TITLE="Expire In ${MINUTES_IN_FUTURE} minutes"
    
  4. Create a timestamp of the current time:

    export TIMESTAMP=$(TZ=${TIME_ZONE} date +"%FT%T.00Z")
    
  5. Build a condition expression that tests whether (time.now < TIMESTAMP+5 minutes):

    export EXPRESSION="request.time < timestamp(\"${TIMESTAMP}\") + duration(\"${MINUTES_IN_FUTURE}s\")"
    
  6. Validate the condition:

    gcloud alpha iam policies lint-condition --expression="${EXPRESSION}" --title="${TITLE}" --description="${DESCRIPTION}"
    
  7. Bind the role and condition to the Google service account:

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
        --role="roles/storage.objectViewer" \
        --condition="expression=${EXPRESSION},description=${DESCRIPTION},title=${TITLE}"
    

For more information, see Managing conditional role bindings

Verify

IAM conditions can take a few minutes to take effect. If permission isn't granted immediately, wait a minute and try again.

  1. Using the same browser window that you used to verify that the app was deployed, refresh the page to see the contents.

  2. Wait for 5 minutes and refresh page to see the 403 error code returned.

Clean up

To avoid incurring charges to your Google Cloud account, remove the resources used in this tutorial:

  1. Remove the Cloud Run service:

    gcloud run services delete ${SERVICE}
    
  2. Remove the Cloud Storage bucket:

    gsutil rm -r gs://${BUCKET}
    
  3. Remove the service account:

    gcloud iam service-accounts delete ${GSA_NAME}
    
  4. Remove the app image:

    gcloud container images delete ${IMAGE_NAME} --force-delete-tags --quiet
    

Submit a tutorial

Share step-by-step guides

Submit a tutorial

Request a tutorial

Ask for community help

Submit a request

View tutorials

Search Google Cloud tutorials

View tutorials

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see our Site Policies. Java is a registered trademark of Oracle and/or its affiliates.