Configura un proxy de Pub/Sub para clientes móviles en GKE


En este instructivo, se muestra cómo publicar mensajes desde apps para dispositivos móviles o del cliente en Pub/Sub mediante un proxy que controla la lógica de autenticación y autorización en lugar de las credenciales del cliente.

Si bien puedes autenticar mensajes del cliente a Pub/Sub con la Identity and Access Management (IAM), estas credenciales de larga duración nunca caducan. En las apps del cliente, estas credenciales se pueden descubrir mediante técnicas como la separación de aplicaciones y la ingeniería inversa.

En su lugar, puedes descargar la lógica de autenticación y autorización a un proxy que realice las siguientes tareas:

  • Autenticar las solicitudes entrantes para validar al usuario
  • Reenviar las solicitudes a Pub/Sub junto con los permisos de IAM adecuados

En este instructivo, se muestra cómo implementar un proxy de Pub/Sub en Google Kubernetes Engine (GKE). Este instructivo está dirigido a desarrolladores de aplicaciones y arquitectos de sistemas que definen e implementan el diseño para aplicaciones móviles o del cliente. Se supone que comprendes los conceptos fundamentales de Kubernetes y que estás familiarizado con Cloud Endpoints.

Flujo de solicitudes para este instructivo

Para comprender cómo encaja Pub/Sub en una canalización de transmisión, considera un análisis de flujo de clics. En este caso práctico, te recomendamos que comprendas cómo interactúan los usuarios con la app para dispositivos móviles. Si deseas obtener estas estadísticas, captura la actividad del usuario en tiempo real. En el siguiente diagrama, se muestra el flujo de datos.

El proxy de Pub/Sub recibe mensajes del cliente antes de que se agreguen los datos.

Los datos que captura la app se envían a Pub/Sub a través de un proxy. Pub/Sub puede tener suscriptores en sentido descendente, como Dataflow o Dataproc, que agregan los datos para que puedas realizar un análisis significativo.

En el siguiente diagrama, se muestra una vista detallada del flujo de solicitud que sigue este instructivo.

Cómo interactúan los componentes de canalización en una solicitud de usuario

En las siguientes secciones, se explica cómo interactúan los distintos componentes de este diagrama.

Autenticación de usuarios

Las aplicaciones para dispositivos móviles pueden usar varios métodos a fin de autenticar a los usuarios. El flujo de autenticación es específico de tu app. En este instructivo, se muestra una de esas soluciones para autenticar usuarios. Este instructivo está acompañado de una implementación de esta solución.

Solicitudes de la app cliente al proxy de Pub/Sub

El backend de la app genera un token de autenticación de corta duración que el cliente almacena de forma local (por ejemplo, mediante el sistema Android Keystore o los servicios de llavero de iOS). En este instructivo, se usan tokens de ID de OpenID Connect (OIDC) para autenticar la app cliente. Google emite y firma el token de ID de OIDC.

La app del lado del cliente envía una solicitud al proxy de Pub/Sub mediante el token de ID de OIDC. El proxy de Pub/Sub valida el token y reenvía la solicitud a Pub/Sub junto con las credenciales de IAM adecuadas.

Publica mensajes

Una vez que la app cliente se autentica de forma correcta, el proxy de Pub/Sub envía una solicitud de publicación a Pub/Sub. Con la IAM, Pub/Sub ayuda a garantizar que el emisor (el proxy de Pub/Sub) tenga los permisos correspondientes para enviar solicitudes de publicación. En este instructivo, el proxy de Pub/Sub usa la cuenta de servicio predeterminada de Compute Engine para autenticarse con Pub/Sub. La cuenta de servicio predeterminada de Compute Engine tiene la función de IAM de Editor (roles/editor), que brinda acceso de publicador al proxy de Pub/Sub.

