无法用服务帐户身份验证我的Google云运行服务

发布于 2025-02-04 01:20:13 字数 2634 浏览 3 评论 0 原文

我在需要身份验证的云运行中部署了一项服务:

gcloud run部署my-service -project my-project-image eu.gcr.io/my-project/my-project/rest-ofst/rest-of-path-platform-管理 - - 欧洲区 - 欧洲 - 不允许未经申请的

这似乎很好。但是,当我尝试从另一个服务访问我的服务时(就我的情况而言)时,它会给我一个响应[403] ,这意味着它拒绝授权它。据我所知,我的服务帐户确实具有正确的角色:云运行调用器,服务帐户令牌创建者,服务控制器。即使我要添加所有者角色,它也无法正常工作。

这是我访问服务的代码:


API_URL="https://my-url.run.app/"

def create_signed_jwt(credentials_json, run_service_url):
    iat = time.time()
    exp = iat + 3600
    payload = {
        'iss': credentials_json['client_email'],
        'sub': credentials_json['client_email'],
        'target_audience': run_service_url,
        'aud': 'https://www.googleapis.com/oauth2/v4/token',
        'iat': iat,
        'exp': exp
    }
    additional_headers = {
        'kid': credentials_json['private_key_id']
    }

    signed_jwt = jwt.encode(
        payload,
        credentials_json['private_key'],
        headers=additional_headers,
        algorithm='RS256'
    )
    return signed_jwt


