Autenticación entre servicios

Puedes implementar la autenticación entre servicios mediante una cuenta de servicio en un servicio gRPC. En esta página se muestra la autenticación entre servicios mediante un ejemplo completo, que incluye cómo configurar el Extensible Service Proxy (ESP) en un servicio gRPC para admitir solicitudes autenticadas y cómo llamar al servicio desde un cliente gRPC.

Para que un servicio pueda hacer llamadas autenticadas a una API de Cloud Endpoints, el servicio que llama debe tener una cuenta de servicio y enviar un token de autenticación en la llamada. La persona que llama debe usar un token de ID de Google o un JSON Web Token (JWT) personalizado que solo esté firmado por la cuenta de servicio de la persona que llama. ESP valida que la reclamación iss del JWT coincida con el ajuste issuer de la configuración del servicio. ESP no comprueba los permisos de Gestión de Identidades y Accesos que se han concedido en la cuenta de servicio.

En nuestro ejemplo, configuramos y usamos la forma más sencilla de autenticación de servicio a servicio, en la que el cliente usa su cuenta de servicio Google Cloud para generar JWTs de autenticación. El proceso de otros métodos de autenticación es similar, aunque el proceso del lado del cliente para obtener tokens de autenticación válidos depende del método de autenticación utilizado.

Antes de empezar

En esta guía se usa el ejemplo de librería que se utiliza en nuestros tutoriales.

  1. Clona el repositorio de Git donde se aloja el código de ejemplo de gRPC:

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
    
  2. Cambia el directorio de trabajo:

    cd python-docs-samples/endpoints/bookstore-grpc/
    
  3. Sigue las instrucciones de los tutoriales para configurar un proyecto si aún no tienes ninguno.

En este ejemplo, se usa el despliegue en Google Kubernetes Engine, aunque la configuración de la autenticación es la misma para Compute Engine.

En el ejemplo, se hace referencia a dos proyectos de Google Cloud Platform:

  • El proyecto del productor del servicio, que es el proyecto propietario del servicio Cloud Endpoints para gRPC.
  • El proyecto del consumidor del servicio, que es el proyecto propietario del cliente gRPC.

Crear la cuenta de servicio y la clave de consumidor

Para crear la cuenta de servicio y la clave del proyecto de consumidor, sigue estos pasos:

  1. En la Google Cloud consola, ve a APIs y servicios.

    APIs y servicios

    Asegúrate de que estás en tu proyecto de consumidor.
  2. En la página Credenciales, en la lista desplegable Crear credenciales, selecciona Clave de cuenta de servicio.
  3. En la página Crear clave de cuenta de servicio, selecciona la cuenta de servicio que quieras usar. De lo contrario, en la lista desplegable Cuenta de servicio, selecciona Nueva cuenta de servicio y escribe un nombre para la cuenta.

    Se creará un ID de cuenta de servicio correspondiente. Anota el ID, ya que lo necesitarás en las secciones siguientes. Por ejemplo:

    service-account-name@YOUR_PROJECT_ID.iam.gserviceaccount.com
    
  4. Haz clic en la lista desplegable Rol y selecciona los siguientes roles:

    • Cuentas de servicio > Usuario de cuenta de servicio
    • Cuentas de servicio > Creador de tokens de cuenta de servicio
  5. Asegúrate de que esté seleccionado el tipo de clave JSON.

  6. Haz clic en Crear. El archivo JSON de la clave de tu cuenta de servicio se descarga en tu máquina local. Anota la ubicación y asegúrate de que esté almacenada de forma segura, ya que se usará más adelante para generar tokens.

Configurar la autenticación del servicio

Usa el proyecto de productor en todos los pasos de esta sección.

Configurar la autenticación en la configuración de la API gRPC

La autenticación de ESP se configura en la sección authentication del archivo YAML de configuración de la API gRPC. La configuración con autenticación de este servicio de ejemplo se encuentra en 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

En la sección providers se especifican los proveedores de autenticación que quieres usar. En este caso, se indica que quieres usar una cuenta de servicio de Google como proveedor de autenticación. La sección rules especifica que necesitas tokens de este proveedor para acceder a todos los métodos de tu servicio.