Objetivos

  • Crear un clúster de GKE para ejecutar un proxy de Pub/Sub
  • Crear un tema de Pub/Sub
  • Implementar el proxy de Pub/Sub
  • Configurar Endpoints para autenticar solicitudes en el proxy de Pub/Sub
  • Verificar que los mensajes se publiquen en Pub/Sub

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

  1. En la consola de Google Cloud, ve a la página del selector de proyectos.

    Ir al selector de proyectos

  2. Selecciona o crea un proyecto de Google Cloud.

  3. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  4. En la consola de Google Cloud, activa Cloud Shell.

    Activar Cloud Shell

    En la parte inferior de la consola de Google Cloud, se inicia una sesión de Cloud Shell en la que se muestra una ventana de línea de comandos. Cloud Shell es un entorno de shell con Google Cloud CLI ya instalada y con valores ya establecidos para el proyecto actual. La sesión puede tardar unos segundos en inicializarse.

  5. Define las variables de entorno que necesitas para este instructivo:
        export PROJECT=$(gcloud config get-value project)
        export REGION=us-central1
        export ZONE=${REGION}-b
        export CLUSTER=pubsub-proxy
        export TOPIC=proxy-test
        export SERVICE_ACCOUNT=publish-test
        export ENDPOINTS_SERVICE="pubtest.endpoints.${PROJECT}.cloud.goog"
        export GENERATE_TOKEN="https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts"
  6. Habilita las APIs para Cloud Build, Compute Engine, Google Kubernetes Engine, Artifact Analysis, Container Registry, Endpoints, Administración de servicios, Control de servicios y Pub/Sub:
        gcloud services enable \
            cloudbuild.googleapis.com \
            compute.googleapis.com \
            container.googleapis.com \
            containeranalysis.googleapis.com \
            containerregistry.googleapis.com \
            endpoints.googleapis.com \
            servicemanagement.googleapis.com \
            servicecontrol.googleapis.com \
            pubsub.googleapis.com

Crea un tema de Pub/Sub

  • En Cloud Shell, crea un tema de Pub/Sub en el que publiques mensajes:

    gcloud pubsub topics create $TOPIC
    

Crea un clúster de GKE

  1. En Cloud Shell, crea un clúster de GKE:

    gcloud container clusters create $CLUSTER \
        --zone $ZONE \
        --scopes "https://www.googleapis.com/auth/cloud-platform"
    
  2. Obtén credenciales para el clúster en ejecución:

    gcloud container clusters get-credentials $CLUSTER \
        --zone $ZONE \
        --project $PROJECT
    

Compila una imagen de contenedor

  1. En Cloud Shell, clona el repositorio de código:

    git clone https://github.com/GoogleCloudPlatform/solutions-pubsub-proxy-rest
    
  2. Usa Cloud Build para compilar una imagen de contenedor desde la fuente y, luego, guárdala en Container Registry:

    cd solutions-pubsub-proxy-rest && \
        gcloud builds submit --tag gcr.io/$PROJECT/pubsub-proxy:v1
    

Crea una dirección IP externa estática

  1. En Cloud Shell, crea una dirección IP externa estática que luego se asigne al balanceador de cargas del proxy de Pub/Sub:

    gcloud compute addresses create service-ip --region $REGION
    
  2. Almacena la dirección IP estática en una variable de entorno, PROXY_IP:

    PROXY_IP=$(gcloud compute addresses describe service-ip \
        --region $REGION --format='value(address)')
    

Implementa Endpoints

El proxy de Pub/Sub usa Endpoints para autenticar las solicitudes de los usuarios. Endpoints usa el proxy de servicio extensible (ESP) para proporcionar funciones de administración de API como autenticación, supervisión, seguimiento y administración del ciclo de vida de la API. En este instructivo, se usa Endpoints solo para autenticar solicitudes entrantes al proxy de Pub/Sub.