def exchange_jwt_for_token(signed_jwt):
    body = {
        'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'assertion': signed_jwt
    }
    token_request = requests.post(
        url='https://www.googleapis.com/oauth2/v4/token',
        headers={
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        data=urllib.parse.urlencode(body)
    )
    return token_request.json()['id_token']


def get_headers():
    """
    Creates the headers for each request to the API on google cloud run
    """
    credentials = {
      "type": "service_account",
      "project_id": "my-project-id",
      "private_key_id": my-key-id,
      "private_key": "-----BEGIN PRIVATE KEY----- very long token-----END PRIVATE KEY-----\n",
      "client_email": "[email protected]",
      "client_id": my-client-id,
      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      "token_uri": "https://oauth2.googleapis.com/token",
      "auth_provider_x509_cert_url": some-standard-url,
      "client_x509_cert_url": some-standard-url
    }
    token = exchange_jwt_for_token(create_signed_jwt(credentials, API_URL))
    return {
        "Authorization": f"Bearer {token}"
    }



def test_request_function():
    """ request example url"""
    response = requests.get(f'{API_URL}/health', get_headers())

print(test_request_function())

为什么不可能授权?

I deployed a service on Cloud Run where authentication is needed:

gcloud run deploy my-service --project my-project --image eu.gcr.io/my-project/rest-of-path --platform managed --region europe-west4 --no-allow-unauthenticated

This seems to work fine. However, when I try to access my service from another service (in my case it is Anvil), it gives me a Response [403], which means it refused to authorize it. My Service Account does have the right roles as far as I know: Cloud Run Invoker, Service Account Token Creator, Service Controller. Even if I to add the owner role, it's not working.

This is my code to access my service:


API_URL="https://my-url.run.app/"

def create_signed_jwt(credentials_json, run_service_url):
    iat = time.time()
    exp = iat + 3600
    payload = {
        'iss': credentials_json['client_email'],
        'sub': credentials_json['client_email'],
        'target_audience': run_service_url,
        'aud': 'https://www.googleapis.com/oauth2/v4/token',
        'iat': iat,
        'exp': exp
    }
    additional_headers = {
        'kid': credentials_json['private_key_id']
    }

    signed_jwt = jwt.encode(
        payload,
        credentials_json['private_key'],
        headers=additional_headers,
        algorithm='RS256'
    )
    return signed_jwt


def exchange_jwt_for_token(signed_jwt):
    body = {
        'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'assertion': signed_jwt
    }
    token_request = requests.post(
        url='https://www.googleapis.com/oauth2/v4/token',
        headers={
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        data=urllib.parse.urlencode(body)
    )
    return token_request.json()['id_token']


def get_headers():
    """
    Creates the headers for each request to the API on google cloud run
    """
    credentials = {
      "type": "service_account",
      "project_id": "my-project-id",
      "private_key_id": my-key-id,
      "private_key": "-----BEGIN PRIVATE KEY----- very long token-----END PRIVATE KEY-----\n",
      "client_email": "[email protected]",
      "client_id": my-client-id,
      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      "token_uri": "https://oauth2.googleapis.com/token",
      "auth_provider_x509_cert_url": some-standard-url,
      "client_x509_cert_url": some-standard-url
    }
    token = exchange_jwt_for_token(create_signed_jwt(credentials, API_URL))
    return {
        "Authorization": f"Bearer {token}"
    }



def test_request_function():
    """ request example url"""
    response = requests.get(f'{API_URL}/health', get_headers())

print(test_request_function())

Why is it not possible to authorize?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冷心人i 2025-02-11 01:20:13

我鼓励您考虑使用Google的 auth library (for python)或任何其他值得信赖的验证库生成JWT。

当您经历的是,制作JWT的工作很粗糙,即使您使其正常工作,您也可以支持支持代码,这可能会更好地留给他人。

请参阅:

import urllib

import google.auth.transport.requests
import google.oauth2.id_token

import os


endpoint=os.getenv("ENDPOINT")
audience=os.getenv("AUDIENCE")

req = urllib.request.Request(endpoint)

auth_req = google.auth.transport.requests.Request()
id_token = google.oauth2.id_token.fetch_id_token(auth_req, audience)

bearer = f"Bearer {id_token}"
print(bearer)

req.add_header("Authorization", bearer)
response = urllib.request.urlopen(req)
print(response.code)

注意您还可以使用应用程序默认凭据,使Google库将代码缩短。

Q=72491606
python3 -m venv venv
source venv/bin/activate
python3 -m pip install google-auth
python3 -m pip install requests

BILLING=...
PROJECT=stackoverflow-${Q}
REGION=...
ACCOUNT="tester"

EMAIL="${ACCOUNT}@${PROJECT}.iam.gserviceaccount.com"

gcloud projects create ${PROJECT}

gcloud beta billing projects link ${PROJECT} \
--billing-account=${BILLING}

gcloud services enable run.googleapis.com \
--project=${PROJECT}

gcloud iam service-accounts create ${ACCOUNT} \
--project=${PROJECT}

gcloud iam service-accounts keys create ${PWD}/${ACCOUNT}.json \
--iam-account=${EMAIL} \
--project=${PROJECT}

gcloud projects add-iam-policy-binding ${PROJECT} \
--member=serviceAccount:${ACCOUNT}@${PROJECT}.iam.gserviceaccount.com \
--role=roles/run.invoker

export GOOGLE_APPLICATION_CREDENTIALS=${PWD}/${ACCOUNT}.json

# Deploy Cloud Run example
gcloud run deploy hello \
--image="gcr.io/cloudrun/hello" \
--no-allow-unauthenticated \
--region=${REGION} \
--platform=managed \
--project=${PROJECT}

# Get ENDPOINT==AUDIENCE
export ENDPOINT=$(\
 gcloud run services describe hello \
  --project=${PROJECT} \
  --region=${REGION} \
  --format="value(status.url)")
export AUDIENCE=${ENDPOINT}

python3 main.py

产生一个身份令牌并希望( 200 )。

然后,您可以将身份令牌插入Eg https://jwt.io 进行检查。

I encourage you to consider using Google's auth library (for Python) or any other reputable auth library to generate the JWT.

As you're experiencing, crafting JWT's is gnarly and, even when you get it working, you're on the hook for supporting code that would probably be better left to others.

See: Authenticating Service-to-Service

import urllib

import google.auth.transport.requests
import google.oauth2.id_token

import os


endpoint=os.getenv("ENDPOINT")
audience=os.getenv("AUDIENCE")

req = urllib.request.Request(endpoint)

auth_req = google.auth.transport.requests.Request()
id_token = google.oauth2.id_token.fetch_id_token(auth_req, audience)

bearer = f"Bearer {id_token}"
print(bearer)

req.add_header("Authorization", bearer)
response = urllib.request.urlopen(req)
print(response.code)

NOTE You can also use Application Default Credentials with the Google library making the code shorter.

Q=72491606
python3 -m venv venv
source venv/bin/activate
python3 -m pip install google-auth
python3 -m pip install requests

BILLING=...
PROJECT=stackoverflow-${Q}
REGION=...
ACCOUNT="tester"

EMAIL="${ACCOUNT}@${PROJECT}.iam.gserviceaccount.com"

gcloud projects create ${PROJECT}

gcloud beta billing projects link ${PROJECT} \
--billing-account=${BILLING}

gcloud services enable run.googleapis.com \
--project=${PROJECT}

gcloud iam service-accounts create ${ACCOUNT} \
--project=${PROJECT}

gcloud iam service-accounts keys create ${PWD}/${ACCOUNT}.json \
--iam-account=${EMAIL} \
--project=${PROJECT}

gcloud projects add-iam-policy-binding ${PROJECT} \
--member=serviceAccount:${ACCOUNT}@${PROJECT}.iam.gserviceaccount.com \
--role=roles/run.invoker

export GOOGLE_APPLICATION_CREDENTIALS=${PWD}/${ACCOUNT}.json

# Deploy Cloud Run example
gcloud run deploy hello \
--image="gcr.io/cloudrun/hello" \
--no-allow-unauthenticated \
--region=${REGION} \
--platform=managed \
--project=${PROJECT}

# Get ENDPOINT==AUDIENCE
export ENDPOINT=$(\
 gcloud run services describe hello \
  --project=${PROJECT} \
  --region=${REGION} \
  --format="value(status.url)")
export AUDIENCE=${ENDPOINT}

python3 main.py

Yields an identity token and hopefully (200).

You can then plug the identity token into e.g. https://jwt.io to inspect it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文