TL;DR 如何获取并使用凭证在 Python 脚本中与 Google 产品交互(使用 Workload Identity Federation 和 GitHub Actions)。代码在文章最后
我的手指上长满了老茧,在网上搜索了超过 8 个小时,并想在这里发布相关的搜索词来帮助下一个可怜的灵魂。
这受到了这个拯救了我生命的答案的启发:https://stackoverflow.com/a/72370925/15187444
具体用例:
问题:
解决方案:
需要采取一些步骤(设置服务帐户、下载适当的库等),这些步骤相当简单且易于实施。我会让您阅读文档来自己完成所有设置。
关键步骤:
如果您的提供商没有正确的属性映射,Google 将无法识别您的服务帐户。设置提供程序是工作负载身份联合设置文档的一部分。
您可能见过这样的错误:
"error": {
"code": 403,
"message": "Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist).",
"status": "PERMISSION_DENIED",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "IAM_PERMISSION_DENIED",
"domain": "iam.googleapis.com",
"metadata": {
"permission": "iam.serviceAccounts.getAccessToken"
}
}
]
}
}
或者这个:
google.auth.exceptions.RefreshError: ('Unable to acquire impersonated credentials', '{\n "error": {\n "code": 403,\n "message": "Permission \'iam.serviceAccounts.getAccessToken\' denied on resource (or it may not exist).",\n "status": "PERMISSION_DENIED",\n "details": [\n {\n "@type": "type.googleapis.com/google.rpc.ErrorInfo",\n "reason": "IAM_PERMISSION_DENIED",\n "domain": "iam.googleapis.com",\n "metadata": {\n "permission": "iam.serviceAccounts.getAccessToken"\n }\n }\n ]\n }\n}\n')
要设置正确的属性,请按照以下屏幕截图操作:
编辑您的提供商
编辑页面如下所示。映射属性,如第二个屏幕截图所示
另一件事需要注意的是,为了连接,您需要使用
identity_pool
库中的 google-auth
类,而不是 service_account
类。您可以在 Python 脚本中看到这一点,我们在导入 credentials = Credentials.from_info(key)
后使用
from google.auth.identity_pool import Credentials
如果您使用
credentials = service_account.Credentials.from_service_account_info(key)
,您将收到此错误,因为通常的服务帐户密钥具有与工作负载身份联合生成的凭据不同的形状/架构:
raise exceptions.MalformedError(
google.auth.exceptions.MalformedError: Service account info was not in the expected format, missing fields token_uri, client_email.
代码:
将数据附加到 Google 电子表格的 Python 脚本:
#!/usr/bin/env python
import json
import os
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from google.auth.identity_pool import Credentials
SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"]
SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID'
RANGE = 'YOUR_RANGE'
f = open(os.environ["GOOGLE_APPLICATION_CREDENTIALS"])
key = json.load(f)
def append_to_spreadsheet(values, value_input_option="USER_ENTERED"):
credentials = Credentials.from_info(key)
append_body = {
"values": values,
"range": RANGE,
"majorDimension": "ROWS",
}
try:
service = build("sheets", "v4", credentials=credentials)
service.spreadsheets().values().append(
spreadsheetId=SPREADSHEET_ID,
range=RANGE,
body=append_body,
valueInputOption=value_input_option,
).execute()
except HttpError as error:
print(error)
append_to_spreadsheet([['value1', 'value2']])
用于 GitHub Actions 运行作业的 YAML 文件:
name: name_of_your_job
on:
workflow_dispatch:
jobs:
build:
permissions:
contents: 'read'
id-token: 'write'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v2'
with:
workload_identity_provider: "projects/<PROJECT_ID>/locations/global/workloadIdentityPools/<POOL_NAME>/providers/<PROVIDER_NAME>"
service_account: "<enter your service account email>"
token_format: 'access_token'
- name: Setup python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: install python packages
run:
python -m pip install --upgrade pip
pip install requests google-api-python-client google-auth-httplib2 google-auth-oauthlib google-auth
- name: call google api
run: python google.py
这是一篇信息性帖子。我希望能够帮助其他人避免痛苦:)
我根据 Google GitHub Actions Auth 文档 创建了我的工作负载身份池,没有任何服务帐户。然后我尝试连接到 Google Drive,这需要 OAuth 2.0 访问令牌。我使用了以下配置:
- name: Authenticate with Google Cloud
uses: 'google-github-actions/auth@v2'
with:
project_id: 'my-project'
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
- name: Upload files to Google Drive
uses: 'Burak-Atak/drive-upload@master'
with:
google_credentials_file_path: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}
files_to_create: "app.spec"
drive_folder_id: "242fgdfg345345"
files_to_update: "requirements.txt"
file_ids_to_update: "asdas3534fdgg"
但是,我使用以下代码收到以下错误:
def authenticate_google(self):
credentials, project_id = load_credentials_from_file(
os.environ["GOOGLE_APPLICATION_CREDENTIALS"],
scopes=[
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.metadata'
]
)
return build("drive", "v3", credentials=credentials)
googleapiclient.errors.HttpError: <HttpError 401 when requesting https://www.googleapis.com/upload/drive/v3/files?fields=id&alt=json&uploadType=multipart returned "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.". Details: "[{'message': 'Invalid Credentials', 'domain': 'global', 'reason': 'authError', 'location': 'Authorization', 'locationType': 'header'}]">
我意识到我应该为 Google Drive API 使用 OAuth 2.0。然后我将配置更改为:
- name: Authenticate with Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
project_id: '<PROJECT_ID>'
service_account: '<PROJECT_ID>@<PROJECT_ID>.iam.gserviceaccount.com'
token_format: 'access_token'
access_token_lifetime: '60s'
access_token_scopes: 'https://www.googleapis.com/auth/drive.file,https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/drive.metadata'
- name: Upload files to Google Drive
uses: 'Burak-Atak/drive-upload@master'
with:
google_credentials_file_path: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}
files_to_create: "app.spec"
drive_folder_id: "242fgdfg345345"
files_to_update: "requirements.txt"
file_ids_to_update: "asdas3534fdgg"
进行此更改后,即使我的服务帐户中具有 Service Account Token Creator 和 Owner 角色,我也开始收到以下错误:
google-github-actions/auth failed with: failed to generate Google Cloud OAuth 2.0 Access Token for <PROJECT_ID>@<PROJECT_ID>.iam.gserviceaccount.com: {
"error": {
"code": 403,
"message": "Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist).",
"status": "PERMISSION_DENIED",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "IAM_PERMISSION_DENIED",
"domain": "iam.googleapis.com",
"metadata": {
"permission": "iam.serviceAccounts.getAccessToken"
}
}
]
}
}
我发现我应该向我的工作负载池添加一个服务帐户。检查 Google Cloud Console 中的“连接的服务帐户”部分。如果没有连接的服务帐户,您应该添加一个。