En este instructivo, implementarás el ESP como un archivo adicional con el proxy de Pub/Sub. El ESP intercepta y autentica las solicitudes entrantes antes de reenviarlas al proxy de Pub/Sub.

  1. En Cloud Shell, reemplaza el marcador de posición [PROJECT_ID] por el ID de tu proyecto de Google Cloud en el archivo openapi.yaml:

    sed -i -e "s/\[PROJECT_ID\]/$PROJECT/g" openapi.yaml
    
  2. En el archivo de manifiesto de OpenAPI, reemplaza el marcador de posición [IP_ADDRESS] por el valor de PROXY_IP:

    sed -i -e "s/\[IP_ADDRESS\]/$PROXY_IP/g" openapi.yaml
    
  3. Implementa la definición del servicio de OpenAPI en Endpoints:

    gcloud endpoints services deploy openapi.yaml
    

    El comando anterior crea lo que se muestra a continuación:

    • Un servicio administrado con el nombre que especificaste en el campo de host del archivo openapi.yaml (pubtest.endpoints.project-id.cloud.goog), en el que project-id es el ID de tu proyecto de Google Cloud.
    • Un registro A de DNS que usa el nombre del servicio y la asignación de dirección IP del balanceador de cargas del proxy de Pub/Sub que se define en la extensión x-google-endpoints en el archivo openapi.yaml.

    Durante la implementación, verás una advertencia que puedes ignorar, ya que en este instructivo se usan tokens de ID de OIDC para la autenticación en lugar de claves de API.

    WARNING: openapi.yaml: Operation 'post' in path '/publish': Operation does
    not require an API key; callers may invoke the method without specifying an
    associated API-consuming project. To enable API key all the
    SecurityRequirement Objects
    (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-requirement-object)
    inside security definition must reference at least one SecurityDefinition
    of type : 'apiKey'.
    
  4. Comprueba si el servicio se implementó de forma correcta:

    gcloud endpoints services describe ${ENDPOINTS_SERVICE}
    

    El resultado es similar al siguiente:

    [...]
    producerProjectId: project-id
    serviceConfig:
      documentation:
        summary: Pub/Sub proxy exposed as an Endpoint API
    [...]
      name: pubtest.endpoints.project-id.cloud.goog
      title: PubSub Proxy
      usage: {}
    serviceName: pubtest.endpoints.project-id.cloud.goog
    

    En el resultado verás lo siguiente:

    • project-id: El ID del proyecto de Google Cloud.

