验证来自 Google Cloud 调度程序的 HTTP 请求的流程是什么?文档 ( https://cloud.google.com/scheduler/docs/creating ) 提到您可以创建一个以任何公开可用的 HTTP 端点为目标的作业,但没有提到服务器如何验证 cron/scheduler 请求。
4 回答
[2019 年 5 月 28 日更新]
Google Cloud Scheduler 现在有两个命令行选项:
--oidc-service-account-email=<service_account_email>
--oidc-token-audience=<service_endpoint_being_called>
这些选项为 Cloud Scheduler 发出的请求添加了一个额外的标头:
Authorization: Bearer ID_TOKEN
您可以在端点代码中处理 ID_TOKEN 以验证谁在调用您的端点。
例如,您可以发出 HTTP 请求来解码 ID Token:
https://oauth2.googleapis.com/tokeninfo?id_token=ID_TOKEN
这将像这样返回 JSON:
{
"aud": "https://cloudtask-abcdefabcdef-uc.a.run.app",
"azp": "0123456789077420983142",
"email": "cloudtask@development.iam.gserviceaccount.com",
"email_verified": "true",
"exp": "1559029789",
"iat": "1559026189",
"iss": "https://accounts.google.com",
"sub": "012345678901234567892",
"alg": "RS256",
"kid": "0123456789012345678901234567890123456789c3",
"typ": "JWT"
}
然后,您可以检查服务帐户电子邮件是否与您授权 Cloud Scheduler 使用的电子邮件相匹配,并且该令牌尚未过期。
[结束更新]
您需要自己验证请求。
Google Cloud Scheduler 包含几个 Google 特定的标头,例如User-Agent: Google-Cloud-Scheduler
. 请参阅下面的文档链接。
但是,任何人都可以伪造 HTTP 标头。您需要创建一个自定义something
,将其包含为 HTTP 标头或包含在您知道如何验证的 HTTP 正文中。使用签名的 JWT 将是安全且易于创建和验证的。
创建 Google Cloud Scheduler 作业时,您可以对headers
和body
字段进行一些控制。您可以将自定义嵌入something
其中之一。
[更新]
这是一个使用 gcloud 的示例(Windows 命令行),以便您可以设置 HTTP 标头和正文。此示例在每个触发器上调用 Cloud Functions,展示如何包含 APIKEY。Google 控制台还没有这种级别的支持。
gcloud beta scheduler ^
--project production ^
jobs create http myfunction ^
--time-zone "America/Los_Angeles" ^
--schedule="0 0 * * 0" ^
--uri="https://us-central1-production.cloudfunctions.net/myfunction" ^
--description="Job Description" ^
--headers="{ \"Authorization\": \"APIKEY=AUTHKEY\", \"Content-Type\": \"application/json\" }" ^
--http-method="POST" ^
--message-body="{\"to\":\"/topics/allDevices\",\"priority\":\"low\",\"data\":{\"success\":\"ok\"}}"
简短的回答
如果您在 Google Cloud 中托管您的应用程序,只需检查 header 是否X-Appengine-Queuename
等于__scheduler
. 但是,这是未记录的行为,有关更多信息,请阅读下文。
此外,如果可能,请使用 Pub/Sub 而不是 HTTP 请求,因为 Pub/Sub 是内部发送的(因此具有隐式验证的来源)。
实验
正如我在这里发现的那样,谷歌删除了某些标头的请求1,但不是全部2。让我们看看 Cloud Scheduler 是否有这样的标头。
1例如,您不能发送任何X-Google-*
标头(通过实验发现,阅读更多)
2例如,您可以发送X-Appengine-*
标头(实验性发现)
实验中使用的 Flask 应用:
@app.route('/echo_headers')
def echo_headers():
headers = {h[0]: h[1] for h in request.headers}
print(headers)
return jsonify(headers)
Cloud Scheduler 发送的请求标头
{
"Host": []
"X-Forwarded-For": "0.1.0.2, 169.254.1.1",
"X-Forwarded-Proto": "http",
"User-Agent": "AppEngine-Google; (+http://code.google.com/appengine)",
"X-Appengine-Queuename": "__scheduler",
"X-Appengine-Taskname": [private]
"X-Appengine-Taskretrycount": "1",
"X-Appengine-Taskexecutioncount": "0",
"X-Appengine-Tasketa": [private]
"X-Appengine-Taskpreviousresponse": "0",
"X-Appengine-Taskretryreason": "",
"X-Appengine-Country": "ZZ",
"X-Cloud-Trace-Context": [private]
"X-Appengine-Https": "off",
"X-Appengine-User-Ip": [private]
"X-Appengine-Api-Ticket": [private]
"X-Appengine-Request-Log-Id": [private]
"X-Appengine-Default-Version-Hostname": [private]
}
证明标题X-Appengine-Queuename
已被 GAE 剥离
限制
Google SLA 和折旧政策很可能不支持此方法,因为它没有记录在案。另外,我不确定当请求源在 Google Cloud 中时是否无法伪造标头(也许它们在外层被剥离)。我已经在 GAE 中使用应用程序进行了测试,结果可能会或可能不会因其他部署选项而异。简而言之,使用风险自负。
此标头应该可以工作:
映射(键:字符串,值:字符串)
HTTP 请求标头。
此映射包含标题字段名称和值。可以在创建作业时设置标题。
Cloud Scheduler 将一些标头设置为默认值:
User-Agent:默认情况下,此标头是“AppEngine-Google; (+http://code.google.com/appengine)”。此标头可以修改,但 Cloud Scheduler 会将“AppEngine-Google; (+http://code.google.com/appengine)”附加到修改后的 User-Agent。X-CloudScheduler:此标头将设置为 true。X-CloudScheduler-JobName:此标头将包含作业名称。X-CloudScheduler-ScheduleTime:对于以 unix-cron 格式指定的 Cloud Scheduler 作业,此标头将包含 RFC3339 UTC "Zulu" 格式的作业调度时间。如果作业有正文,Cloud Scheduler 会设置以下标头:
Content-Type:默认情况下,Content-Type 标头设置为“application/octet-stream”。可以通过在创建作业时将 Content-Type 显式设置为特定媒体类型来覆盖默认值。例如,Content-Type 可以设置为“application/json”。Content-Length:这是由 Cloud Scheduler 计算的。该值仅用于输出。它不能改变。以下标题仅用于输出。它们不能被设置或覆盖:
X-Google- :仅供 Google 内部使用。X-AppEngine-:仅供 Google 内部使用。此外,一些包含作业特定信息的 App Engine 标头也会发送到作业处理程序。
包含“键”列表的对象:值对。示例:{“名称”:“扳手”,“质量”:“1.3kg”,“计数”:“3”}。
if request.META['HTTP_X_CLOUDSCHEDULER'] == 'true':
print("True")