Autenticação entre serviços

Pode implementar a autenticação entre serviços através de uma conta de serviço num serviço gRPC. Esta página demonstra a autenticação de serviço para serviço através de um exemplo completo, incluindo como configurar o Extensible Service Proxy (ESP) num serviço gRPC para suportar pedidos autenticados e como chamar o serviço a partir de um cliente gRPC.

Para que qualquer serviço faça chamadas autenticadas para uma API Cloud Endpoints, o serviço de chamada tem de ter uma conta de serviço e enviar um token de autorização na chamada. O autor da chamada tem de usar um token de ID Google ou um token da Web JSON (JWT) personalizado que seja assinado apenas pela conta de serviço do autor da chamada. O ESP valida se a reivindicação iss no JWT corresponde à definição issuer na configuração do serviço. O ESP não verifica as autorizações de gestão de identidade e acesso que foram concedidas na conta de serviço.

No nosso exemplo, configura e usa a forma mais simples de autenticação de serviço a serviço, em que o cliente usa a respetiva conta de serviço para gerar JWTs de autenticação. Google Cloud A abordagem para outros métodos de autenticação é semelhante, embora o processo do lado do cliente para obter tokens de autenticação válidos dependa do método de autenticação usado.

Antes de começar

Este guia usa o exemplo da livraria usado nos nossos tutoriais.

  1. Clone o repositório git onde o código de exemplo do gRPC está alojado:

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
    
  2. Altere o diretório de trabalho:

    cd python-docs-samples/endpoints/bookstore-grpc/
    
  3. Siga as instruções nos tutoriais para configurar um projeto, se ainda não tiver um.

Neste exemplo, usa a implementação no Google Kubernetes Engine, embora a configuração da autenticação seja a mesma para o Compute Engine.

No exemplo, existem dois projetos da Google Cloud Platform que são referenciados:

  • O projeto do produtor, que é o projeto proprietário do serviço Cloud Endpoints for gRPC.
  • O projeto do consumidor do serviço, que é o projeto proprietário do cliente gRPC.

Criar a conta de serviço e a chave do consumidor

Para criar a conta de serviço e a chave para o projeto de consumidor:

  1. Na Google Cloud consola, aceda a APIs e serviços.

    APIs e serviços

    Certifique-se de que está no seu projeto do consumidor.
  2. Na página Credenciais, na lista pendente Criar credenciais, selecione Chave de conta de serviço.
  3. Na página Criar chave da conta de serviço, se tiver uma conta de serviço que queira usar, selecione-a. Caso contrário, na lista pendente Conta de serviço, selecione Nova conta de serviço e escreva um nome de conta.

    É criado um ID da conta de serviço correspondente. Tome nota do ID, uma vez que é necessário nas secções seguintes. Por exemplo:

    service-account-name@YOUR_PROJECT_ID.iam.gserviceaccount.com
    
  4. Clique na lista pendente Função e selecione as seguintes funções:

    • Contas de serviço > Utilizador da conta de serviço
    • Contas de serviço > Criador de tokens de contas de serviço
  5. Certifique-se de que o tipo de chave JSON está selecionado.

  6. Clique em Criar. O ficheiro de chave JSON da conta de serviço é transferido para o seu computador local. Tome nota da localização e certifique-se de que está armazenada em segurança, porque é usada mais tarde para gerar tokens.

Configurar a autenticação para o serviço

Use o projeto do produtor para todos os passos nesta secção.

Configure a autenticação na configuração da API gRPC

A autenticação para o ESP está configurada na secção authentication do ficheiro YAML de configuração da API gRPC. A configuração com autenticação para este serviço de exemplo está em api_config_auth.yaml.

authentication:
  providers:
  - id: google_service_account
    # Replace SERVICE-ACCOUNT-ID with your service account's email address.
    issuer: SERVICE-ACCOUNT-ID
    jwks_uri: https://www.googleapis.com/robot/v1/metadata/x509/SERVICE-ACCOUNT-ID
  rules:
  # This auth rule will apply to all methods.
  - selector: "*"
    requirements:
      - provider_id: google_service_account

A secção providers especifica os fornecedores de autenticação que quer usar. Neste caso, quer usar uma conta de serviço Google como fornecedor de autenticação. A secção rules especifica que precisa de tokens deste fornecedor para aceder a todos os métodos do seu serviço.

Na sua própria cópia deste ficheiro do repositório clonado:

  • Altere MY_PROJECT_ID para o ID do seu projeto de produtor.
  • Altere SERVICE-ACCOUNT-ID na secção authentication (nos valores issuer e jwks_uri) para o ID da conta de serviço do consumidor que anotou na secção anterior. Isto indica ao ESP que quer conceder acesso ao seu serviço a utilizadores que forneçam tokens válidos desta conta de serviço específica.
  • Opcionalmente, adicione jwt_locations abaixo do elemento providers. Pode usar este valor para definir uma localização JWT personalizada. As localizações JWT predefinidas são os metadados Authorization (com o prefixo "Bearer ") e os metadados X-Goog-Iap-Jwt-Assertion.

Guarde o ficheiro para o passo seguinte.

Implemente a configuração e o serviço

