了解如何调用经过身份验证的 Cloud Functions 函数

1. 简介

概览

Cloud Functions 是一个轻量级计算解决方案,可供开发者创建单一用途的独立函数,这些函数可使用 HTTPS 触发或响应 CloudEvents,而无需管理服务器或运行时环境。

有两种主要方法可以控制对 Cloud Functions 的调用:基于身份保护访问和使用基于网络的访问权限控制保护访问。此 Codelab 重点介绍第一种方法,并引导您了解基于身份以调用函数时保护访问权限的 3 种场景:

  1. 使用您的 gcloud 身份令牌调用函数以进行本地开发和测试目的
  2. 在本地开发和测试时模拟服务账号,以使用生产环境中的凭据
  3. 使用 Google 客户端库处理对 Google Cloud API 的身份验证,例如当服务需要调用函数时

学习内容

  • 如何对 Cloud Functions 函数配置身份验证,并验证是否已正确配置身份验证
  • 通过为您的 gcloud 身份提供令牌,从本地开发环境调用经过身份验证的函数
  • 如何创建服务账号并向其授予调用函数的适当角色
  • 如何从具有调用函数的适当角色的本地开发环境中模拟服务

2. 设置和要求

前提条件

  • 您已登录到 Cloud 控制台
  • 您之前部署了 HTTP 触发的第 2 代 Cloud Functions 函数
  • (可选)对于第 3 种场景,此 Codelab 以 Node.jsnpm 为例,但您可以使用 Google Auth 客户端库支持的任何运行时。

激活 Cloud Shell

  1. 在 Cloud Console 中,点击激活 Cloud Shell853e55310c205094

55efc1aaa7a4d3ad.png

如果这是您第一次启动 Cloud Shell,系统会显示一个中间屏幕,说明它是什么。如果您看到中间屏幕,请点击继续

9c92662c6a846a5c

预配和连接到 Cloud Shell 只需花几分钟时间。

9f0e51b578fecce5

这个虚拟机装有所需的所有开发工具。它提供了一个持久的 5 GB 主目录,并在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。您在此 Codelab 中的大部分(即使不是全部)工作都可以通过浏览器完成。

在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设为您的项目 ID。

  1. 在 Cloud Shell 中运行以下命令以确认您已通过身份验证:
gcloud auth list

命令输出

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. 在 Cloud Shell 中运行以下命令,以确认 gcloud 命令了解您的项目:
gcloud config list project

命令输出

[core]
project = <PROJECT_ID>

如果不是上述结果,您可以使用以下命令进行设置:

gcloud config set project <PROJECT_ID>

命令输出

Updated property [core/project].

3. 创建和测试经过身份验证的 Cloud Functions 函数

此 Codelab 的相关说明与 Cloud Functions 控制台快速入门中的相同,但有一个值得注意的例外情况:您的函数需要进行身份验证。

要求进行身份验证意味着调用函数的原则必须具有 Cloud Functions Invoker(以及针对第 2 代的 Cloud Run Invoker)角色;否则,该函数将返回“403 Forbidden”错误。此 Codelab 将展示如何向原则授予适当的 Invoker 角色

创建经过身份验证的函数

使用 Cloud 控制台的步骤如下:

  1. 转到 Cloud Functions 概览页面,然后点击创建函数
  2. 环境选项下,选择第 2 代
  3. 将该函数命名为 my-authenticated-function
  4. 将“身份验证”字段中的默认值保留为需要身份验证

936eee0d5930d12b

  1. 点击下一步
  2. 在此 Codelab 中,您可以选择任意语言
  3. 然后点击部署

部署您的函数大约需要 1 分钟。

为简化的 gcloud 命令设置本地环境变量

首先,您要创建一些环境变量,以提高此 Codelab 中使用的 gcloud 命令的可读性。

您需要为函数指定区域。此示例使用 us-central1

REGION="us-central1"

然后,您可以将函数网址保存为环境变量供日后使用。

PROJECT_ID=$(gcloud config get-value project)
FUNCTION_URL="$(gcloud functions describe my-authenticated-function --gen2 --region us-central1 --format='get(serviceConfig.uri)')"

