Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Acceso seguro a servicios de AWS desde Google Kubernetes Engine (GKE)

By Alexei LedenevFeb 17, 20208 min read

Esta página también está disponible en English, Deutsch, Français, Italiano, 日本語 y Português.

1 nxvyzhmtvz4ujcq1yadd7g

Es habitual que una aplicación que se ejecuta en Google Kubernetes Engine ( GKE) necesite consumir APIs de Amazon Web Services ( AWS). Toda aplicación tiene sus necesidades. Quizá deba ejecutar una consulta analítica en Amazon Redshift, leer datos almacenados en un bucket de Amazon S3, convertir texto a voz con Amazon Polly o usar cualquier otro servicio de AWS. Este escenario multicloud es cada vez más común, ya que las empresas trabajan con varios proveedores de nube.

1 nxvyzhmtvz4ujcq1yadd7g

El acceso entre nubes plantea un nuevo reto: cómo gestionar las credenciales que se requieren para acceder desde un proveedor de nube a los servicios que se ejecutan en otro. El enfoque ingenuo —repartir y guardar los secretos del proveedor de nube— no es el más seguro: distribuir credenciales de larga duración a cada servicio que necesite acceder a AWS resulta difícil de gestionar y supone un riesgo de seguridad.

Soluciones actuales

Cada nube ofrece su propia solución para resolver este reto y, si trabajas con un único proveedor, suele ser más que suficiente.

Google Cloud presentó Workload Identity, la forma recomendada para que las aplicaciones en GKE se autentiquen y consuman otros servicios de Google Cloud. Workload Identity vincula cuentas de servicio de Kubernetes con cuentas de servicio de Cloud IAM, de modo que puedes usar conceptos nativos de Kubernetes para definir qué workloads se ejecutan bajo qué identidades y permitir que tus workloads accedan automáticamente a otros servicios de Google Cloud, todo sin tener que gestionar secretos de Kubernetes ni claves de cuentas de servicio de IAM. Te recomendamos leer el artículo de DoiT Kubernetes GKE Workload Identity.

Amazon Web Services ofrece una funcionalidad similar con IAM Roles for Service Accounts. Con los roles de IAM para cuentas de servicio en clusters de Amazon EKS, puedes asociar un rol de IAM con una cuenta de servicio de Kubernetes. Esa cuenta de servicio puede entonces otorgar permisos de AWS a los contenedores de cualquier pod que la utilice. Gracias a esta funcionalidad, ya no hace falta otorgar permisos extendidos al rol de IAM del worker node para que los pods de ese node puedan invocar APIs de AWS.

Pero ¿qué pasa si ejecutas tu workload de aplicación en un cluster de GKE y quieres acceder a servicios de AWS sin sacrificar la seguridad?

Definición del caso de uso

Supongamos que ya cuentas con una cuenta de AWS y un cluster de GKE, y que tu empresa decidió ejecutar una aplicación basada en microservicios en GKE, pero quiere seguir usando recursos de la cuenta de AWS (servicios de Amazon S3 y SNS) para integrarse con otros sistemas desplegados en AWS.

Por ejemplo, el orchestration job (desplegado como un Kubernetes Job) se ejecuta dentro de un cluster de GKE y necesita subir un archivo de datos a un bucket de S3 y enviar un mensaje a un topic de Amazon SNS. La línea de comandos equivalente podría ser:

https://gist.github.com/90682305b879a96d273284df5d20fdcb

Un ejemplo bastante sencillo. Para que estos comandos funcionen, el orchestration job debe disponer de credenciales de AWS, y esas credenciales deben poder realizar las llamadas correspondientes a la API.

El enfoque ingenuo (e inseguro): credenciales de IAM de larga duración

Consiste en exportar la Access Key y la Secret Key de AWS de algún usuario de IAM e inyectar esas credenciales en el orchestration job, ya sea como archivo de credenciales o como variables de entorno. Lo normal es no hacerlo de forma directa, sino mediante el recurso Kubernetes Secrets protegido con una política de autorización RBAC.

El riesgo es que estas credenciales nunca caducan. Hay que transferirlas de algún modo del entorno de AWS al de GCP y, en la mayoría de los casos, se acaban guardando en algún lugar para poder recrear el orchestration job más adelante si hace falta.

Al usar credenciales de AWS de larga duración, existen múltiples vías por las que tu cuenta de AWS puede verse comprometida: subir credenciales a un repositorio de GitHub sin querer, guardarlas en un sistema Wiki, reutilizarlas en distintos servicios y aplicaciones, permitir accesos sin restricciones, y un largo etcétera.