Implementa un proxy

  1. En Cloud Shell, genera un certificado SSL autofirmado para permitir conexiones HTTPS al proxy.

    openssl req -x509 -nodes -days 365 \
        -newkey rsa:2048 -keyout ./nginx.key \
        -out ./nginx.crt \
        -subj "/CN=${ENDPOINTS_SERVICE}"
    
  2. Crea un secreto de Kubernetes mediante el certificado SSL y la clave privada:

    kubectl create secret generic nginx-ssl \
        --from-file=./nginx.crt \
        --from-file=./nginx.key
    
  3. Reemplaza el marcador de posición [PROJECT_ID] en el archivo de manifiesto de la implementación por el ID del proyecto de Google Cloud:

    sed -i -e "s/\[PROJECT_ID\]/$PROJECT/g" kube/deployment.yaml
    
  4. Reemplaza el marcador de posición [IP_ADDRESS] en el archivo de manifiesto de servicio por el valor de PROXY_IP:

    sed -i -e "s/\[IP_ADDRESS\]/$PROXY_IP/g" kube/service.yaml
    
  5. Implementa el proxy:

    kubectl apply -f kube/
    
  6. Verifica que la implementación sea exitosa:

    kubectl rollout status deployment/pubsub-proxy
    

    El resultado es similar al siguiente:

    [...]
    deployment "pubsub-proxy" successfully rolled out
    
  7. Asegúrate de que dos contenedores (ESP y proxy de Pub/Sub) se estén ejecutando en el Pod:

    kubectl get pods $(kubectl get pod \
        -l app=pubsub-proxy \
        -o jsonpath="{.items[0].metadata.name}") \
        -o jsonpath={.spec.containers[*].name}
    

    El resultado es similar al siguiente:

    esp  pubsub-proxy
    
  8. Busca el valor de EXTERNAL-IP para cambiar de <pending> a la dirección IP externa estática que creaste antes:

    kubectl get svc pubsub-proxy -w
    

    El resultado es similar al siguiente:

    NAME          TYPE          CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
    pubsub-proxy  LoadBalancer  10.7.247.212  <pending>     443:31104/TCP  6m32s
    pubsub-proxy  LoadBalancer  10.7.247.212  <PROXY_IP>    443:31104/TCP  6m5s
    

    Para detener la reproducción, presiona CTRL+C.

    Una vez que el proxy de Pub/Sub se implementa de forma correcta, se expone en https://${ENDPOINTS_SERVICE}/publish. Es posible que la nueva configuración de DNS tarde algunos minutos en propagarse.

  9. Verifica la configuración de DNS:

    watch nslookup ${ENDPOINTS_SERVICE}
    

    El resultado es similar al siguiente:

    Server:   169.254.169.254
    Address:  169.254.169.254#53
    
    Non-authoritative answer:
    Name: pubtest.endpoints.project-id.cloud.goog
    Address: gke-load-balancer-ip
    

    En el resultado verás lo siguiente:

    • gke-load-balancer-ip: La dirección IP de tu balanceador de cargas de GKE (IP del proxy).

    Para detener la reproducción, presiona CTRL+C.

Si alguno de los pasos anteriores genera un error, consulta los pasos para solucionar problemas.

Genera un token de autenticación

El siguiente procedimiento para generar un token de autenticación se proporciona como ejemplo. Para tu entorno de producción, necesitas una forma de que los usuarios generen sus propios tokens de autenticación. Por ejemplo, puedes encontrar código de muestra para obtener un token de ID de OIDC de manera programática en la documentación de Identity-Aware Proxy.

Para generar un token de autenticación, haz lo siguiente:

  1. Crea una cuenta de servicio de Google Cloud para la que generes un token de ID de OIDC:

    gcloud iam service-accounts create \
        $SERVICE_ACCOUNT \
        --display-name $SERVICE_ACCOUNT
    
  2. Obtén la identidad de correo electrónico de la cuenta de servicio:

    SA_EMAIL=${SERVICE_ACCOUNT}@${PROJECT}.iam.gserviceaccount.com
    
  3. Otorga la función de IAM de Creador de tokens de cuentas de servicio (roles/iam.serviceAccountTokenCreator) para la cuenta de servicio:

    gcloud iam service-accounts add-iam-policy-binding $SA_EMAIL \
        --member user:$(gcloud config get-value account) \
        --role roles/iam.serviceAccountTokenCreator
    
  4. Mediante la API de credenciales de IAM, genera un token de ID de OIDC.

    TOKEN=$(curl -s ${GENERATE_TOKEN}/${SA_EMAIL}:generateIdToken \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
        -d '{"audience": "'${ENDPOINTS_SERVICE}'", "includeEmail": "true"}' | jq -r ".token")
    

    El nombre del servicio de Endpoints se especifica en el campo audience. La reclamación audience identifica el destinatario al que está destinado el token.

  5. Verifica que el token se haya creado de forma correcta:

    echo $TOKEN
    

    El token web JSON (JWT) es similar al siguiente:

    eyJhbGciOiJSUzI1NiIsImtpZCI6IjY4NjQyODlm[...].eyJhdWQiOiJwdWJ0ZXN0LmVuZHBvaW50cy52aXR
    hbC1vY3RhZ29uLTEwOTYxMi5jbG91ZC5nb[...].SjBI4TZjZAlYo6lFKkrvfAcVUp_AJzFKoSsjNbmD_n[...]
    