尝试以匿名调用者身份调用该函数,验证该函数是否需要身份验证

您将在不进行身份验证的情况下调用该函数,以验证是否会收到预期的 403 错误。

从命令行运行以下 curl 命令:

curl $FUNCTION_URL

您将看到以下结果:

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

现在,您可以开始了解可以通过提供身份验证来调用函数的 3 种场景。

4. 场景 1:使用您的 gcloud 身份令牌

作为开发者,您需要一种方法来在本地开发函数时对其进行测试。在本部分中,您将执行一项快速测试,以验证使用您自己的身份对该函数进行正确身份验证。

运行以下命令,验证您是否已使用 gcloud 进行了身份验证:

gcloud auth list

您应该会在您的活跃身份旁边看到一个星号,例如:

Credentialed Accounts
ACTIVE  ACCOUNT

*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`

确认您使用的是正确的身份后,您需要将账号电子邮件地址保存到环境变量中。

ACCOUNT_EMAIL=$(gcloud auth list --filter=status:ACTIVE --format="value(account)")

如需详细了解如何设置 gcloud initgcloud auth login,请参阅相关文档。

接下来,调用函数并向其传递您的身份令牌。

curl $FUNCTION_URL -H "Authorization: bearer $(gcloud auth print-identity-token)"

现在,您将看到结果:

Hello World!

问题排查

如果您收到“403 Forbidden”错误,请确保您的身份具有第 2 代函数的 Cloud Functions Invoker 角色Cloud Run Invoker 角色。您可以使用 IAM 控制台验证提供给主账号的角色。

虽然使用您自己的身份令牌是在开发期间测试函数的快速方法,但经过身份验证的函数的调用方需要适当的角色;否则,调用方将收到“403 Forbidden”错误。

您需要限制拥有角色以调用该函数的身份和服务账号的数量,以遵循最小权限原则

创建新的服务账号并向其授予必要的角色。

5. 场景 2:模拟服务账号

在这种情况下,您将模拟(即假定拥有)服务账号的权限,以便在本地开发和测试时调用函数。通过模拟服务账号,您可以使用在生产环境中所用的凭据对函数进行测试。

这样一来,您不仅可以验证角色,还可以遵循最小权限原则,因为您不必仅仅出于本地测试目的向其他身份授予 Cloud Functions Invoker 角色。

在此 Codelab 中,您将创建一个新的服务账号,该账号仅具有调用您在此 Codelab 中创建的函数的角色。

创建新服务账号

首先,您需要创建另外几个环境变量,用于表示 gcloud 命令中使用的服务账号。

SERVICE_ACCOUNT_NAME="invoke-functions-codelab"
SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com

接下来,您将创建服务账号。

gcloud iam service-accounts create $SERVICE_ACCOUNT_NAME \
  --display-name="Cloud Function Authentication codelab"

并向服务账号授予 Cloud Functions invoker 角色

gcloud functions add-iam-policy-binding my-authenticated-function \
  --region=us-central1 --gen2 \
  --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
  --role='roles/cloudfunctions.invoker'

通过模拟服务账号来调用函数

为此,您将通过获取新创建的服务账号的 ID 令牌来模拟该服务账号。

添加模拟所需的角色

要模拟服务账号,您的用户账号需要具有 Service Account Token Creator (roles/iam.serviceAccountTokenCreator) 角色,才能为服务账号生成 ID 令牌。

gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT_ADDRESS  \
  --member user:$ACCOUNT_EMAIL \
  --role='roles/iam.serviceAccountTokenCreator'

使用服务账号的 ID 令牌

现在,您可以通过传递服务账号的 ID 令牌来调用该函数。

curl $FUNCTION_URL -H "Authorization: bearer $(gcloud auth print-identity-token --impersonate-service-account $SERVICE_ACCOUNT_ADDRESS)" 

您将看到以下内容:

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. 场景 3:使用 Google 客户端库

对于此 Codelab 的最后一部分,您将在本地运行一项小型服务,以便为服务账号生成 ID 令牌,然后使用 Google Auth 客户端库应用默认凭据 (ADC) 以编程方式调用该函数。要详细了解 Google 客户端库,请参阅文档的客户端库说明部分

如果您想在本地编写和测试函数(例如在笔记本电脑上、在 Cloud Shell 中等),同时又需要与其他 Google Cloud 资源(例如 Cloud Storage、Vision API 等)交互,使用 ADC 就显得尤为重要。在此示例中,您将了解如何让服务调用另一个需要身份验证的函数。如需详细了解 ADC 和本地开发,请参阅博文如何在本地开发和测试您的 Cloud Functions 函数 |Google Cloud 博客

运行 gcloud 命令以模拟服务账号

ADC 会根据应用环境自动查找凭据,并使用这些凭据向 Google Cloud API 进行身份验证。借助 –impersonate-service-account 标志,您可以使用服务账号的身份来模拟针对 Google Cloud API 的身份验证。

如需模拟服务账号,您可以运行以下命令:

gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS

现在,您是以该服务账号(而非您的身份)身份运行 gcloud 命令。

创建并运行服务以调用经过身份验证的函数

每个运行时都有自己的 Google Auth 客户端库,您可以安装该库。此 Codelab 将指导您在本地创建和运行 Node.js 应用。

以下是适用于 Node.js 的步骤:

  1. 创建新的 Node.js 应用
npm init
  1. 安装 Google Auth 客户端库
npm install google-auth-library
  1. 创建 index.js 文件
  2. 检索 Cloud Functions 函数的网址,以在下一步中将其添加到代码中。
echo $FUNCTION_URL
  1. 将以下代码添加到 index.js 中。请务必将 targetAudience 变量更改为您的 Cloud Functions 函数网址。

index.js

// Cloud Functions uses your function's url as the `targetAudience` value

const targetAudience = '<YOUR-CLOUD-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;
});
  1. 运行应用
node index.js

您应该会看到生成的“Hello World!”

问题排查

如果您看到权限“iam.serviceAccounts.getOpenIdToken”的错误在资源上被拒绝(或者该角色可能不存在),请等待几分钟,以便 Service Account Token Creator 角色生效。

如果您收到“无法在此环境中提取 ID 令牌”错误,请使用 GCE 或将 GOOGLE_APPLICATION_CREDENTIALS 环境变量设置为服务账号凭据 JSON 文件,那么您可能忘了运行该命令

gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS

7. 恭喜!

恭喜您完成此 Codelab!

建议您查看有关如何保护 Cloud Functions 函数的文档。

我们还建议您阅读有关使用 Cloud Functions 进行本地开发的这篇博文,了解如何在本地开发者环境中开发和测试您的 Cloud Functions 函数。

所学内容

  • 如何对 Cloud Functions 函数配置身份验证,并验证是否已正确配置身份验证
  • 通过为您的 gcloud 身份提供令牌,从本地开发环境调用经过身份验证的函数
  • 如何创建服务账号并向其授予调用函数的适当角色
  • 如何从具有调用函数的适当角色的本地开发环境中模拟服务

8. 清理

为避免产生意外费用(例如,此 Cloud Functions 函数被意外调用的次数超过免费层级每月 Cloud Functions 函数调用次数),您可以删除 Cloud Functions 函数或删除您在第 2 步中创建的项目。

如需停止模拟服务账号,您可以使用自己的身份重新登录:

gcloud auth application-default login

如需删除此 Cloud Functions 函数,请前往 https://console.cloud.google.com/functions/ 前往 Cloud Functions Cloud 控制台。确保您在第 2 步中创建的项目是当前选定的项目。

选择您之前部署的 my-authenticated-function。然后点击删除

如果您选择删除整个项目,可以前往 https://console.cloud.google.com/cloud-resource-manager,选择您在第 2 步中创建的项目,然后选择“删除”。如果删除项目,则需要在 Cloud SDK 中更改项目。您可以通过运行 gcloud projects list 来查看所有可用项目的列表。