如何使用身份验证从 Cloud Scheduler 调用 Cloud Function
How to invoke Cloud Function from Cloud Scheduler with Authentication
我到处都看过,似乎人们要么使用 pubsub、app engine http,要么使用没有授权的 http。没有太多人展示他们通过使用 oidc 令牌的身份验证来访问函数以访问 google 函数的工作。
我检查过:Cannot invoke Google Cloud Function from GCP Scheduler 但似乎没有任何效果。
我遵循的文档:
https://cloud.google.com/scheduler/docs/http-target-auth#using-gcloud_1
- 创建了一个新的服务帐户
- 设置角色(云调度程序服务agent/Cloud函数服务agent/Cloud调度程序admin/cloud函数调用者...甚至尝试过
主人!)
- 已部署 google 不允许 public(未验证)访问的函数(一个简单的 helloworld 函数)
- 将云调度程序上的 cron 作业设置为每分钟 运行 使用此配置针对新部署的功能:
- url = helloworld 函数
- oidc-令牌
- 新创建的服务帐户
- 受众设置为 hello world 函数 url
云调度程序日志的结果:
Expand all | Collapse all{
httpRequest: {
}
insertId: "ibboa4fg7l1s9"
jsonPayload: {
@type: "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished"
jobName: "projects/project/locations/region/jobs/tester"
status: "PERMISSION_DENIED"
targetType: "HTTP"
url: "https://region-project.cloudfunctions.net/tester"
}
logName: "projects/project/logs/cloudscheduler.googleapis.com%2Fexecutions"
receiveTimestamp: "2020-04-15T17:50:14.287689800Z"
resource: {…}
severity: "ERROR"
timestamp: "2020-04-15T17:50:14.287689800Z"
我看到一个解决方案显示有人创建了一个新项目来让它工作,还有其他的吗??
感谢提供的任何帮助。
更新
New Google Function - running in central (same as my app engine app)
New Service Account - w/ Owner role
New Scheduled Task - Info
New Scheduled Task - Status
New Scheduled Task - Logs
实际修复
如果您缺少 cloudscheduler 服务帐户(例如:
服务 1231231231412@gcp-sa-cloudscheduler.iam.gserviceaccount.com)
Http 身份验证任务不会工作。
要修复,我必须禁用 api 并重新启用,它给了我服务帐户,
我没有使用这个服务帐户,但这是我这样做后唯一改变的因素。
1.I 创建了一个 clod 函数(与我的 App Engine 应用程序位于同一区域),它不允许 public 访问
2.I 创建了一个具有所有者角色的服务帐户
3.I 创建了云调度程序作业:
url = https://europe-west2-my-project.cloudfunctions.net/my-function
oidc-令牌
新创建的服务帐户
观众 = https://europe-west2-my-project.cloudfunctions.net/my-function
一切都按预期进行。请仔细检查您的设置,因为它应该有效。
这些是您必须执行的确切步骤。一定不要跳过第二步,它会在服务帐户上设置调用者权限,以便调度程序能够使用该服务帐户的 OIDC 信息调用 HTTP Cloud Function。注意:为简单起见,我在这里选择默认服务帐户,但是,为此目的创建一个权限较低的单独服务帐户是明智的。
# Create cloud function
gcloud functions deploy my_function \
--entry-point=my_entrypoint \
--runtime=python37 \
--trigger-http \
--region=europe-west1 \
--project=${PROJECT_ID}
# Set invoke permissions
gcloud functions add-iam-policy-binding my_function \
--region=europe-west1 \
--member=serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com \
--role="roles/cloudfunctions.invoker" \
--project=${PROJECT_ID}
# Deploy scheduler
gcloud scheduler jobs create http my_job \
--schedule="every 60 minutes" \
--uri="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function/" \
--http-method=POST \
--oidc-service-account-email="${PROJECT_ID}@appspot.gserviceaccount.com" \
--oidc-token-audience="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function" \
--project=${PROJECT_ID}
我想我会扩展 Nebulastic 的答案,因为我刚刚发现有一些边缘情况他们的答案不完整。
创建云函数
# Create cloud function
gcloud functions deploy my_function \
--entry-point=my_entrypoint \
--runtime=python37 \
--trigger-http \
--region=europe-west1 \
--project=${PROJECT_ID}
您可能想要更改 entry-point
、runtime
或 trigger
等字段。在 Cloud Functions deploy docs.
中阅读更多内容
创建 Cloud Scheduler 作业:
# Deploy scheduler
gcloud scheduler jobs create http my_job \
--schedule="every 60 minutes" \
--uri="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function/" \
--http-method=POST \
--oidc-service-account-email="${SERVICE_ACCOUNT_EMAIL}" \
--oidc-token-audience="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function" \
--project=${PROJECT_ID}
有关更多信息,请参阅 Cloud Schedule docs。
请注意,任何服务帐户都可用于 OIDC 身份验证,而不仅仅是为项目创建的默认帐户。
此外,您不需要像 Nebulastic 的回答中建议的那样调用 gcloud functions add-iam-policy-binding
,而是可以设置 Cloud Functions Invoker role to the service account passed to --oidc-service-account-email
(see doc on adding roles)。这样,此类服务帐户将有权调用任何 Cloud Functions,而无需在每次部署时都授予此类权限。这并不是说Nebulastic建议的方法不正确,你也可以这样做。
您可以通过在 Cloud Functions 列表中单击其名称 -> 权限选项卡 -> 角色子选项卡 -> 打开 Cloud Functions Invoker 行来验证哪些服务帐户有权调用该函数。
现在是边缘情况:
入口 - 仅允许内部流量
尽管 the documentation 提出了建议,但将函数的入口设置设置为 'Allow internal traffic only' 不会包含 Cloud Scheduler 的流量,并且会导致 PERMISSION_DENIED 错误。这是 GCP 开发人员知道的事情,将来可能会修复。现在,使用 'Allow all traffic'(如果使用 gcloud 部署,则使用 --ingress-settings=all
)。
URL参数和OIDC认证
如果您的 Cloud Schedule 作业使用 OIDC 身份验证并且您的函数需要 URL parameters 形式的参数 - 例如。 ...my_function?key=value
- 那么您必须确保 OIDC 受众 不 包含 URL 参数。仅在 URL 字段中指定 URL 参数,但如果它们出现在 Audience 字段中,请求将 return 403 UNAUTHENTICATED。请注意,如果您未手动指定 OIDC 受众,则 OIDC 受众 auto-filled 包含 URL 的副本。这是 GCP 开发人员知道的另一个问题,并且可能正在修复或更新文档。
tldr:不要在 OIDC 受众 URL 中放置 URL 参数 - 仅在 URL 字段中:
--uri="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function?key=value" \
--oidc-token-audience="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function" \
JSON 参数和 Content-Type
如果您选择使用 Cloud Scheduler 作业调用带有 JSON 参数的函数 - 例如。 {"key": "value"}
在 body 字段中 - 然后您需要:
在函数中手动解析 JSON - 例如 Flask's get_json 将不起作用,除非您使用 force=True
.
调用它
使用 gcloud scheduler jobs create
创建函数,而不是从控制台创建函数,并确保通过添加以下标志指定 Content-Type header:--headers Content-Type=application/json
Cloud Scheduler API 在 19/03/2019 之前启用
The Cloud Scheduler service account with this role granted is automatically set up when you enable the Cloud Scheduler API, unless you enabled it prior to March 19, 2019, in which case you must add the role manually.
这似乎是作者问题中列出的问题背后的原因。有关更多信息,请参阅 docs。造成问题的原因如下:
Cloud Scheduler itself must have a service account of its own that has the Cloud Scheduler Service Agent role granted. This is so it can generate header tokens on behalf of your client service account to authenticate to your target.
资料来源:刚刚花了一周时间与他们的一位支持人员一起解决所有这些问题 - 向帮助发现所有这些怪癖的 Jason 大声疾呼。他们正在处理这些问题,但我想我会把这些信息留在这里,直到(如果有的话)这些问题得到解决。
对于那些在 2019 年 3 月 19 日之后激活 api 的用户来说,这是一个令人沮丧的错误。
对于 2019 年 3 月 19 日之前的用户,google 已注意到以下修复;
此过程允许您在不禁用和重新启用 API 的情况下解决问题。
Google 有一份文档解决了这个问题并指出
“仅当您在 2019 年 3 月 19 日之前启用 Cloud Scheduler API 时才有必要。”
https://cloud.google.com/scheduler/docs/http-target-auth#add
使用控制台:
使用 gcloud
Find your project number:
gcloud projects describe [project-id] --format='table(projectNumber)'
Replacing [project-id] with your project ID.
Copy down the number.
Grant the Cloud Scheduler service account the Cloud Scheduler Service Agent role, using the project number you copied down:
gcloud projects add-iam-policy-binding [project-id] --member serviceAccount:service-[project-number]@gcp-sa-cloudscheduler.iam.gserviceaccount.com --role roles/cloudscheduler.serviceAgent
Replacing [project-id] with your project ID and [project-number] with the project number from above.
以上评论中的注意事项:
禁用并重新启用 API 也可以解决问题(因为它会自动执行此过程),但对于不想破坏当前任务的任何人来说,这都是破坏性的。
只是想分享一下,我 运行 遇到了类似的身份验证问题,但原因不同。我完全按照概述的方式遵循了所有文档,设置服务帐户,授予它调用特定云 运行 函数的权限,在创建云调度程序作业时相应地设置身份验证 header。看到此错误消息:
{
httpRequest: {
status: 401
}
insertId: "1xxol5ifezru6m"
jsonPayload: {
@type: "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished"
jobName: "projects/<PROJECT_ID>/locations/us-central1/jobs/test"
status: "UNAUTHENTICATED"
targetType: "HTTP"
url: "<MY_URL>"
}
logName: "projects/<PROJECT_ID>/logs/cloudscheduler.googleapis.com%2Fexecutions"
receiveTimestamp: "2022-04-19T02:09:54.932289922Z"
resource: {
labels: {
job_id: "test"
location: "us-central1"
project_id: "<PROJECT_ID>"
}
type: "cloud_scheduler_job"
}
severity: "ERROR"
timestamp: "2022-04-19T02:09:54.932289922Z"
}
以下是我创建云调度程序作业的方式:
gcloud scheduler jobs create http senses-test-run \
--oidc-service-account-email="senses-cloud-scheduler@<PROJECT_ID>.iam.gserviceaccount.com" \
--oidc-token-audience="https://my-custom-domain-that-maps-to-cloud-run" \
--location="us-central1" \
--schedule="5 4 * * mon" \
--uri="https://my-custom-domain-that-maps-to-cloud-run" \
--http-method="post" \
--headers="Content-Type=application/json" \
--message-body="{\"foo\":\"bar\",\"beep\":\"boop\"}"
我有一个映射到我的云 运行 函数的自定义域,所以不是调度程序作业 post 从 GCP 生成的 URL,即 https://<cloud-run-name>-XXXXXX-uc.a.run.app
,它会 post 到 https://my-custom-domain-that-maps-to-cloud-run
。当指定您的云调度程序将 post 到的 uri
但不是 oidc-token-audience
.
时,这工作正常
以下是 the docs 对 oidc-token-audience
的评价:
--oidc-token-audience=OIDC_TOKEN_AUDIENCE
The audience to be used when generating an OpenId Connect token to be included in the request sent to the target when executing the job. If not specified, the URI specified in target will be used.
由于我不知道的原因,当我输入 https://my-custom-domain-that-maps-to-cloud-run
时这不起作用。我不得不使用提供的云 运行 URL 来代替我的服务 https://<cloud-run-name>-XXXXXX-uc.a.run.app
。一旦我更改了听众 URL,身份验证问题就消失了。
希望这可以节省我解决这个问题所花费的时间!
我到处都看过,似乎人们要么使用 pubsub、app engine http,要么使用没有授权的 http。没有太多人展示他们通过使用 oidc 令牌的身份验证来访问函数以访问 google 函数的工作。
我检查过:Cannot invoke Google Cloud Function from GCP Scheduler 但似乎没有任何效果。
我遵循的文档: https://cloud.google.com/scheduler/docs/http-target-auth#using-gcloud_1
- 创建了一个新的服务帐户
- 设置角色(云调度程序服务agent/Cloud函数服务agent/Cloud调度程序admin/cloud函数调用者...甚至尝试过 主人!)
- 已部署 google 不允许 public(未验证)访问的函数(一个简单的 helloworld 函数)
- 将云调度程序上的 cron 作业设置为每分钟 运行 使用此配置针对新部署的功能:
- url = helloworld 函数
- oidc-令牌
- 新创建的服务帐户
- 受众设置为 hello world 函数 url
云调度程序日志的结果:
Expand all | Collapse all{
httpRequest: {
}
insertId: "ibboa4fg7l1s9"
jsonPayload: {
@type: "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished"
jobName: "projects/project/locations/region/jobs/tester"
status: "PERMISSION_DENIED"
targetType: "HTTP"
url: "https://region-project.cloudfunctions.net/tester"
}
logName: "projects/project/logs/cloudscheduler.googleapis.com%2Fexecutions"
receiveTimestamp: "2020-04-15T17:50:14.287689800Z"
resource: {…}
severity: "ERROR"
timestamp: "2020-04-15T17:50:14.287689800Z"
我看到一个解决方案显示有人创建了一个新项目来让它工作,还有其他的吗??
感谢提供的任何帮助。
更新
New Google Function - running in central (same as my app engine app)
New Service Account - w/ Owner role
New Scheduled Task - Info
New Scheduled Task - Status
New Scheduled Task - Logs
实际修复
如果您缺少 cloudscheduler 服务帐户(例如: 服务 1231231231412@gcp-sa-cloudscheduler.iam.gserviceaccount.com) Http 身份验证任务不会工作。 要修复,我必须禁用 api 并重新启用,它给了我服务帐户, 我没有使用这个服务帐户,但这是我这样做后唯一改变的因素。
1.I 创建了一个 clod 函数(与我的 App Engine 应用程序位于同一区域),它不允许 public 访问
2.I 创建了一个具有所有者角色的服务帐户
3.I 创建了云调度程序作业:
url = https://europe-west2-my-project.cloudfunctions.net/my-function
oidc-令牌
新创建的服务帐户
观众 = https://europe-west2-my-project.cloudfunctions.net/my-function
一切都按预期进行。请仔细检查您的设置,因为它应该有效。
这些是您必须执行的确切步骤。一定不要跳过第二步,它会在服务帐户上设置调用者权限,以便调度程序能够使用该服务帐户的 OIDC 信息调用 HTTP Cloud Function。注意:为简单起见,我在这里选择默认服务帐户,但是,为此目的创建一个权限较低的单独服务帐户是明智的。
# Create cloud function
gcloud functions deploy my_function \
--entry-point=my_entrypoint \
--runtime=python37 \
--trigger-http \
--region=europe-west1 \
--project=${PROJECT_ID}
# Set invoke permissions
gcloud functions add-iam-policy-binding my_function \
--region=europe-west1 \
--member=serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com \
--role="roles/cloudfunctions.invoker" \
--project=${PROJECT_ID}
# Deploy scheduler
gcloud scheduler jobs create http my_job \
--schedule="every 60 minutes" \
--uri="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function/" \
--http-method=POST \
--oidc-service-account-email="${PROJECT_ID}@appspot.gserviceaccount.com" \
--oidc-token-audience="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function" \
--project=${PROJECT_ID}
我想我会扩展 Nebulastic 的答案,因为我刚刚发现有一些边缘情况他们的答案不完整。
创建云函数
# Create cloud function
gcloud functions deploy my_function \
--entry-point=my_entrypoint \
--runtime=python37 \
--trigger-http \
--region=europe-west1 \
--project=${PROJECT_ID}
您可能想要更改 entry-point
、runtime
或 trigger
等字段。在 Cloud Functions deploy docs.
创建 Cloud Scheduler 作业:
# Deploy scheduler
gcloud scheduler jobs create http my_job \
--schedule="every 60 minutes" \
--uri="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function/" \
--http-method=POST \
--oidc-service-account-email="${SERVICE_ACCOUNT_EMAIL}" \
--oidc-token-audience="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function" \
--project=${PROJECT_ID}
有关更多信息,请参阅 Cloud Schedule docs。
请注意,任何服务帐户都可用于 OIDC 身份验证,而不仅仅是为项目创建的默认帐户。
此外,您不需要像 Nebulastic 的回答中建议的那样调用 gcloud functions add-iam-policy-binding
,而是可以设置 Cloud Functions Invoker role to the service account passed to --oidc-service-account-email
(see doc on adding roles)。这样,此类服务帐户将有权调用任何 Cloud Functions,而无需在每次部署时都授予此类权限。这并不是说Nebulastic建议的方法不正确,你也可以这样做。
您可以通过在 Cloud Functions 列表中单击其名称 -> 权限选项卡 -> 角色子选项卡 -> 打开 Cloud Functions Invoker 行来验证哪些服务帐户有权调用该函数。
现在是边缘情况:
入口 - 仅允许内部流量
尽管 the documentation 提出了建议,但将函数的入口设置设置为 'Allow internal traffic only' 不会包含 Cloud Scheduler 的流量,并且会导致 PERMISSION_DENIED 错误。这是 GCP 开发人员知道的事情,将来可能会修复。现在,使用 'Allow all traffic'(如果使用 gcloud 部署,则使用 --ingress-settings=all
)。
URL参数和OIDC认证
如果您的 Cloud Schedule 作业使用 OIDC 身份验证并且您的函数需要 URL parameters 形式的参数 - 例如。 ...my_function?key=value
- 那么您必须确保 OIDC 受众 不 包含 URL 参数。仅在 URL 字段中指定 URL 参数,但如果它们出现在 Audience 字段中,请求将 return 403 UNAUTHENTICATED。请注意,如果您未手动指定 OIDC 受众,则 OIDC 受众 auto-filled 包含 URL 的副本。这是 GCP 开发人员知道的另一个问题,并且可能正在修复或更新文档。
tldr:不要在 OIDC 受众 URL 中放置 URL 参数 - 仅在 URL 字段中:
--uri="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function?key=value" \
--oidc-token-audience="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/my_function" \
JSON 参数和 Content-Type
如果您选择使用 Cloud Scheduler 作业调用带有 JSON 参数的函数 - 例如。 {"key": "value"}
在 body 字段中 - 然后您需要:
在函数中手动解析 JSON - 例如 Flask's get_json 将不起作用,除非您使用
调用它force=True
.使用
gcloud scheduler jobs create
创建函数,而不是从控制台创建函数,并确保通过添加以下标志指定 Content-Type header:--headers Content-Type=application/json
Cloud Scheduler API 在 19/03/2019 之前启用
The Cloud Scheduler service account with this role granted is automatically set up when you enable the Cloud Scheduler API, unless you enabled it prior to March 19, 2019, in which case you must add the role manually.
这似乎是作者问题中列出的问题背后的原因。有关更多信息,请参阅 docs。造成问题的原因如下:
Cloud Scheduler itself must have a service account of its own that has the Cloud Scheduler Service Agent role granted. This is so it can generate header tokens on behalf of your client service account to authenticate to your target.
资料来源:刚刚花了一周时间与他们的一位支持人员一起解决所有这些问题 - 向帮助发现所有这些怪癖的 Jason 大声疾呼。他们正在处理这些问题,但我想我会把这些信息留在这里,直到(如果有的话)这些问题得到解决。
对于那些在 2019 年 3 月 19 日之后激活 api 的用户来说,这是一个令人沮丧的错误。
对于 2019 年 3 月 19 日之前的用户,google 已注意到以下修复;
此过程允许您在不禁用和重新启用 API 的情况下解决问题。
Google 有一份文档解决了这个问题并指出
“仅当您在 2019 年 3 月 19 日之前启用 Cloud Scheduler API 时才有必要。”
https://cloud.google.com/scheduler/docs/http-target-auth#add
使用控制台:
使用 gcloud
Find your project number:
gcloud projects describe [project-id] --format='table(projectNumber)'
Replacing [project-id] with your project ID.
Copy down the number.
Grant the Cloud Scheduler service account the Cloud Scheduler Service Agent role, using the project number you copied down:
gcloud projects add-iam-policy-binding [project-id] --member serviceAccount:service-[project-number]@gcp-sa-cloudscheduler.iam.gserviceaccount.com --role roles/cloudscheduler.serviceAgent
Replacing [project-id] with your project ID and [project-number] with the project number from above.
以上评论中的注意事项: 禁用并重新启用 API 也可以解决问题(因为它会自动执行此过程),但对于不想破坏当前任务的任何人来说,这都是破坏性的。
只是想分享一下,我 运行 遇到了类似的身份验证问题,但原因不同。我完全按照概述的方式遵循了所有文档,设置服务帐户,授予它调用特定云 运行 函数的权限,在创建云调度程序作业时相应地设置身份验证 header。看到此错误消息:
{
httpRequest: {
status: 401
}
insertId: "1xxol5ifezru6m"
jsonPayload: {
@type: "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished"
jobName: "projects/<PROJECT_ID>/locations/us-central1/jobs/test"
status: "UNAUTHENTICATED"
targetType: "HTTP"
url: "<MY_URL>"
}
logName: "projects/<PROJECT_ID>/logs/cloudscheduler.googleapis.com%2Fexecutions"
receiveTimestamp: "2022-04-19T02:09:54.932289922Z"
resource: {
labels: {
job_id: "test"
location: "us-central1"
project_id: "<PROJECT_ID>"
}
type: "cloud_scheduler_job"
}
severity: "ERROR"
timestamp: "2022-04-19T02:09:54.932289922Z"
}
以下是我创建云调度程序作业的方式:
gcloud scheduler jobs create http senses-test-run \
--oidc-service-account-email="senses-cloud-scheduler@<PROJECT_ID>.iam.gserviceaccount.com" \
--oidc-token-audience="https://my-custom-domain-that-maps-to-cloud-run" \
--location="us-central1" \
--schedule="5 4 * * mon" \
--uri="https://my-custom-domain-that-maps-to-cloud-run" \
--http-method="post" \
--headers="Content-Type=application/json" \
--message-body="{\"foo\":\"bar\",\"beep\":\"boop\"}"
我有一个映射到我的云 运行 函数的自定义域,所以不是调度程序作业 post 从 GCP 生成的 URL,即 https://<cloud-run-name>-XXXXXX-uc.a.run.app
,它会 post 到 https://my-custom-domain-that-maps-to-cloud-run
。当指定您的云调度程序将 post 到的 uri
但不是 oidc-token-audience
.
以下是 the docs 对 oidc-token-audience
的评价:
--oidc-token-audience=OIDC_TOKEN_AUDIENCE The audience to be used when generating an OpenId Connect token to be included in the request sent to the target when executing the job. If not specified, the URI specified in target will be used.
由于我不知道的原因,当我输入 https://my-custom-domain-that-maps-to-cloud-run
时这不起作用。我不得不使用提供的云 运行 URL 来代替我的服务 https://<cloud-run-name>-XXXXXX-uc.a.run.app
。一旦我更改了听众 URL,身份验证问题就消失了。
希望这可以节省我解决这个问题所花费的时间!