
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.

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 PodAWS_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.

Guía del flujo de configuración
Desplegar gtoken-webhook
- Para desplegar el servidor
gtoken-webhookhay 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 GKEroles/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
- GitHub: Acceso seguro a servicios de AWS desde un cluster de GKE con doitintl/gtoken
- Documentación de AWS: Creating a Role for Web Identity or OpenID Connect Federation
- Blog: Kubernetes GKE Workload Identity link
- Blog de AWS: Introducing fine-grained IAM roles for service accounts link
- GitHub: AWS Auth con Web Identity Federation desde Google Cloud, proyecto en GitHub shrikant0013/gcp-aws-webidentityfederation
- Blog: Using GCP Service Accounts to access AWS IAM Roles artículo de Colin Panisset
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.