Aunque es posible diseñar una solución adecuada de gestión de credenciales para los usuarios de IAM emitidos, no será necesaria si nunca llegas a crear esas credenciales de larga duración.

El enfoque propuesto

La idea de fondo es asignar un rol de IAM de AWS a un Pod de GKE, de manera análoga a las funcionalidades específicas de cada nube como Workload Identity y EKS IAM Roles for Service Accounts.

Por suerte, AWS permite crear un rol de IAM para proveedores de identidad OpenID Connect Federation OIDC en lugar de para usuarios de IAM. Por su parte, Google implementa un proveedor OIDC y lo integra estrechamente con GKE mediante la funcionalidad Workload Identity, que entrega un token OIDC válido al pod de GKE que se ejecuta bajo una cuenta de servicio de Kubernetes vinculada a una cuenta de servicio de Google Cloud. Todo esto resulta útil para implementar un acceso seguro de GKE a AWS.

Intercambiar un access token OIDC por un ID token

Falta una pieza para completar el rompecabezas. Con Workflow Identity bien configurado, el Pod de GKE obtiene un access token OIDC que permite acceder a los servicios de Google Cloud. Pero para obtener credenciales temporales de AWS desde el AWS Security Token Service ( STS), necesitas un ID token OIDC válido.

El AWS SDK (y la herramienta aws-cli) solicitará automáticamente credenciales temporales de AWS al servicio STS cuando estén bien configuradas las siguientes variables de entorno:

  • AWS_WEB_IDENTITY_TOKEN_FILE: ruta al archivo del web identity token (OIDC ID token)
  • AWS_ROLE_ARN: el ARN del rol que asumirán los contenedores del Pod
  • AWS_ROLE_SESSION_NAME: el nombre asignado a esta sesión assume-role

Puede sonar algo complejo, pero te dejo una guía paso a paso y un proyecto open source de apoyo, dointl/gtoken, para simplificar la configuración.

gtoken-webhook: Mutating Admission webhook de Kubernetes

El gtoken-webhook es un mutating admission webhook de Kubernetes que muta cualquier Pod de K8s que se ejecute bajo una cuenta de servicio de Kubernetes con anotaciones específicas (más detalles a continuación).

Flujo de mutación de gtoken-webhook

El gtoken-webhook inyecta un gtoken``initContainer en el Pod de destino y un contenedor sidekick adicional gtoken (que refresca el OIDC ID token un instante antes de su expiración), monta un volumen de token e inyecta tres variables de entorno específicas de AWS. El contenedor gtoken genera un GCP OIDC ID Token válido y lo escribe en el volumen del token. También inyecta las variables de entorno requeridas por AWS.

El AWS SDK realizará automáticamente las llamadas AssumeRoleWithWebIdentity correspondientes a AWS STS por ti, y se encargará tanto del cacheo en memoria como del refresco de credenciales cuando haga falta.

1 7usxylt69dwxddxzi50teg

Guía del flujo de configuración

Desplegar gtoken-webhook

  1. Para desplegar el servidor gtoken-webhook hay que crear un servicio de webhook y un deployment en nuestro cluster de Kubernetes. Es bastante directo, salvo por un detalle: la configuración TLS del servidor. Si revisas el archivo deployment.yaml, verás que el certificado y la clave privada se leen desde argumentos de línea de comandos, y que la ruta a estos archivos viene de un volume mount que apunta a un secret de Kubernetes:

https://gist.github.com/b9fc2fe5acb556e3b48ee89af7db368e

Lo más importante es no olvidar configurar después el certificado de CA correspondiente en la configuración del webhook, para que el apiserver sepa que debe aceptarlo. Por ahora, vamos a reutilizar el script que escribió originalmente el equipo de Istio para generar una solicitud de firma de certificado. Después enviaremos la solicitud a la API de Kubernetes, obtendremos el certificado y crearemos el secret requerido a partir del resultado.

Primero, ejecuta el script webhook-create-signed-cert.sh y comprueba que se haya creado el secret con el certificado y la clave:

https://gist.github.com/91d2bd8cddf116aa0fa8cefbf3208bcd

Una vez creado el secret, podemos crear un deployment y un servicio. Son recursos estándar de deployment y servicio de Kubernetes. Hasta este punto, lo único que tenemos es un servidor HTTP que acepta solicitudes a través del servicio en el puerto 443:

https://gist.github.com/34ec56018700a610f37a09a9e846eecb

Configurar el Mutating Admission webhook

Ahora que nuestro servidor de webhook está en marcha, ya puede aceptar solicitudes del apiserver. No obstante, primero conviene crear algunos recursos de configuración en Kubernetes. Empecemos por el validating webhook; el mutating webhook lo configuraremos después. Si revisas la configuración del webhook, verás que contiene un placeholder para CA_BUNDLE:

https://gist.github.com/d40101fba8203cc77d62629dde4d6a8f

Hay un pequeño script que sustituye el placeholder CA_BUNDLE en la configuración por esta CA. Ejecuta este comando antes de crear la configuración del validating webhook:

https://gist.github.com/5e0ed44892fc7a21709ac18b381cb5f6

Crea una configuración de mutating webhook:

https://gist.github.com/a545e146e1168f3b88d04b3f4c490d68

Configurar RBAC para gtoken-webhook

Crea la cuenta de servicio de Kubernetes que se usará con gtoken-webhook:

https://gist.github.com/aabfa30bc005ae8654b360814523c9bc

Define los permisos RBAC para la cuenta de servicio del webhook:

https://gist.github.com/5ceb8a6e1ca19d9be3b8ed2d810ddc96

Variables del flujo

Algunas de las siguientes variables las debe proporcionar el usuario; otras se generan automáticamente y se reutilizan en los pasos siguientes.

  • PROJECT_ID: ID del proyecto de GCP (lo proporciona el usuario)
  • CLUSTER_NAME: nombre del cluster de GKE (lo proporciona el usuario)
  • GSA_NAME: nombre de la cuenta de servicio de Google Cloud (lo proporciona el usuario)
  • GSA_ID: ID único de la cuenta de servicio de Google Cloud (lo genera Google)
  • KSA_NAME: nombre de la cuenta de servicio de Kubernetes (lo proporciona el usuario)
  • KSA_NAMESPACE: namespace de Kubernetes (lo proporciona el usuario)
  • AWS_ROLE_NAME: nombre del rol de IAM de AWS (lo proporciona el usuario)
  • AWS_POLICY_NAME: política de IAM de AWS que se asigna al rol de IAM (la proporciona el usuario)
  • AWS_ROLE_ARN: identificador ARN del rol de IAM de AWS (lo genera AWS)

Google Cloud: habilitar GKE Workload Identity

Crea un nuevo cluster de GKE con Workload Identity habilitado:

https://gist.github.com/0fcbc309cd1967edac19bac6795d651d

o bien actualiza un cluster existente:

https://gist.github.com/05a94c349aa4a274fc27daae45448be9

Google Cloud: crear una cuenta de servicio de Google Cloud

Crea una cuenta de servicio de Google Cloud:

https://gist.github.com/1790d9e61c63123937d01c8146c1a67d

Asigna a la cuenta de servicio de Google GSA_NAME los siguientes roles:

  • roles/iam.workloadIdentityUser: suplantar cuentas de servicio desde workloads de GKE
  • roles/iam.serviceAccountTokenCreator: suplantar cuentas de servicio para crear tokens de acceso OAuth2, firmar blobs o firmar tokens JWT

https://gist.github.com/19570300e584798d4d5c3a234ceb0d62

AWS: crear un rol de IAM de AWS con federación OIDC de Google

Prepara un documento de política de confianza del rol para el proveedor OIDC de Google:

https://gist.github.com/d80514354a5270bd0f6766dee7947902

Crea el rol de IAM de AWS con Google Web Identity:

https://gist.github.com/94f0ce213cf60cfce6ac987af51afbed

Asigna al rol de AWS las políticas que necesites:

https://gist.github.com/afd9ecc776cfba5148137769aaf71a5f

Obtén el ARN del rol de AWS para usarlo en la anotación de la SA de K8s:

https://gist.github.com/d407769dffd4eb276ed22f7b723cbdc4

GKE: crear una cuenta de servicio de Kubernetes

Crea un namespace de K8s:

https://gist.github.com/59dafb6baf1223552eb7d9fc3c3cbea3

Crea la cuenta de servicio de K8s:

https://gist.github.com/58fd6924e79270857f39d924a0f12011

Anota la cuenta de servicio de K8s con GKE Workload Identity (correo de la cuenta de servicio de GCP):

https://gist.github.com/0be380a02ff228a38b2a9e5400b75dcb

Anota la cuenta de servicio de K8s con el ARN del rol de AWS:

https://gist.github.com/01620978944525a8df6c3d76ddc1fb67

Ejecutar la demo

Ejecuta un nuevo Pod de K8s con la cuenta de servicio de K8s ``${KSA_NAME}```:

https://gist.github.com/9c5a30f5dd3825d5c3bc6adc14b76fbc

Referencias externas


Resumen

Espero que este artículo te resulte útil. Quedo atento a tus comentarios y preguntas.

¿Quieres más historias? Visita nuestro blog o sigue a Alexei en Twitter.