En tu copia de este archivo del repositorio clonado:

  • Cambia MY_PROJECT_ID por el ID de tu proyecto productor.
  • Cambia SERVICE-ACCOUNT-ID en la sección authentication (en los valores de issuer y jwks_uri) por el ID de la cuenta de servicio de consumidor que has anotado en la sección anterior. De esta forma, se indica al ESP que quieres conceder acceso a tu servicio a los usuarios que proporcionen tokens válidos de esta cuenta de servicio concreta.
  • También puedes añadir jwt_locations en el elemento providers. Puede usar este valor para definir una ubicación de JWT personalizada. Las ubicaciones predeterminadas de JWT son los metadatos Authorization (con el prefijo "Bearer ") y los metadatos X-Goog-Iap-Jwt-Assertion.

Guarda el archivo para el siguiente paso.

Desplegar la configuración y el servicio

Estos pasos son los mismos que en la sección Empezar a usar gRPC en GKE:

  1. Despliega la configuración de tu servicio en Endpoints: debes hacerlo aunque ya lo hayas hecho en el tutorial, ya que se trata de una configuración diferente. Anota el nombre del servicio devuelto:

    gcloud endpoints services deploy api_descriptor.pb api_config_auth.yaml --project PRODUCER_PROJECT
    
  2. Crea un clúster de contenedores y autentícate kubectl en el clúster, si aún no lo has hecho.

  3. Despliega la API de ejemplo y ESP en el clúster. Si usas proyectos de productor y de consumidor independientes, primero asegúrate de haber definido el proyecto adecuado en la herramienta de línea de comandos gcloud:

    gcloud config set project PRODUCER_PROJECT
    

Llamar a métodos autenticados desde un cliente gRPC

Por último, en el lado del cliente, puedes usar la clave de cuenta de servicio para generar un token JWT y, a continuación, usar el token para llamar a un método de Bookstore autenticado. Primero, instala los requisitos de Python adecuados para generar el token y ejecutar el cliente de ejemplo. Asegúrate de que estás en la carpeta python-docs-samples/endpoints/bookstore-grpc de tu cliente clonado y haz lo siguiente:

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

Generar un token JWT

En este ejemplo, la librería usa la autenticación de servicio a servicio, donde el servicio de llamada se autentica únicamente mediante su cuenta de servicio, por lo que es sencillo crear un token adecuado para enviarlo con nuestras solicitudes. Ten en cuenta que también puedes requerir una autenticación de servicio a servicio más estricta, en la que Google debe autenticar el token generado (mediante un token de ID de Google).

En este ejemplo, la secuencia de comandos de Python proporcionada puede generar un token a partir del archivo de clave JSON descargado anteriormente, usando un ID de usuario y un correo electrónico ficticios.

#!/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 generar un token con la secuencia de comandos, sigue estos pasos:

  • Genera un token JWT y asígnalo a la variable $JWT_TOKEN:

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

    donde:

    • [SERVICE_ACCOUNT_FILE] es el archivo de clave JSON de la cuenta de servicio de consumidor descargado.
    • [SERVICE_NAME] es el nombre del servicio Bookstore que se devolvió cuando desplegaste su configuración de servicio actualizada en Endpoints.
    • [SERVICE-ACCOUNT-ID] es el ID completo de la cuenta de servicio de consumidor cuando generaste tu cuenta de servicio.

Hacer una llamada gRPC autenticada

En este último paso se usa bookstore_client.py, que es el mismo cliente que se usa en los tutoriales. Para hacer una llamada autenticada, el cliente envía el JWT como metadatos con su llamada al método.

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

Para ejecutar el ejemplo, sigue estos pasos:

  1. Usa kubectl get services para obtener la dirección IP externa de la librería desplegada:

    #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
    

    En este caso, se trata del servicio esp-grpc-bookstore y su IP externa es 104.196.60.37.

  2. Asigna la dirección IP a la variable EXTERNAL_IP

    EXTERNAL_IP=104.196.60.37
    
  3. Lista de todas las estanterías del servicio Bookstore:

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

    El servicio devuelve todas las estanterías de la librería actual. Puedes comprobarlo si no proporcionas un token o si especificas el ID de cuenta de servicio incorrecto al generar el JWT. El comando debería fallar.

Siguientes pasos