Estes passos são os mesmos que em Introdução ao gRPC no GKE:

  1. Implemente a configuração do serviço nos Endpoints: tem de o fazer mesmo que o tenha feito para o tutorial, uma vez que se trata de uma configuração diferente. Tome nota do nome do serviço devolvido:

    gcloud endpoints services deploy api_descriptor.pb api_config_auth.yaml --project PRODUCER_PROJECT
    
  2. Crie um cluster de contentores e autentique kubectl no cluster, se ainda não o tiver feito.

  3. Implemente a API de exemplo e o ESP no cluster. Se estiver a usar projetos de produtor e consumidor separados, certifique-se primeiro de que definiu o projeto adequado na ferramenta de linha de comandos:gcloud

    gcloud config set project PRODUCER_PROJECT
    

Chamar métodos autenticados a partir de um cliente gRPC

Por último, no lado do cliente, pode usar a chave da conta de serviço para gerar um token JWT e, em seguida, usar o token para chamar um método autenticado da livraria. Primeiro, instale os requisitos do Python adequados para gerar o token e executar o cliente de exemplo. Certifique-se de que está na pasta python-docs-samples/endpoints/bookstore-grpc do cliente clonado e, em seguida:

virtualenv bookstore-env
source bookstore-env/bin/activate
pip install -r requirements.txt

Gere um token JWT

Neste exemplo, a livraria está a usar a autenticação de serviço para serviço, em que o serviço de chamada é autenticado puramente pela respetiva conta de serviço, pelo que é simples criar um token adequado para enviar com os nossos pedidos. Tenha em atenção que também pode exigir uma autenticação mais rigorosa de serviço para serviço, em que o token gerado tem de ser autenticado posteriormente pela Google (através de um token de ID da Google).

Para este exemplo, o script Python fornecido pode gerar um token a partir do ficheiro de chave JSON transferido anteriormente, usando um ID de utilizador e um email fictícios.

#!/usr/bin/env python

# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Example of generating a JWT signed from a service account file."""

import argparse
import json
import time

import google.auth.crypt
import google.auth.jwt

"""Max lifetime of the token (one hour, in seconds)."""
MAX_TOKEN_LIFETIME_SECS = 3600


def generate_jwt(service_account_file, issuer, audiences):
    """Generates a signed JSON Web Token using a Google API Service Account."""
    with open(service_account_file) as fh:
        service_account_info = json.load(fh)

    signer = google.auth.crypt.RSASigner.from_string(
        service_account_info["private_key"], service_account_info["private_key_id"]
    )

    now = int(time.time())

    payload = {
        "iat": now,
        "exp": now + MAX_TOKEN_LIFETIME_SECS,
        # aud must match 'audience' in the security configuration in your
        # swagger spec. It can be any string.
        "aud": audiences,
        # iss must match 'issuer' in the security configuration in your
        # swagger spec. It can be any string.
        "iss": issuer,
        # sub and email are mapped to the user id and email respectively.
        "sub": issuer,
        "email": "user@example.com",
    }

    signed_jwt = google.auth.jwt.encode(signer, payload)
    return signed_jwt


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
    )
    parser.add_argument("--file", help="The path to your service account json file.")
    parser.add_argument("--issuer", default="", help="issuer")
    parser.add_argument("--audiences", default="", help="audiences")

    args = parser.parse_args()

    signed_jwt = generate_jwt(args.file, args.issuer, args.audiences)
    print(signed_jwt.decode("utf-8"))

Para gerar um token através do script:

  • Gere um token JWT e atribua-o à variável $JWT_TOKEN:

    JWT_TOKEN=$(python jwt_token_gen.py \
        --file=[SERVICE_ACCOUNT_FILE] \
        --audiences=[SERVICE_NAME] \
        --issuer=[SERVICE-ACCOUNT-ID])
    

    where:

    • [SERVICE_ACCOUNT_FILE] é o ficheiro de chave JSON da conta de serviço do consumidor transferido.
    • [SERVICE_NAME] é o nome do serviço Bookstore que foi devolvido quando implementou a respetiva configuração de serviço atualizada nos Endpoints.
    • [SERVICE-ACCOUNT-ID] é o ID completo da conta de serviço de consumidor quando gerou a sua conta de serviço.

Faça uma chamada gRPC autenticada

Este último passo usa o bookstore_client.py, que é o mesmo cliente usado nos tutoriais. Para fazer uma chamada autenticada, o cliente transmite o JWT como metadados com a respetiva chamada de método.

def run(
    host, port, api_key, auth_token, timeout, use_tls, servername_override, ca_path

Para executar o exemplo:

  1. Use kubectl get services para obter o endereço IP externo da livraria implementada:

    #kubectl get services
    NAME                 CLUSTER-IP      EXTERNAL-IP      PORT(S)           AGE
    echo                 10.11.246.240   104.196.186.92   80/TCP            10d
    endpoints            10.11.243.168   104.196.210.50   80/TCP,8090/TCP   10d
    esp-grpc-bookstore   10.11.254.34    104.196.60.37    80/TCP            1d
    kubernetes           10.11.240.1     <none>           443/TCP           10d
    

    Neste caso, é o serviço esp-grpc-bookstore e o respetivo IP externo é 104.196.60.37.

  2. Atribua o endereço IP à variável EXTERNAL_IP

    EXTERNAL_IP=104.196.60.37
    
  3. Liste todas as prateleiras do serviço Bookstore:

    python bookstore_client.py --port=80 --host=$EXTERNAL_IP --auth_token=$JWT_TOKEN
    

    O serviço devolve todas as prateleiras na livraria atual. Pode verificar novamente se não fornecer um token ou se especificar o ID da conta de serviço errado ao gerar o JWT. O comando deve falhar.

O que se segue?