从K8s Pod获取JWT调用GCP REST API时遭遇401未授权错误
问题背景
我有一个正在运行的Kubernetes Pod,它能正常和BigQuery通信——调用Pod自身端点时,BigQuery数据访问一切正常,说明关联的服务账号权限没问题。我在Pod的/var/run/secrets/kubernetes.io/serviceaccount路径下读取了token文件得到JWT,用解码器检查后解析结果也正常。
但直接发起如下REST调用时却收到401错误:
curl --request GET \ --url https://bigquery.googleapis.com/bigquery/v2/projects/testproject/datasets/testdataset \ --header 'Authorization: Bearer <jwt>'
错误响应:
{ "error": { "code": 401, "message": "请求包含无效的身份验证凭据。需要OAuth 2访问令牌、登录Cookie或其他有效身份验证凭据。", "errors": [ { "message": "无效凭据", "domain": "global", "reason": "authError", "location": "Authorization", "locationType": "header" } ], "status": "UNAUTHENTICATED" } }
问题原因及解决办法
K8s服务账号的JWT无法直接用于Google Cloud API
你拿到的JWT是Kubernetes自身的服务账号令牌,它的受众(audience)是Kubernetes API服务器,而非Google Cloud的API。Google Cloud服务仅接受针对GCP API颁发的OAuth2访问令牌,因此直接用该K8s令牌调用BigQuery API会被拒绝。正确获取有效令牌的两种方式
通过Workload Identity(GKE推荐方案)
如果你的Pod运行在GKE上且已配置Workload Identity,Pod内的应用其实是通过应用默认凭据(ADC)自动获取到了GCP API的有效访问令牌,这也是Pod自身端点能正常访问BigQuery的原因。若要手动获取,可在Pod内执行gcloud auth application-default print-access-token(需预先安装gcloud SDK),返回的结果就是可直接用于BigQuery API的OAuth2访问令牌。手动交换K8s令牌为GCP访问令牌
若未使用Workload Identity,需将K8s服务账号令牌交换为GCP访问令牌(前提是K8s服务账号已与GCP服务账号绑定):# 获取K8s服务账号令牌 TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) # 调用GCP令牌交换端点 curl -X POST "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/<你的GCP服务账号邮箱>:generateAccessToken" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "scope": ["https://www.googleapis.com/auth/bigquery"] }'响应结果中的
accessToken就是可用于BigQuery API的有效令牌。
验证调用
使用获取到的GCP访问令牌发起请求:curl --request GET \ --url https://bigquery.googleapis.com/bigquery/v2/projects/testproject/datasets/testdataset \ --header "Authorization: Bearer <GCP_ACCESS_TOKEN>"
内容的提问来源于stack exchange,提问作者curiousengineer