Llama a Pub/Sub mediante un proxy

  1. En Cloud Shell, publica un mensaje de prueba:

    curl -i -k -X POST https://${ENDPOINTS_SERVICE}/publish \
        -H "Authorization: Bearer $TOKEN" \
        -H "Content-Type: application/json" \
        -d '{"topic": "'$TOPIC'", "messages": [ {"attributes": {"key1": "value1", "key2" : "value2"}, "data": "test data"}]}'
    

    El resultado es similar al siguiente:

    HTTP/2 200
    server: nginx
    date: Sun, 02 Jun 2019 03:53:46 GMT
    ...
    
  2. Comprueba si el mensaje se publicó de forma correcta en el tema de Pub/Sub:

    kubectl logs -f --tail=5 deployment/pubsub-proxy -c pubsub-proxy
    

    En los registros de implementación del proxy de Pub/Sub, se muestra el mensaje Successfully published:

    2019-06-02 03:49:39.723:INFO:oejs.Server:main: Started @2554ms
    Jun 02, 2019 3:53:44 AM com.google.pubsub.proxy.publish.PublishMessage
    getPublisher
    INFO: Creating new publisher for: proxy-test
    Jun 02, 2019 3:53:47 AM
    com.google.pubsub.proxy.publish.PublishMessage$1 onSuccess
    INFO: Successfully published: 569006136173844
    

Soluciona problemas

  1. En Cloud Shell, verifica el estado de ambos contenedores en el Pod del proxy de Pub/Sub:

    kubectl describe pods $(kubectl get pod -l app=pubsub-proxy \
        -o jsonpath="{.items[0].metadata.name}")
    

    En el resultado del registro, el estado de los contenedores es Running:

    [...]
    Containers:
      esp:
    [...]
      State:  Running
        Started:  Fri, 21 Jun 2019 16:41:30 +0530
      Ready:  True
      Restart Count:  0
    [...]
      pubsub-proxy:
        State:  Running
          Started:  Fri, 21 Jun 2019 16:41:42 +0530
        Ready:  True
        Restart Count:  0
    [...]
    
  2. Verifica los registros del contenedor para ver si hay otros errores (opcional). Por ejemplo, para verificar los registros del proxy de Pub/Sub, ejecuta el siguiente comando:

    kubectl logs -f --tail=10 deployment/pubsub-proxy -c pubsub-proxy
    

Para obtener ayuda con la solución de problemas, consulta los siguientes documentos:

Limpia

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos que utilizaste en este instructivo, puedes borrar el proyecto de Google Cloud que creaste para este instructivo o borrar los recursos asociados con este instructivo.

Borra el proyecto de Google Cloud

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.

  1. En la consola de Google Cloud, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Borra recursos

Si deseas conservar el proyecto de Google Cloud que usaste en este instructivo, borra los recursos individuales:

  1. En Cloud Shell, borra el clúster de GKE:

    gcloud container clusters delete $CLUSTER --zone $ZONE --async
    
  2. Borra el código descargado, los artefactos y otras dependencias:

    cd .. && rm -rf solutions-pubsub-proxy-rest
    
  3. Borra la imagen en Container Registry:

    gcloud container images list-tags \
        gcr.io/$PROJECT/pubsub-proxy \
        --format 'value(digest)' | \
        xargs -I {} gcloud container images delete \
        --force-delete-tags --quiet \
        gcr.io/${PROJECT}/pubsub-proxy@sha256:{}
    
  4. Borra el tema de Pub/Sub :

    gcloud pubsub topics delete $TOPIC
    
  5. Borra la cuenta de servicio:

    gcloud iam service-accounts delete $SA_EMAIL
    
  6. Borrar Endpoints:

    gcloud endpoints services delete ${ENDPOINTS_SERVICE}
    
  7. Borra la dirección IP estática:

    gcloud compute addresses delete service-ip --region $REGION
    

¿Qué sigue?