如何验证 Python 客户端应用程序以访问受限的 Google 云功能?
How to authenticate Python client app for access to restricted Google Cloud function?
我创建了一个 Google 云函数,我想从我正在开发的 Python 应用程序访问它。不需认证时可以访问该功能,但启用认证后无法访问该功能
这是我使用的服务帐户密钥,其中包含已删除的信息。它配置的唯一角色是调用云功能。
{
"type": "service_account",
"project_id": "XYZ",
"private_key_id": "XYZ",
"private_key": "XYZ",
"client_email": "XYZ",
"client_id": "XYZ",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "XYZ"
}
经过身份验证的请求似乎需要包含在请求中的令牌 Authorization
header,但我不知道从哪里获取此令牌。
我试过使用 here 概述的方法和环境变量以及默认的 auth 方法,但这不起作用。我认为这是因为服务帐户密钥不同于 OAuth 令牌。 (我已经创建了一个具有 Cloud Functions 调用者权限的新服务帐户,并且正在使用该服务帐户密钥)。我收到以下错误:
google.auth.exceptions.RefreshError: ('invalid_scope: Invalid OAuth scope or ID token audience provided.', '{"error":"invalid_scope","error_description":"Invalid OAuth scope or ID token audience provided."}')
如何生成此令牌来验证来自我的 Python 脚本的请求?还是建议使用服务帐户的方法,但其他地方出了问题?
您的问题几乎可以肯定是您赋予该服务帐户的角色。服务帐户很挑剔,根据我的经验,角色/权限并不总是按照您认为的方式行事。首先创建一个具有完全权限的服务帐户(项目所有者)。在您的脚本中使用该服务帐户,然后从那里开始限制权限。听起来您至少需要云功能“admin”。如果可行,请尝试降低另一个级别。云功能“开发者”等
如果您使用 App Engine 或其他 Cloud Functions 连接到您的 Cloud Functions,您可以使用这个:Function-to-function,步骤基本上是:
授予 Cloud Functions Invoker。
在调用函数中,您需要:
创建一个 Google-signed OAuth ID 令牌,并将受众 (aud) 设置为接收函数的 URL
在对函数的请求中的 Authorization: Bearer ID_TOKEN header 中包含 ID 令牌。
导入请求
//# TODO<developer>: set these values
REGION = 'us-central1'
PROJECT_ID = 'my-project'
RECEIVING_FUNCTION = 'my-function'
//# Constants for setting up metadata server request
//# See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
function_url = f'https://{REGION}-{PROJECT_ID}.cloudfunctions.net/{RECEIVING_FUNCTION}'
metadata_server_url = \
'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience='
token_full_url = metadata_server_url + function_url
token_headers = {'Metadata-Flavor': 'Google'}
def calling_function(request):
//# Fetch the token
token_response = requests.get(token_full_url, headers=token_headers)
jwt = token_response.text
//# Provide the token in the request to the receiving function
function_headers = {'Authorization': f'bearer {jwt}'}
function_response = requests.get(function_url, headers=function_headers)
return function_response.text
我已经测试了这个解决方案并且按预期工作。
如果您从无法访问计算元数据的计算实例(例如您自己的服务器)调用函数,则必须手动生成正确的令牌:
Self-sign 服务帐户 JWT,其 target_audience 声明设置为接收函数的 URL。
将 self-signed JWT 换成 Google-signed ID 令牌,它应该将 aud 声明设置为上述 URL.
在对函数的请求中的 Authorization: Bearer ID_TOKEN header 中包含 ID 令牌。
云 IAP 文档有 sample code to demonstrate this functionality. The part you could be interested in should be this Authenticating from a service account
虽然在撰写本文时 there is a bug in the documentation 让我失望了,但我能够使它正常工作。
使用 google-auth
库中的 IDTokenCredentials class 可以访问受保护的 Cloud Functions:
credentials = service_account.IDTokenCredentials.from_service_account_file(
SERVICE_ACCOUNT_JSON_FILE,
target_audience="https://function/url/here",
)
authed_session = AuthorizedSession(credentials)
response = authed_session.post(url)
我的服务帐户配置了“Cloud Functions Invoker”角色。
我创建了一个 Google 云函数,我想从我正在开发的 Python 应用程序访问它。不需认证时可以访问该功能,但启用认证后无法访问该功能
这是我使用的服务帐户密钥,其中包含已删除的信息。它配置的唯一角色是调用云功能。
{
"type": "service_account",
"project_id": "XYZ",
"private_key_id": "XYZ",
"private_key": "XYZ",
"client_email": "XYZ",
"client_id": "XYZ",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "XYZ"
}
经过身份验证的请求似乎需要包含在请求中的令牌 Authorization
header,但我不知道从哪里获取此令牌。
我试过使用 here 概述的方法和环境变量以及默认的 auth 方法,但这不起作用。我认为这是因为服务帐户密钥不同于 OAuth 令牌。 (我已经创建了一个具有 Cloud Functions 调用者权限的新服务帐户,并且正在使用该服务帐户密钥)。我收到以下错误:
google.auth.exceptions.RefreshError: ('invalid_scope: Invalid OAuth scope or ID token audience provided.', '{"error":"invalid_scope","error_description":"Invalid OAuth scope or ID token audience provided."}')
如何生成此令牌来验证来自我的 Python 脚本的请求?还是建议使用服务帐户的方法,但其他地方出了问题?
您的问题几乎可以肯定是您赋予该服务帐户的角色。服务帐户很挑剔,根据我的经验,角色/权限并不总是按照您认为的方式行事。首先创建一个具有完全权限的服务帐户(项目所有者)。在您的脚本中使用该服务帐户,然后从那里开始限制权限。听起来您至少需要云功能“admin”。如果可行,请尝试降低另一个级别。云功能“开发者”等
如果您使用 App Engine 或其他 Cloud Functions 连接到您的 Cloud Functions,您可以使用这个:Function-to-function,步骤基本上是:
授予 Cloud Functions Invoker。
在调用函数中,您需要:
创建一个 Google-signed OAuth ID 令牌,并将受众 (aud) 设置为接收函数的 URL
在对函数的请求中的 Authorization: Bearer ID_TOKEN header 中包含 ID 令牌。
导入请求
//# TODO<developer>: set these values REGION = 'us-central1' PROJECT_ID = 'my-project' RECEIVING_FUNCTION = 'my-function' //# Constants for setting up metadata server request //# See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature function_url = f'https://{REGION}-{PROJECT_ID}.cloudfunctions.net/{RECEIVING_FUNCTION}' metadata_server_url = \ 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=' token_full_url = metadata_server_url + function_url token_headers = {'Metadata-Flavor': 'Google'} def calling_function(request): //# Fetch the token token_response = requests.get(token_full_url, headers=token_headers) jwt = token_response.text //# Provide the token in the request to the receiving function function_headers = {'Authorization': f'bearer {jwt}'} function_response = requests.get(function_url, headers=function_headers) return function_response.text
我已经测试了这个解决方案并且按预期工作。
如果您从无法访问计算元数据的计算实例(例如您自己的服务器)调用函数,则必须手动生成正确的令牌:
Self-sign 服务帐户 JWT,其 target_audience 声明设置为接收函数的 URL。
将 self-signed JWT 换成 Google-signed ID 令牌,它应该将 aud 声明设置为上述 URL.
在对函数的请求中的 Authorization: Bearer ID_TOKEN header 中包含 ID 令牌。
云 IAP 文档有 sample code to demonstrate this functionality. The part you could be interested in should be this Authenticating from a service account
虽然在撰写本文时 there is a bug in the documentation 让我失望了,但我能够使它正常工作。
使用 google-auth
库中的 IDTokenCredentials class 可以访问受保护的 Cloud Functions:
credentials = service_account.IDTokenCredentials.from_service_account_file(
SERVICE_ACCOUNT_JSON_FILE,
target_audience="https://function/url/here",
)
authed_session = AuthorizedSession(credentials)
response = authed_session.post(url)
我的服务帐户配置了“Cloud Functions Invoker”角色。