использование python для аутентификации в конечной точке API вычислений GCP - PullRequest
3 голосов
/ 28 июня 2019

Моя цель - воспроизвести / воспроизвести функциональность gcloud compute addresses create без зависимости от двоичного файла gcloud.

Я пытаюсь использовать python для аутентификации POST в конечной точке вычисления googleapis в соответствии с документацией https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address о резервировании статического внешнего IP-адреса

Но мой POST возвращается каждый раз 401.

Я создал JWT из модуля Python google.auth.jwt, и когда я декодирую его, в JWT встроены все строки, которые я ожидаю увидеть.

Я также попытался включить в JWT комбинации следующих областей OAuth: - "https://www.googleapis.com/auth/userinfo.email" - "https://www.googleapis.com/auth/compute" - "https://www.googleapis.com/auth/cloud-platform"

это моя функция для получения JWT с использованием информации в файле ключа JSON моей учетной записи службы

def _generate_jwt( tokenPath, expiry_length=3600 ):
    now = int(time.time())
    tokenData = load_json_data( tokenPath )
    sa_email = tokenData['client_email']
    payload = {
        'iat': now, 
        # expires after 'expiry_length' seconds.
        "exp": now + expiry_length,
        'iss': sa_email,
        "scope": " ".join( [
            "https://www.googleapis.com/auth/cloud-platform",
            "https://www.googleapis.com/auth/compute",
            "https://www.googleapis.com/auth/userinfo.email"
        ] ),
        'aud': "https://www.googleapis.com/oauth2/v4/token",
        'email': sa_email
    }
    # sign with keyfile
    signer = google.auth.crypt.RSASigner.from_service_account_file( tokenPath )
    jwt = google.auth.jwt.encode(signer, payload)

    return jwt

как только у меня есть JWT, я делаю следующее сообщение, которое терпит неудачу, 401, ::

    gapiURL = 'https://www.googleapis.com/compute/v1/projects/' + projectID + '/regions/' + region + '/addresses'
    jwtToken = _generate_jwt( servicetoken )
    headers = {  
        'Authorization': 'Bearer {}'.format( jwtToken ),
        'content-type' : 'application/json',
    }    
    post = requests.post( url=gapiURL, headers=headers, data=data ) 
    post.raise_for_status()
    return post.text

Я получил 401 независимо от того, сколько комбинаций областей я использовал в JWT или разрешений, предоставленных моей учетной записи службы. Что я делаю не так?

edit : большое спасибо @JohnHanley за указание на то, что я пропускаю следующий / второй запрос POST для https://www.googleapis.com/oauth2/v4/token в последовательности аутентификации GCP. Итак, вы получаете JWT для получения токена доступа.

Я изменил свои звонки, чтобы использовать модуль python jwt, а не модуль google.auth.jwt, встроенный в google.auth.crypt.RSASigner. Так что код немного проще, и я поместил его в один метод

## serviceAccount auth sequence for google :: JWT -> accessToken
def gke_get_token( serviceKeyDict, expiry_seconds=3600 ):

    epoch_time = int(time.time())
    # Generate a claim from the service account file.
    claim = {
        "iss": serviceKeyDict["client_email"],
        "scope": " ".join([
            "https://www.googleapis.com/auth/cloud-platform",
            "https://www.googleapis.com/auth/userinfo.email"
        ]),
        "aud": "https://www.googleapis.com/oauth2/v4/token",
        "exp": epoch_time + expiry_seconds,
        "iat": epoch_time
    }    
    # Sign claim with JWT.
    assertion = jwt.encode( claim, serviceKeyDict["private_key"], algorithm='RS256' ).decode() 
    data = urllib.urlencode( {
        "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
        "assertion": assertion
    } )  
    # Request the access token.
    result = requests.post(
        url="https://www.googleapis.com/oauth2/v4/token",
        headers={
            "Content-Type": "application/x-www-form-urlencoded"
        },
        data=data
    )    
    result.raise_for_status()
    return loadJsonData(result.text)["access_token"]



1 Ответ

2 голосов
/ 03 июля 2019

В Google Cloud есть три типа «токенов», которые предоставляют доступ:

  • Подпись JWT
  • Токен доступа
  • Идентификационный токен

В вашем случае вы создали Signed JWT. Несколько сервисов Google принимают этот токен. Большинство нет.

После создания подписанного JWT следующим шагом будет вызов конечной точки Google OAuth и обмен на токен доступа. Я написал статью, которая подробно описывает это:

Google Cloud - Создание маркеров доступа OAuth для вызовов API REST

Некоторые службы Google теперь принимают Identity Tokens. Это называется Identity Based Access Control (IBAC). Это не относится к вашему вопросу, но это тенденция будущего в Google Cloud Authorization. Примером может служить моя статья о Cloud Run + Cloud Storage + KMS:

Google Cloud - Go - управление доступом на основе идентификаторов

В следующем примере кода Python показано, как обмениваться токенами:

def exchangeJwtForAccessToken(signed_jwt):
    '''
    This function takes a Signed JWT and exchanges it for a Google OAuth Access Token
    '''

    auth_url = "https://www.googleapis.com/oauth2/v4/token"

    params = {
        "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
        "assertion": signed_jwt
    }

    r = requests.post(auth_url, data=params)

    if r.ok:
        return(r.json()['access_token'], '')

    return None, r.text
...