Autenticação entre serviços

É possível implementar autenticação entre serviços usando uma conta de serviço em um serviço gRPC. Nesta página, mostramos como usar a autenticação serviço a serviço com um exemplo completo, inclusive como configurar o Extensible Service Proxy (ESP) em um serviço gRPC para dar suporte a solicitações autenticadas e como chamar o serviço em um cliente gRPC.

Para que qualquer serviço faça chamadas autenticadas para uma API do Cloud Endpoints, ele precisa ter uma conta de serviço e enviar um token de autenticação na chamada. O autor da chamada precisa usar um token de código do Google ou um JSON Web Token (JWT) personalizado e assinado somente pela conta de serviço do autor da chamada. O ESP valida que a declaração iss no JWT corresponde à definição de issuer na configuração do serviço. O ESP não verifica as permissões do Cloud Identity and Access Management que foram concedidas na conta de serviço.

No exemplo, você configura e usa a forma mais simples de autenticação serviço a serviço em que o cliente usa a conta de serviço do Google Cloud Platform (GCP) para gerar JWTs de autenticação. A abordagem por outros métodos de autenticação é semelhante, mas o processo no lado do cliente para conseguir tokens de autenticação válidos depende do método usado para isso.

Antes de começar

Este guia usa o exemplo Bookstore que aparece em nossos tutoriais.

  1. Clone o repositório "git" no local em que o código de exemplo do gRPC está hospedado:

    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 caso ainda não tenha um.

Neste exemplo, usaremos a implantação no Google Kubernetes Engine, ainda que a configuração de autenticação seja a mesma para o Compute Engine.

No exemplo, são mencionados dois projetos do Google Cloud Platform:

  • o projeto de produtor de serviços, que é proprietário do serviço Cloud Endpoints para gRPC;
  • o projeto de consumidor de serviços, que é proprietário do cliente gRPC.

Como criar a conta de serviço e a chave do consumidor

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

  1. No Console do Google Cloud Platform, acesse APIs e serviços.

    APIs e serviços

    Verifique se você está no seu projeto de consumidor.
  2. Na página Credenciais, na lista suspensa Criar credenciais, selecione Chave da conta de serviço.
  3. Na página Criar chave da conta de serviço, selecione a conta de serviço atual que você quer usar. Caso contrário, na lista suspensa da Conta de serviço, selecione Nova conta de serviço e digite um nome de conta.

    Será criado um código de conta de serviço correspondente. Anote o código, que será necessário nas próximas seções. Exemplo:

    service-account-name@YOUR_PROJECT_ID.iam.gserviceaccount.com
    
  4. Clique na lista suspensa Papel e selecione o seguinte:

    • Contas de serviço > Usuário da conta de serviço
    • Contas de serviço > Criador do token da conta de serviço
  5. Verifique se o tipo de chave JSON está selecionado.

  6. Clique em Criar. O download do arquivo de chave JSON da conta de serviço é feito em sua máquina local. Observe a localização e verifique se o arquivo está armazenado com segurança. Ele será necessário posteriormente para a geração de tokens.

Como configurar a autenticação do serviço

Use o projeto de produtor em todas as etapas nesta seção.

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

A autenticação para o ESP é configurada na seção authentication do arquivo YAML de configuração da API gRPC. No serviço de exemplo, a configuração com autenticação 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 seção providers especifica os provedores de autenticação que você quer usar. Neste caso, você quer usar uma conta de serviço do Google como provedor de autenticação. A seção rules especifica que você requer tokens desse provedor para conceder acesso a todos os métodos do seu serviço.

Em sua cópia desse arquivo do repositório clonado:

  • Altere MY_PROJECT_ID para o código do projeto de produtor.
  • Na seção authentication, altere SERVICE-ACCOUNT-ID nos valores issuer e jwks_uri para o código da conta de serviço do consumidor anotado na seção anterior. Isso informa ao ESP que você quer conceder acesso ao serviço aos usuários que informarem tokens válidos a partir desta conta de serviço específica.

Salve o arquivo para a próxima etapa.

Implantar a configuração e o serviço

Estas etapas são as mesmas da Introdução ao gRPC no GKE:

  1. Implemente sua configuração de serviço no Endpoints: como essa é uma configuração diferente, faça isso mesmo que já o tenha feito para o tutorial. Observe o nome de serviço retornado:

    gcloud endpoints services deploy api_descriptor.pb api_config_auth.yaml --project PRODUCER_PROJECT
    
  2. Crie um cluster de contêiner e autentique o kubectl para o cluster se ainda não tiver feito isso.

  3. Implante a API de amostra e o ESP no cluster. Se estiver usando projetos separados de produtor e consumidor, confirme antes se definiu o projeto apropriado na ferramenta de linha de comando gcloud.

    gcloud config set project PRODUCER_PROJECT
    

Como chamar métodos autenticados a partir de um cliente gRPC

Por fim, no lado do cliente, é possível usar a chave da conta de serviço para gerar um token JWT e usá-lo para chamar um método de Bookstore autenticado. Primeiro instale os requisitos apropriados do Python para gerar o token e executar o cliente de exemplo. Verifique se você 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

Gerar um token JWT

Neste exemplo, a Bookstore está usando a autenticação serviço a serviço em que o serviço que faz a chamada é simplesmente autenticado pela respectiva conta de serviço. Isso simplifica a criação de um token apropriado para enviar com nossas solicitações. Observe que também é possível exigir uma autenticação serviço a serviço mais restrita, em que o token gerado precisa ser autenticado também pelo Google (usando um token de código do Google).

Nste exemplo, o script Python fornecido gera um token do arquivo de chave JSON, de que foi feito o download anteriormente, usando e-mail de usuário e código fictícios.

#!/usr/bin/env python

# Copyright 2016 Google Inc. All Rights Reserved.
#
# 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, 'r') 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)

Para gerar um token usando o 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])
    

    em que:

    • [SERVICE_ACCOUNT_FILE] é o arquivo de chave JSON da conta de serviço de consumidor de que foi feito o download;
    • [SERVICE_NAME] foi o nome do serviço Bookstore retornado quando você implantou sua configuração de serviço atualizada no Endpoints;
    • [SERVICE-ACCOUNT-ID] é o código completo da conta de serviço de consumidor quando você gerou sua conta de serviço.

Fazer uma chamada gRPC autenticada

Nesta última etapa, é usado bookstore_client.py, que é o mesmo cliente utilizado nos tutoriais. Para fazer uma chamada autenticada, o cliente transmite o JWT como metadados com a respectiva chamada de método.

def run(host, port, api_key, auth_token, timeout):
    """Makes a basic ListShelves call against a gRPC Bookstore server."""

    channel = grpc.insecure_channel('{}:{}'.format(host, port))

    stub = bookstore_pb2_grpc.BookstoreStub(channel)
    metadata = []
    if api_key:
        metadata.append(('x-api-key', api_key))
    if auth_token:
        metadata.append(('authorization', 'Bearer ' + auth_token))
    shelves = stub.ListShelves(empty_pb2.Empty(), timeout, metadata=metadata)
    print('ListShelves: {}'.format(shelves))

Para executar o exemplo:

  1. Use kubectl get services para receber o endereço IP externo da Bookstore implantada:

    #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 104.196.60.37 é o IP externo.

  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 retorna todas as prateleiras na Bookstore atual. É possível confirmar isso: se você não fornecer um token nem especificar um código incorreto da conta de serviço ao gerar o JWT, haverá uma falha no comando.

A seguir

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Cloud Endpoints com gRPC
Precisa de ajuda? Acesse nossa página de suporte.