1. Introduction
Overview
Cloud Run functions is a lightweight compute solution for developers to create single-purpose, stand-alone functions that can be triggered using HTTPS or respond to CloudEvents without needing to manage a server or runtime environment. Learn more about Cloud Run functions in our blog post.
There are two main approaches to controlling invocations to Cloud Run functions: securing access based on identity and securing access using network-based access controls. This codelab focuses on the first approach and walks you through 3 scenarios for securing access based on identity to invoke a function:
- Use your gcloud identity token to invoke a function for local development & testing purposes
- Impersonate a service account when developing and testing locally to use same the credentials as in production
- Use Google client libraries to handle authentication to Google Cloud APIs, e.g. when a service needs to invoke a function
What you'll learn
- How to configure authentication on a Cloud Run function and verify authentication has been properly configured
- Invoke an authenticated function from a local development environment by providing the token for your gcloud identity
- How to create a service account and grant it the appropriate role to invoke a function
- How to impersonate a service from a local development environment that has the appropriate roles for invoking a function
2. Setup and Requirements
Prerequisites
- You are logged into the Cloud Console
- You have previously deployed an HTTP triggered Cloud Run function. See quickstart example.
- (optional) For the 3rd scenario, this codelab uses Node.js and npm as the example, but you can use any runtime that's supported by the Google Auth client libraries.
Activate Cloud Shell
- From the Cloud Console, click Activate Cloud Shell .
If this is your first time starting Cloud Shell, you're presented with an intermediate screen describing what it is. If you were presented with an intermediate screen, click Continue.
It should only take a few moments to provision and connect to Cloud Shell.
This virtual machine is loaded with all the development tools needed. It offers a persistent 5 GB home directory and runs in Google Cloud, greatly enhancing network performance and authentication. Much, if not all, of your work in this codelab can be done with a browser.
Once connected to Cloud Shell, you should see that you are authenticated and that the project is set to your project ID.
- Run the following command in Cloud Shell to confirm that you are authenticated:
gcloud auth list
Command output
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- Run the following command in Cloud Shell to confirm that the gcloud command knows about your project:
gcloud config list project
Command output
[core] project = <PROJECT_ID>
If it is not, you can set it with this command:
gcloud config set project <PROJECT_ID>
Command output
Updated property [core/project].
3. Create and test an authenticated Cloud Run function
Requiring authentication means that the principle invoking the function must have the Cloud Run Invoker role; otherwise, the function will return a 403 Forbidden error. This codelab will show how to grant the appropriate Invoker roles to a principle.
Setup local environment variables for simplified gcloud commands
First, you will create a few environment variables to improve the readability of the gcloud
commands used in this codelab.
REGION=us-central1 PROJECT_ID=$(gcloud config get-value project)
Create the source code for the function
Although this codelab uses Node.js, you can use any runtime that's supported by the Google Auth client libraries.
First, create a directory and cd into that directory.
mkdir auth-function-codelab && cd $_
Then, create the package.json file.
touch package.json echo '{ "dependencies": { "@google-cloud/functions-framework": "^3.0.0" } } ' > package.json
Next, create the index.js source file.
touch index.js echo 'const functions = require("@google-cloud/functions-framework"); functions.http("helloWorld", (req, res) => { res.send(`Hello ${req.query.name || req.body.name || "World"}!`); });' > index.js
Create the authenticated function
Here are the steps for creating an authenticated function for the nodejs20 runtime. However, you can use any runtime that's supported by the Google Auth client libraries.
FUNCTION_NAME=authenticated-function-codelab ENTRY_POINT=helloWorld
To deploy a Cloud Run function directly onto Cloud Run, run the following command:
gcloud beta run deploy $FUNCTION_NAME \ --source . \ --function helloWorld \ --region $REGION \ --no-allow-unauthenticated
and then you can save the function URL as an environment variable to use later.
FUNCTION_URL="$(gcloud run services describe $FUNCTION_NAME --region $REGION --format 'value(status.url)')"
If you prefer to deploy as a Cloud Functions 2nd gen, use the following command:
gcloud functions deploy nodejs-http-function \ --gen2 \ --runtime=nodejs20 \ --region=$REGION \ --source=. \ --entry-point=helloWorld \ --trigger-http \ --no-allow-unauthenticated
and then you can save the function URL as an environment variable to use later.
FUNCTION_URL="$(gcloud functions describe $FUNCTION_NAME --gen2 --region us-central1 --format='get(serviceConfig.uri)')"
Verify the function requires authentication by attempting to invoke as an anonymous caller
You'll invoke the function without authentication to verify that you receive an expected 403 error.
From a command line, run the following curl
command:
curl -i $FUNCTION_URL
You will see the following result:
<html><head> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <title>403 Forbidden</title> </head> <body text=#000000 bgcolor=#ffffff> <h1>Error: Forbidden</h1> <h2>Your client does not have permission to get URL <code>/</code> from this server.</h2> <h2></h2> </body></html>
Now you are ready to walk through 3 scenarios where you can invoke your Function by providing authentication.
4. Scenario 1: Use your gcloud identity token
As a developer, you'll want a way to test your function while you are developing it locally. In this section, you'll perform a quick test to verify the function is properly authenticated using your own identity.
Verify you are authenticated using gcloud
by running the following command:
gcloud auth list
You should see an asterisk next to your active identity, for example:
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
You can find more information on setting up gcloud init and gcloud auth login in the docs.
Next, invoke the Function and pass it your identity token.
curl $FUNCTION_URL -H "Authorization: bearer $(gcloud auth print-identity-token)"
Now you will see the result:
Hello World!
Troubleshooting
If you receive a 403 Forbidden error, make sure your identity has the Cloud Run Invoker role. You can use the IAM console to verify roles given to a principal.
Although using your own identity token is a quick way to test your function during development, the caller of your authenticated function will need the appropriate roles; otherwise, the caller will receive a 403 Forbidden error.
You'll want to follow the principle of least privilege by limiting the number of identities and service accounts that have roles to invoke the function. In the next scenario, you'll learn how to create a new service account and grant it the appropriate roles to invoke the function.
5. Scenario 2: Impersonate a service account
In this scenario, you'll impersonate (i.e. assume the permissions of) a service account to invoke a function when developing and testing locally. By impersonating a service account, you can test your function the same credentials as in production.
By doing this, you'll not only verify roles, but also follow the principle of least privilege by not having to grant the Cloud Function Invoker role to other identities just for local testing purposes.
For purposes of this codelab, you'll create a new service account that only has roles to invoke the function you created in this codelab.
Create a new service account
First, you'll create a couple additional environment variables to represent the service accounts used in the gcloud commands.
SERVICE_ACCOUNT_NAME="invoke-functions-codelab" SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com
Next, you'll create the service account.
gcloud iam service-accounts create $SERVICE_ACCOUNT_NAME \ --display-name="Cloud Run function Authentication codelab"
And grant the service account Cloud Run invoker role:
gcloud run services add-iam-policy-binding $FUNCTION_NAME \ --region=us-central1 \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role='roles/run.invoker'
Invoke the function by impersonating the service account
For this, you'll impersonate the newly created service account by obtaining its ID token.
Add required roles for impersonation
For impersonating a service account, your user account needs to have the Service Account Token Creator (roles/iam.serviceAccountTokenCreator) role to generate an ID token for the service account.
You can run the following commands to grant your active user account this role:
ACCOUNT_EMAIL=$(gcloud auth list --filter=status:ACTIVE --format="value(account)") gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT_ADDRESS \ --member user:$ACCOUNT_EMAIL \ --role='roles/iam.serviceAccountTokenCreator'
Use the ID token of the service account
Wait a couple of minutes for permissions to propagate. Now you can invoke the Function by passing the ID token of the service account.
curl $FUNCTION_URL -H "Authorization: bearer $(gcloud auth print-identity-token --impersonate-service-account $SERVICE_ACCOUNT_ADDRESS)"
And you will see the following:
WARNING: This command is using service account impersonation. All API calls will be executed as [invoke-functions-codelab@<project-id>.iam.gserviceaccount.com]. Hello World!
6. Scenario 3: Use Google client libraries
For this last portion of the codelab, you will run a small service locally to generate an ID token for a service account and then programmatically call the Function using the Google Auth client libraries and Application Default Credentials (ADC). You can read more about Google client libraries in the client libraries explained section of the docs.
Using ADC is especially important when you want to write and test your Function locally (e.g. on your laptop, in Cloud Shell, etc.) while interacting with other Google Cloud resources (e.g. Cloud Storage, Vision API, etc.) For this example, you'll see how to have a service invoke another Function that requires authentication. For more info on ADC and local development, see the blog post How to develop and test your Cloud Functions locally | Google Cloud Blog
Run the gcloud command to impersonate a service account
ADC automatically finds credentials based on the application environment and uses those credentials to authenticate to Google Cloud APIs. The –impersonate-service-account flag allows you to impersonate a service account by using its identity for authentication against Google Cloud APIs.
To impersonate a service account, you can run the following command:
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Now you are running gcloud commands as that service account, instead of your identity.
Create and run a service to invoke an authenticated function
Each runtime has its own Google Auth client library that you can install. This codelab walks you through creating and running a Node.js app locally.
Here are the steps for Node.js:
- Create a new directory
mkdir local-dev && cd $_
- Create a new Node.js app
npm init -y
- Install the Google Auth client library
npm install google-auth-library
- Create an
index.js
file - Retrieve the URL of your Cloud Run function, which you will add to your code in the following step.
echo $FUNCTION_URL
- Add the following code to index.js. Make sure to change the targetAudience variable to your Cloud Run function URL.
index.js
// Cloud Functions uses your function's url as the `targetAudience` value
const targetAudience = '<YOUR-CLOUD-RUN-FUNCTION-URL>';
// For Cloud Functions, endpoint(`url`) and `targetAudience` should be equal
const url = targetAudience;
const { GoogleAuth } = require('google-auth-library');
const auth = new GoogleAuth();
async function request() {
console.info(`request ${url} with target audience ${targetAudience}`);
// this call retrieves the ID token for the impersonated service account
const client = await auth.getIdTokenClient(targetAudience);
const res = await client.request({ url });
console.info(res.data);
}
request().catch(err => {
console.error(err.message);
process.exitCode = 1;
});
- Run the app
node index.js
And you should see the resulting "Hello World!"
Troubleshooting
If you see an error Permission ‘iam.serviceAccounts.getOpenIdToken' denied on resource (or it may not exist)., please wait a few minutes for the Service Account Token Creator role to propagate.
If you received the error Cannot fetch ID token in this environment, use GCE or set the GOOGLE_APPLICATION_CREDENTIALS environment variable to a service account credentials JSON file, you may have forgotten to run the command
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
7. Congratulations!
Congratulations for completing the codelab!
We recommend reviewing the documentation on how to secure Cloud Run functions.
We also recommend this blog post on local development with Cloud Run functions to learn how to develop and test your Cloud Run function in your local developer environment.
What we've covered
- How to configure authentication on a Cloud Run function and verify authentication has been properly configured
- Invoke an authenticated function from a local development environment by providing the token for your gcloud identity
- How to create a service account and grant it the appropriate role to invoke a function
- How to impersonate a service from a local development environment that has the appropriate roles for invoking a function
8. Clean up
To avoid inadvertent charges, (for example this Cloud Function is inadvertently invoked more times than your monthly Cloud Run function invokement allocation in the free tier), you can either delete the Cloud Function or delete the project you created in Step 2.
To stop impersonating the service account, you can re-login using your identity:
gcloud auth application-default login
To delete the Cloud Run function, go to the Cloud Run function Cloud Console at https://console.cloud.google.com/functions/ Make sure the project you created in step 2 is the currently selected project.
Select the my-authenticated-function you deployed earlier. Then hit Delete.
If you choose to delete the entire project, you can go to https://console.cloud.google.com/cloud-resource-manager, select the project you created in Step 2, and choose Delete. If you delete the project, you'll need to change projects in your Cloud SDK. You can view the list of all available projects by running gcloud projects list
.