Un aspecto fundamental de los servicios en la nube es la comunicación entre servicios, y es esencial hacerlo de forma segura. Como arquitecto que diseñó servicios de autenticación y autorización centralizados en CyberArk , un proveedor de SaaS de ciberseguridad, compartiré mi opinión sobre el desarrollo de un mecanismo de autorización seguro entre los servicios en la nube de AWS, ya sean sin servidor o en contenedores.
Cuando termine de leer esta publicación, no solo comprenderá la importancia de la autenticación y autorización de servicios, sino que también contará con los conocimientos prácticos para implementarla. Esto incluye habilitar de forma segura el acceso entre cuentas a los recursos para patrones de comunicación sincrónicos y asincrónicos mediante AWS IAM.
Esta publicación incluye políticas IAM JSON y ejemplos de código Python.
Esta publicación es la primera de muchos temas de seguridad en una próxima serie.
Tabla de contenido
Conceptos de autenticación y autorización
La autenticación es:
La autenticación es el acto de probar una afirmación , como la identidad de un usuario de un sistema informático - Wikipedia
y la autorización por otra parte es:
la función de especificar derechos/privilegios de acceso a los recursos - Wikipedia
En un servicio seguro, la autenticación y la autorización van de la mano.
Como desarrollador de servicios que expone una API REST, es fundamental comprender que dejar su API abierta al mundo sin las medidas de autenticación y autorización adecuadas puede generar acceso no autorizado y manipulación de datos.
Desea que su servicio acepte primero las solicitudes de comunicación de servicios autenticados ( principales ), aquellos cuya identidad se ha demostrado que es confiable y conocida. Una vez identificados, desea asegurarse de que solo los servicios autorizados puedan activar su API o comunicarse con su servicio.
O en otras palabras:
La autenticación tiene como objetivo validar e identificar que un principal es quien dice ser.
La autorización tiene como objetivo definir la relación entre principales, acciones y recursos.
Podemos visualizar la autorización como la relación entre los principales (servicios), las acciones que desean realizar (por ejemplo, ejecutar una API) y los recursos (el punto final de la API REST real y el tipo de acción HTTP) en los que se invocan sus acciones.
Por lo tanto, la seguridad de la comunicación de servicio a servicio consta de dos partes:
Primero, asegúrese de que el servicio que llama sea quien dice ser.
Luego, afirme que tiene permiso para realizar acciones sobre el recurso.
Para comprender mejor la importancia de la autenticación y la autorización, consideremos algunos ejemplos reales de comunicación entre servicios. Estos ejemplos resaltarán el papel fundamental que desempeñan estas medidas para garantizar la seguridad y la integridad de los servicios de API REST.
Patrones de comunicación de servicio a servicio
Los desarrolladores dedican mucho tiempo a diseñar API REST orientadas al cliente. Estas API introducen el concepto de usuarios en el sistema (humanos o no humanos). AWS ofrece varias opciones de autenticación de usuarios y servicios, como Amazon Cognito (Cognito) y AWS Identity and Access Management (IAM), siendo IAM la opción principal para la gestión interna de usuarios, mientras que Cognito puede conectarse a proveedores de identidad externos como CyberArk y otros.
En esta publicación, me gustaría centrarme en AWS IAM y su papel en un aspecto importante de los servicios en la nube: la autenticación y autorización de servicio a servicio.
Patrón de comunicación sincrónica
En este caso de uso, hay tres servicios: A, B y C, cada uno en su propia cuenta de AWS.
El servicio A no tiene servidor, el B se basa en EC2 y el C ofrece una API REST con API Gateway. Tanto el servicio A como el B utilizan la API del servicio C.
*En esta parte se tratan las API Gateway públicas. Para conocer las API Gateway privadas, visite el apéndice que aparece a continuación.
Sin embargo, el servicio C quiere asegurarse de que solo estos servicios específicos puedan llamar a su API y quizás incluso ajustarla para que tenga el menor privilegio posible, de modo que solo una función Lambda particular o una máquina EC2 pueda activarla.
Continuemos con el segundo patrón.
Patrón de comunicación asincrónica
En este caso de uso, tenemos nuestros tres servicios como antes:
A, B y C cada uno en su propia cuenta de AWS como antes.
C contiene un tema SNS utilizado como un servicio de comunicación asincrónica centralizado entre editor y suscriptor en la organización.
El servicio A es el editor y el servicio B se suscribe al tema SNS a través de una cola SQS.
El servicio C desea asegurarse de que solo el servicio A pueda publicar mensajes en el tema SNS y que solo el SQS del servicio B pueda suscribirse al tema, ya que el tema contiene datos que debe mantener privados.
Este es un patrón estándar. Puede cambiar el tema de SNS al bus EventBridge o a cualquier servicio de mensajería que tenga, como Amazon MSK.
Cuentas individuales o múltiples
Como nota al margen, los servicios A, B y C pueden compartir la misma cuenta, lo que simplifica la solución IAM. Sin embargo, es fundamental seguir las prácticas de IAM de esta publicación; incluso si todos los servicios comparten la misma cuenta, no tome atajos. Es posible que en el futuro se encuentre trasladándolos a cuentas diferentes. En ese caso, si no sigue las prácticas recomendadas, tendrá dificultades y muchos cambios importantes por delante (¡según mi experiencia!). Siempre es mejor seguir las prácticas recomendadas, especialmente cuando se trata de seguridad.
Autenticación y autorización basadas en IAM
Ahora que cubrimos los conceptos básicos de los problemas de autenticación y autorización de servicio a servicio, analicemos la solución y comencemos a usar AWS IAM.
Esta solución cubrirá tanto la autenticación y autorización entre cuentas como entre cuentas.
La autenticación IAM significa que un principal, por ejemplo, el servicio A, debe estar autenticado (iniciar sesión en AWS) con sus credenciales para enviar una solicitud a otros servicios y recursos de AWS, o al servicio C en nuestro ejemplo. Una vez autenticado, podemos aprovechar IAM nuevamente para asegurarnos de que el servicio A esté autorizado para acceder al servicio C.
Combinaremos dos aspectos de IAM, políticas basadas en identidad y políticas basadas en recursos , y brindaremos soluciones de autorización para comunicaciones sincrónicas y asincrónicas. También brindaremos acceso entre cuentas, es decir, servicios de autorización desde diferentes cuentas de AWS mediante el mecanismo de "asumir rol" o delegación .
Políticas basadas en recursos
Según la documentación de AWS:
Las políticas basadas en recursos otorgan al principal especificado permiso para realizar acciones específicas en ese recurso y definen bajo qué condiciones se aplica esto.
Las políticas basadas en recursos son documentos de políticas JSON que se adjuntan a un recurso, como API Gateway, SNS, S3 u otro recurso. Con las políticas basadas en recursos, puede especificar quién tiene acceso al recurso y qué acciones puede realizar en él. Además, se pueden usar para permitir el acceso entre cuentas. Suena perfecto, ¿verdad? Bueno, tiene limitaciones y no todos los tipos de recursos lo admiten.
Repasemos los pros y contras de este mecanismo IAM.
Ventajas:
Sencillo de definir
Habilitar el acceso entre cuentas.
Contras:
No todos los recursos de AWS admiten políticas basadas en recursos.
Las políticas basadas en recursos tienen un límite de tamaño, como todas las políticas de IAM. Cuando se definen más recursos y se alcanza el tamaño máximo, no hay forma de superarlo, salvo aprovisionando un nuevo recurso (duplicando parte del servicio C, básicamente).
Mecanismo de asumir roles
El mecanismo de delegación de acceso de IAM, o "asumir rol", como lo llamo, es crucial en la comunicación entre cuentas. Sin embargo, también se puede utilizar en un escenario de una sola cuenta de AWS.
El mecanismo de delegación de acceso de IAM se sustenta en políticas basadas en identidad asociadas a un rol de IAM. En nuestro ejemplo, el rol C se encuentra en la cuenta del servicio C y otorga acceso al recurso en cuestión, que podría ser el API Gateway o el tema de SNS del servicio C.
Cualquiera que tenga permiso para asumir el rol C puede obtener un conjunto de credenciales de seguridad IAM temporales que pueden usarse para la autenticación y autorización para comunicarse con el servicio C, ya sea para ejecutar una llamada a la API REST o publicar un mensaje en un tema SNS.
Asumir un rol implica realizar una llamada del SDK de AWS a Amazon STS (AWS Security Token Service) y utilizar las credenciales temporales de la respuesta del SDK para iniciar una sesión de comunicación con el servicio con el que desea comunicarse; exploraremos ejemplos de código más adelante en esta publicación para asegurarnos de que tenga una comprensión integral del proceso.
Sin embargo, necesitamos definir quién puede asumir este rol y obtener acceso al servicio C. Necesitaremos políticas de recursos y definir que solo los roles de los servicios A y B pueden asumir el rol C y obtener acceso al servicio C.
Repasemos los pros y contras de este mecanismo IAM.
Ventajas:
Admite todos los tipos de recursos siempre que pueda definir una política de IAM.
Abstrae el recurso y su ARN. Obtienes un rol que te proporciona acceso. El recurso puede cambiar mañana, pero todo lo que sabes es el rol del ARN y qué llamada API usar. Supongamos que la llamada se abstrae en un SDK organizacional que encapsula el recurso. En ese caso, puedes cambiar los recursos (tema SNS a bus EventBridge) y mantener los cambios en las políticas de rol y los niveles de implementación del SDK, pero los ARN del rol siguen siendo los mismos.
La política basada en recursos del rol C, que define quién puede asumir el rol, tiene un límite de tamaño. Sin embargo, si queremos agregar más servicios, podemos aprovisionar un nuevo rol para que lo asuman nuevos servicios. A diferencia del mecanismo anterior, está bien aprovisionar un nuevo recurso ya que el recurso protegido permanece sin cambios (tema de API Gateway o SNS).
Contras:
Es más complicado de definir y requiere un rol adicional en el servicio C.
Asumir un rol es otra llamada del SDK de AWS que extiende el tiempo de ejecución general de los servicios A y B y requiere más código propenso a errores para mantener.
Veamos cómo podemos resolver nuestros problemas de autenticación y autorización en patrones de comunicación sincrónicos y asincrónicos con políticas IAM concretas y ejemplos de código Python.
Solución IAM de comunicación sincrónica
Comencemos por mejorar la seguridad del servicio C, API Gateway. Primero, agregaremos un autorizador de IAM a todos sus puntos finales de API. Al hacerlo, de manera predeterminada, se rechazarán todas las solicitudes no autenticadas y no autorizadas.
Cuando la autorización de IAM está habilitada, los clientes deben usar Signature Version 4 para firmar sus solicitudes con credenciales de AWS. API Gateway invoca su ruta de API solo si el cliente tiene permiso de ejecución de API para la ruta. - Documentación de AWS
AWS IAM garantiza que solo las solicitudes con credenciales de IAM autenticadas y válidas que estén autorizadas ejecutarán la API C del servicio. Todo lo que queda es que el servicio C defina qué servicios (identidades) y cuentas de AWS pueden ejecutar sus API.
Hay dos maneras de lograrlo:
Política basada en recursos
Las políticas basadas en identidad asumen mecanismos de roles.
Comencemos con una política basada en recursos.
Política basada en recursos
En este caso de uso, necesitamos modificar la política basada en recursos de API Gateway del servicio C para permitir que los servicios A y B (su función Lambda, por ejemplo, o su cuenta completa o punto final de VPC, etc.) ejecuten la API y sus puntos finales. Podemos definir una definición detallada y decidir exactamente con qué punto final puede comunicarse cada servicio.
A continuación se muestra un ejemplo de una política de recursos de este tipo para el servicio C API Gateway:
En las líneas 8 y 9, podemos permitir que una cuenta completa (principal), como la cuenta del servicio A o la cuenta B o un ARN de rol específico en una cuenta diferente (una mejor opción, con menos privilegios), realice la acción (línea 12) de ejecutar un punto final de API. Podemos establecer el punto final exacto y el comando HTTP en la línea 14.
Tenga en cuenta que, por el momento, no se puede utilizar este mecanismo en una puerta de enlace de API HTTP, solo en la variante REST.
En el lado de los servicios A y B, deben definir sus roles (rol de función Lambda para el servicio A) con permisos de identidad para la misma acción especificada en la política de recursos. Para el acceso entre cuentas, debe definir la política de esta manera bilateral. El servicio A define el rol de su función Lambda con permisos para llamar a la API Gateway del servicio C, y el servicio C permite que el servicio A lo llame desde la otra cuenta.
Además, los servicios A y B deben enviar sus credenciales IAM ( Sig v4 ) en el encabezado de autorización HTTP cuando llaman al API Gateway del servicio C.
A continuación se muestra un ejemplo de Python para este proceso:
En este ejemplo, usamos el rol de nuestro servicio para autenticarnos con IAM, creamos los encabezados de autenticación en la línea 7 y los enviamos en la línea 15.
Asumir el papel
En este caso de uso, necesitamos crear un rol en la cuenta del servicio C con permisos para ejecutar API Gateway y dejar que un principal en los servicios A y B lo asuma.
Podemos comenzar definiendo la política de confianza del rol: en esta política, el recurso es el rol en sí.
Permitimos que roles específicos (principales) de los servicios A y B asuman este rol (acción). Podemos otorgar un alcance más amplio para los servicios A o B, ya sea a toda la cuenta o a un rol con un prefijo predefinido. Sin embargo, generalmente es mejor minimizar el alcance, por lo que un prefijo de rol o un rol específico es mejor y más seguro.
A continuación, debemos agregar los permisos del rol para ejecutar la API del servicio C al rol:
Por último, el código del servicio A es muy similar al anterior, con el añadido del código de rol asumido.
Puede encontrar ejemplos de código para múltiples servicios aquí o consultar el código a continuación.
Para "RoleArn", debe proporcionar el rol que el servicio C crea y comparte su ARN con usted. Normalmente, los equipos de servicio intercambian ARN manualmente. Recomendaría guardar ese ARN como una variable de entorno en la función Lambda. Además, asegúrese de que su rol Lambda tenga los permisos necesarios para asumir roles.
Como puede ver a continuación, el código es muy similar al ejemplo de código anterior, solo con la adición de la llamada a la API de STS en las líneas 5 a 10 y el uso de los valores de respuesta en las líneas 11 a 18.
Si desea obtener más información sobre los casos de uso de API Gateway privados, consulte el apéndice .
Solución de comunicación asincrónica
Nuestro objetivo es permitir que el servicio A publique mensajes SNS en el tema SNS del servicio C y permitir que el servicio B se suscriba a través de SQS a los mensajes SNS.
Repasemos las dos opciones de implementación de IAM que tenemos.
Política basada en recursos
En este caso, definiremos una política de acceso a SNS que permita al servicio A (principal) publicar mensajes (acción) en el tema SNS del servicio C (recurso) y al servicio B suscribirse a los mensajes. Tenga en cuenta que es mejor ajustar estos permisos al rol que puede publicar y al SQS específico que puede suscribirse.
En el lado del servicio A, el rol Lambda definirá sus permisos para publicar en el tema de SNS. Si los dos lados definen los permisos, podrán trabajar cuando se trate de acceso entre cuentas.
Al publicar un mensaje en el tema, la función Lambda del servicio A utilizará el SDK de AWS para realizar la llamada a la API. El SDK utiliza las credenciales del rol de la función para encargarse de la autenticación y la autorización de IAM.
En el lado del servicio B, necesitamos definir una suscripción SQS al tema SNS siguiendo la documentación aquí .
Asumir el papel
En este caso, necesitamos crear un rol en la cuenta del servicio C con permiso para publicar mensajes en el tema de redes sociales. Luego, dejamos que un rol específico del servicio A asuma este rol.
Podemos hacer esto definiendo la política de confianza del rol.
Podemos otorgar un alcance más amplio al servicio A para toda la cuenta o un rol con un prefijo predefinido. Sin embargo, normalmente es mejor minimizar el alcance. Por lo tanto, un prefijo de rol o un rol específico es mejor y más seguro.
A continuación, debemos agregar al rol los permisos para publicar mensajes en el tema SNS del servicio C:
Ahora, del lado del servicio A, debemos asumir el rol y usar el SDK de AWS para enviar mensajes de SNS. También es importante garantizar que el rol de la función Lambda tenga los permisos 'sns:Publish' y 'sts:AssumeRole'; de lo contrario, las llamadas del SDK de AWS no funcionarán.
Los equipos de servicio necesitan intercambiar los ARN de recursos y los números de cuenta para que las políticas funcionen.
El servicio A asume el rol de llamada SDK y utiliza las credenciales IAM temporales para crear un cliente boto para la llamada SDK del mensaje de publicación de SNS.
El servicio B permanece como en el ejemplo de política basada en recursos; solo puede funcionar como una política de recursos que permite que su SQS se suscriba. Sin embargo, el servicio A requiere algunos cambios de código.
Elegir entre políticas basadas en la asunción de roles y políticas basadas en recursos
La comunicación de su servicio será segura con autenticación y autorización, ya sea que elija políticas basadas en recursos o una solución de roles asumidos. Sin embargo, cada implementación tiene sus pros y sus contras que pueden complicarle la vida en el futuro si las ignora.
Dividiré mi recomendación por diferentes parámetros.
Supongamos que tuviera que elegir solo una implementación. En ese caso, optaría por la opción de "asumir el rol", ya que permite que mi servicio admita varios servicios en el futuro fácilmente. Puedo crear más roles para admitir más servicios que los asuman y no estoy limitado por el tamaño de la política de IAM.
Sin embargo, las políticas basadas en recursos son mejores si solo te preocupa el rendimiento, así que no agregues otra llamada de SDK para asumir el rol. Ten en cuenta que estas políticas tienen un tamaño máximo. Supongamos que esperas conectar muchos servicios y diferentes cuentas de AWS (piensa en una cuenta de publicación y suscripción central o una API central). En ese caso, encontrarás estas limitaciones en algún momento en el futuro. Como no tiene sentido duplicar el tema de SNS o la puerta de enlace de API para la integración con nuevos servicios, es mejor que elijas la ruta de "asumir el rol". Es más fácil proporcionar nuevos roles que duplicar un tema de SNS o una puerta de enlace de API, lo cual no tiene sentido.
Otro factor decisivo es si los servicios están en la misma cuenta y quién los mantiene, el mismo equipo o no. La política basada en recursos es excelente para la comunicación interna entre servicios o microservicios cuando el mismo equipo los mantiene, ya que introduce cierto grado de acoplamiento. Aun así, es aceptable ya que "permanece" en la familia. Sin embargo, supongamos que están involucradas diferentes cuentas y equipos. En ese caso, la ruta de "asumir el rol" es mejor ya que desacopla los recursos y los equipos en cuestión y admite infinitas extensiones futuras.
Por último, siempre puedes cambiar la implementación, así que no tengas miedo de elegir; solo asegúrate de seleccionar una de estas dos opciones.
Resumen
En esta publicación, aprendimos sobre autenticación y autorización. También aprendimos sobre dos patrones de comunicación entre servicios: asincrónico y sincrónico.
Hemos implementado tanto la autenticación como la autorización para esos patrones mediante AWS IAM. Vimos cuatro implementaciones diferentes y analizamos sus ventajas y desventajas, ya sea la ruta de políticas basadas en recursos o la de "asumir rol".
En las siguientes publicaciones, analizaremos los desafíos que plantean estos patrones, cómo resolverlos y cómo llevar la autorización un paso más allá en el dominio de granularidad fina.
Apéndice: Puertas de enlace de API públicas y privadas
AWS recomienda crear API Gateways privados para la comunicación de servicio a servicio para mejorar el rendimiento, reducir los costos de red (no abandona la red de AWS) y mejorar la seguridad.
...el tráfico a su API privada utiliza conexiones seguras y no sale de la red de Amazon; está aislado de Internet público - Documentación de AWS.
En realidad, es posible y más fácil crear API como API Gateways públicas. En ese caso, la autenticación y la autorización se vuelven más críticas y debes seguir las pautas de esta publicación.
Las puertas de enlace de API privadas requieren VPC. Esto genera una complejidad adicional, ya que los servicios que se conectan también deben usar VPC y puntos finales de VPC.
AWS recomienda conectar las redes VPC de los servicios a través de políticas de recursos para puntos finales de VPC o interconexión de VPC . Puede leer más sobre esto aquí .
Cuando utiliza funciones Lambda y sin servidor y desea comunicarse con una API Gateway privada, debe colocar sus funciones Lambda dentro de VPC. Esto no es ideal, ya que las VPC tienen efectos no deseados en las funciones Lambda, como inicios en frío más prolongados y mayores costos.
Las VPC no reemplazan la autenticación y la autorización
Quiero dejar este punto claro.
Configurar una conexión de red entre diferentes puntos finales de VPC no significa que haya implementado la autenticación o autorización del servicio ni que esté 100 % seguro.
Sí, aporta una capa adicional de seguridad, pero no reemplaza la autenticación y autorización basadas en IAM.
En primer lugar, no es el menor privilegio; cualquier servicio dentro de esas redes VPC puede acceder a su servicio. Es una capa adicional de seguridad, pero no reemplaza la autorización. Además, no es escalable. Cuantos más servicios agregue, más "vulnerado" estará su servicio, y más VPC y servicios obtendrán acceso.
Además, si los atacantes obtienen acceso a una de las VPC, pueden comunicarse libremente con su servicio porque su servicio acepta cualquier llamada entrante.
Sin embargo, la combinación de la autenticación y autorización de IAM con VPC proporciona la mejor y más completa seguridad para su API Gateway.
Puede obtener más información sobre estos patrones con API Gateways y VPC en el siguiente video:
コメント