En el primer artículo de esta serie, hemos hablado de los tres principios que nos permitían asegurar la infraestructura sobre la que corre nuestra IA.
Estos principios son:
1. Aislar completamente los datos de IA del operador de infraestructura,
2. Permitir a los clientes aislar sus propios datos y
3. Garantizar comunicaciones seguras entre dispositivos de infraestructura de aceleración de ML.
Para el segundo principio veíamos la combinación entre AWS Nitro Enclaves y AWS Key Management Service (KMS) lo que nos permitía encriptar y transferir datos de manera segura. Es hora de ver un ejemplo práctico de este principio.
Introducción
Los modelos de Aprendizaje Automático (ML) se utilizan para inferir datos sensibles en sectores como la salud, automoción, finanzas y farmacéutica (entre otros). Estas industrias requieren herramientas que protejan sus datos en tránsito, almacenamiento y procesamiento, asegurando su aislamiento durante todo el proceso.
Durante el procesamiento, las amenazas pueden originarse desde el propio stack tecnológico, como puede ser el sistema operativo o los programas instalados en el host. El poder disponer de un proceso que aplique la separación de roles y responsabilidades dentro de una organización nos permitirá minimizar la capacidad del personal para acceder a datos sensibles. En este blog, vamos a ver cómo ejecutar inferencia de ML dentro de Nitro Enclaves para mostrar cómo se protegen los datos sensibles durante el procesamiento. Esto nos va a permitir reducir la superficie de ataque cuando los datos se descifran para su procesamiento.
¿Qué son las Nitro Enclaves?
Las Nitro Enclaves nos permiten crear entornos de cómputo aislados dentro de instancias EC2 para proteger y procesar de forma segura datos que son altamente sensibles. Las enclaves no tienen almacenamiento persistente, no tienen acceso interactivo y no tienen redes externas. La comunicación entre la instancia y el enclave se realiza utilizando un canal local seguro llamado vsock. Por defecto, ni siquiera un usuario administrador o root en la instancia principal podrá acceder al enclave.
Visión General
En nuestro ejemplo vamos a ver cómo implementar una carga de trabajo de IA/ML y ejecutar inferencias dentro de Nitro Enclaves para procesar de forma segura nuestros datos. Utilizaremos una imagen para demostrar el proceso de cómo los datos pueden ser cifrados, almacenados, transferidos, descifrados y procesados cuando sea necesario, minimizando el riesgo de nuestros datos.
+------------------------------------+ | Machine Learning | | Object Recognition | +------------------------------------+ | | +--------v--------+ | Data Security | +----------------+ | | +--------v--------+ | Infrastructure| | Layer | +--------+--------+ | | +--------v--------+ | AWS Nitro | | Enclaves | | | +----------------+
¿Cómo lo vamos a hacer?
Vamos a desplegar una carga de trabajo que utiliza un modelo de IA/ML de código abierto para detectar objetos en una imagen que representará nuestros datos sensibles, y devolveremos un resumen del tipo de objetos que hayamos detectado.
A continuación os mostramos la imagen que utilizaremos:
Para cifrar la imagen, vamos a utilizar un script en Python que se ejecuta en una instancia de EC2 (aplicación Encryptor – ver Figura 2) ; en un escenario real, este paso se realizaría en un entorno seguro como un Nitro Enclave o una estación de trabajo segura antes de transferir los datos cifrados. La aplicación Encryptor utiliza el cifrado de AWS KMS con una clave para cifrar.
Nota: también es posible utilizar claves asimétricas para realizar el cifrado/descifrado.
Ahora que la imagen está cifrada, veamos cada componente y qué papel juega en la arquitectura de la solución. En la figura 3 tenéis cómo se vería dicha arquitectura:
La aplicación Cliente lee el archivo cifrado y lo envía a la aplicación Servidor a través del canal vsock (canal de comunicación local seguro).
La aplicación Servidor, que se ejecuta dentro de un Nitro Enclave, extrae la clave de datos cifrada y la envía a AWS KMS para que la descifre. Una vez que la clave de datos se descifra, la aplicación Servidor la utiliza para descifrar la imagen y ejecutar inferencias en ella para detectar los objetos en la imagen (en nuestro caso, coches de F1). Una vez que la inferencia está completa, los resultados se devuelven a la aplicación Cliente sin exponer la imagen original o los datos sensibles.
Para permitir que el Nitro Enclave se comunique con AWS KMS, utilizamos la Herramienta de Enclave KMS que utiliza el vsock para conectarse a AWS KMS y descifrar la clave cifrada.
El vsock-proxy (empaquetado con el Nitro CLI) enruta el tráfico entrante desde la Herramienta KMS hacia AWS KMS siempre que el punto final de AWS KMS esté incluido en la lista de permisos del vsock-proxy. La respuesta de AWS KMS se envía luego de regreso a la Herramienta de Enclave KMS a través del vsock.
Como parte de la solicitud a AWS KMS, la Herramienta de Enclave KMS extrae y envía un documento firmado que contiene las mediciones del enclave para confirmar su identidad. AWS KMS verifica este documento antes de descifrar la clave de datos. Una vez verificada, la clave se descifra y se devuelve de forma segura a la Herramienta KMS, que la transfiere de manera segura a la aplicación Servidor para descifrar la imagen.
Environment Setup
Vaya por delante que para ejecutar la siguiente solución necesitaréis una cuenta de AWS y un role (IAM) con los permisos adecuados.
Plantilla CloudFormation
Paso 1. Nos bajamos la siguiente plantilla de CloudFormation: nitro-enclave-demo.yaml. Esta plantilla nos permitirá desplegar los recursos necesarios. A grandes rasgos:
- Definirá los parámetros, como la ID de la última AMI de Amazon Linux.
- Creará los recursos como un Grupo de Seguridad, Puntos de Enlace de VPC, una Puerta de Enlace de Internet, VPC, Subredes Públicas y Privadas, Tablas de Rutas, Registros de Flujo de VPC, Roles de IAM, Instancias EC2, y Claves KMS.
- Configurará la seguridad para permitir el acceso a sistemas de administración de AWS y servicios como SSM, EC2, y KMS.
- Establecerá una infraestructura para ejecutar instancias EC2 en una VPC con acceso controlado a través de puntos de enlace privados y configuración de roles de IAM.
- Proporcionará integración con servicios de AWS como STS y KMS a través de puntos de enlace privados.
Paso 2. Una vez desplegado, desde la pestaña de “Resources” en el stack NitroEnclaveStack de la consola de CloudFormation, buscaremos la instancia (NitroEnclaveInstance) y haremos click en su ID para navegar a la instancia.
Paso 3. Selecciona “Session Manager” y haz click en “Connect”.
Configuración EC2
Después de haber provisionado la instancia EC2 y habernos conectado a ella, sigue estos pasos para configurarla:
Paso 1. Instala la CLI de Nitro Enclaves que te permitirá construir y ejecutar una aplicación de Nitro Enclave:
sudo amazon-linux-extras install aws-nitro-enclaves-cli -y
sudo yum install aws-nitro-enclaves-cli-devel -y
Paso 2. Verifica que la CLI de Nitro Enclaves se haya instalado correctamente ejecutando el siguiente comando:
nitro-cli --version
Paso 3. Para descargar la aplicación desde GitHub y construir una imagen de Docker, primero necesitas instalar Docker y Git ejecutando los siguientes comandos:
sudo yum install git -y
sudo usermod -aG ne ssm-user
sudo usermod -aG docker ssm-user
sudo systemctl start docker && sudo systemctl enable docker
La Configuración del Enclave de Nitro
Como comentábamos al principio, una Enclave de Nitro es un entorno aislado que se ejecuta dentro de la instancia EC2, por lo tanto, necesitamos especificar los recursos (CPU y Memoria) que el servicio de asignación de Enclave de Nitro dedica al contenedor del enclave.
Introduce los siguientes comandos para establecer la CPU y Memoria disponibles para que el servicio de asignación de Enclave de Nitro las asigne a tu contenedor:
ALLOCATOR_YAML=/etc/nitro_enclaves/allocator.yaml
MEM_KEY=memory_mib
DEFAULT_MEM=20480
sudo sed -r "s/^(\s*${MEM_KEY}\s*:\s*).*/\1${DEFAULT_MEM}/" -i "${ALLOCATOR_YAML}"
sudo systemctl start nitro-enclaves-allocator.service && sudo systemctl enable nitro-enclaves-allocator.service
Para verificar que la configuración se haya aplicado, ejecuta el siguiente comando y toma nota de los valores para memory_mib y cpu_count:
cat /etc/nitro_enclaves/allocator.yaml
Creando una Imagen de Enclave de Nitro
Descarga el Proyecto y Construye la Imagen Base de Enclave
Ahora que la instancia EC2 está configurada, descargaremos el código y construiremos la imagen base de Docker del enclave. Esta imagen contiene el Kit de Desarrollo de Software (SDK) de Nitro Enclaves que permite a un enclave solicitar un documento de certificación firmado criptográficamente del Hipervisor de Nitro. El documento de certificación incluye medidas únicas (hashes SHA384) que se utilizan para demostrar la identidad del enclave a servicios como AWS KMS.
Clona el Proyecto en GitHub
cd ~/ && git clone https://github.com/aws-samples/aws-nitro-enclaves-ai-ml-object-detection.git
Navega hasta la carpeta del proyecto clonado y construye la imagen “enclave_base”:
cd ~/aws-nitro-enclaves-ai-ml-object-detection/enclave-base-image
sudo docker build ./ -t enclave_base
Nota: El paso anterior tomará aproximadamente de 10 a 12 minutos para completarse.
Construye y Ejecuta la Imagen de Enclave de Nitro
Para construir la imagen de Enclave de Nitro del trabajo, construye una imagen de docker de tu aplicación y luego utiliza la CLI de Nitro para construir la imagen de Enclave de Nitro:
Descarga el modelo pre-entrenado de TensorFlow:
cd ~/aws-nitro-enclaves-ai-ml-object-detection/src
mkdir -p models/faster_rcnn_openimages_v4_inception_resnet_v2_1 && cd models/
wget -O tensorflow-model.tar.gz https://tfhub.dev/google/faster_rcnn/openimages_v4/inception_resnet_v2/1?tf-hub-format=compressed
tar -xvf tensorflow-model.tar.gz -C faster_rcnn_openimages_v4_inception_resnet_v2_1
Navega hasta la carpeta del caso de uso y construye la imagen de docker para la aplicación:
cd ~/aws-nitro-enclaves-ai-ml-object-detection/src
sudo docker build ./ -t nitro-enclave-container-ai-ml:latest
Usa la CLI de Nitro para construir un Archivo de Imagen de Enclave (.eif) utilizando la imagen de docker que construiste en el paso anterior:
sudo nitro-cli build-enclave --docker-uri nitro-enclave-container-ai-ml:latest --output-file nitro-enclave-container-ai-ml.eif
La salida del paso anterior produce los registros de configuración de plataforma o hashes PCR y un archivo de imagen de enclave de nitro (.eif). Toma nota del valor de PCR0, que es un hash del archivo de imagen de enclave. Ejemplo PCR0:
{ "Measurements": { "PCR0": "7968aee86dc343ace7d35fa1a504f955ee4e53f0d7ad23310e7df535a187364a0e6218b135a8c2f8fe205d39d9321923" ... }
}
Lanza el contenedor de Enclave de Nitro usando el Archivo de Imagen de Enclave (.eif) generado en el paso anterior y asignale recursos. Debes asignar al menos 4 veces el tamaño del archivo EIF para la memoria del enclave. Esto es necesario porque el sistema de archivos tmpfs utiliza la mitad de la memoria y el resto de la memoria se utiliza para descomprimir el initramfs inicial donde reside el ejecutable de la aplicación. Para la asignación de CPU, debes asignar CPU en núcleos completos, es decir, 2x vCPU para instancias con hiperprocesamiento x86.
En nuestro caso, vamos a asignar 14 GB o 14,366 MB para el enclave:
sudo nitro-cli run-enclave --cpu-count 2 --memory 14336 --eif-path nitro-enclave-container-ai-ml.eif
Nota: Dale unos segundos para que el servidor se inicie antes de ejecutar la aplicación Cliente en la siguiente sección.
Actualiza la Política de Clave KMS para Incluir el Hash PCR0
Ahora que tienes el valor de PCR0 para tu imagen de enclave, actualiza la política de clave KMS para permitir únicamente el acceso de tu contenedor de Enclave de Nitro a la clave KMS.
Paso 1. Navega al servicio AWS KMS y asegúrate de estar en la misma región donde se implementó tu plantilla de CloudFormation.
Paso 2. Selecciona “Claves administradas por el cliente”.
Paso 3. Busca una clave con el alias “EnclaveKMSKey” y haz clic en ella.
Paso 4. Haz clic en “Editar” en la “Política de clave”.
Paso 5. Desplázate hasta el final de la política de clave y reemplaza el valor de “EXAMPLETOBEUPDATED” para la clave “kms:RecipientAttestation:PCR0” con el hash PCR0 que anotaste en la sección anterior y haz clic en “Guardar cambios”.
Detección de Objetos de IA/ML usando un Enclave de Nitro
Ahora que tienes un archivo de imagen de enclave, ejecuta los componentes de la solución.
Instalación de Requisitos para la Aplicación Cliente
Paso 1. Instala los requisitos de Python usando el siguiente comando:
cd ~/aws-nitro-enclaves-ai-ml-object-detection/src
pip3 install -r requirements.txt
Paso 2. Establece la región en la que se implementó tu pila de CloudFormation. En nuestro caso seleccionamos North Virginia:
CFN_REGION=us-east-1
Paso 3. Ejecuta el siguiente comando para encriptar la imagen usando la clave KMS de AWS “EnclaveKMSKey”, asegúrate de reemplazar “us-east-1” con la región donde desplegaste tu plantilla de CloudFormation:
python3 ./envelope-encryption/encryptor.py --filePath ./images/car-race.jpg --cmkId alias/EnclaveKMSkey --region $CFN_REGION
Paso 4. Verifica que la salida contenga: ¿file encrypted? True
Nota: El comando anterior genera dos archivos: un archivo de imagen encriptada y un archivo de clave de datos encriptada. El archivo de clave de datos se genera para que podamos demostrar un intento desde la instancia principal de desencriptar la clave de datos.
Lanzamiento del Proxy VSock
Lanza el Proxy VSock que hace de intermediario para las solicitudes desde el Enclave de Nitro a un punto final externo, en este caso, a AWS KMS. Ten en cuenta que el archivo vsock-proxy-config.yaml contiene una lista de endpoints que permiten listar aquellos con los que un enclave puede comunicarse.
cd ~/aws-nitro-enclaves-ai-ml-object-detection/src
vsock-proxy 8001 "kms.$CFN_REGION.amazonaws.com" 443 --config vsock-proxy-config.yaml &
Detección de Objetos usando Enclaves de Nitro
Envía la imagen encriptada al enclave para desencriptar la imagen y usar el modelo de IA/ML para detectar objetos y devolver un resumen de los objetos detectados:
cd ~/aws-nitro-enclaves-ai-ml-object-detection/src
python3 client.py --filePath ./images/air-show.jpg.encrypted | jq -C '.'
El paso anterior toma alrededor de un par de minutos en finalizar cuando se llama por primera vez. Dentro del enclave, la aplicación del servidor desencripta la imagen, la ejecuta a través del modelo de IA/ML para generar una lista de objetos detectados y devuelve esa lista a la aplicación del cliente.
Credenciales de la Instancia Principal
Intento de Desencriptar la Clave de Datos usando las Credenciales de la Instancia Principal
Para demostrar que la instancia principal no puede desencriptar el contenido, intenta desencriptar la imagen usando las credenciales de la instancia principal:
cd ~/aws-nitro-enclaves-ai-ml-object-detection/src
aws kms decrypt --ciphertext-blob fileb://images/air-show.jpg.data_key.encrypted --region $CFN_REGION
Nota: Se espera que el comando falle con AccessDeniedException, ya que a la instancia principal no se le permite desencriptar la clave de datos.
Limpiamos nuestro entorno
Como siempre aconsejamos, una vez realizada la prueba y verificado el funcionamiento, procederemos a borrar la infraestructura para no incurrir en gastos adicionales.
Paso 1. Abre la consola de AWS CloudFormation en: https://console.aws.amazon.com/cloudformation/.
Paso 2. Selecciona el stack que creaste anteriormente, (ej: NitroEnclaveStack).
Paso 3. Elige Eliminar, luego elige Eliminar stack.
Paso 4. El estado del stack es inicialmente DELETE_IN_PROGRESS. Haz clic en el botón Actualizar periódicamente para actualizar su estado. El estado cambia a DELETE_COMPLETE después de que haya terminado y el nombre del stack ya no aparecerá en tu lista de stacks activas.
Conclusión
En este post, hemos visto cómo procesar datos sensibles con Enclaves de Nitro en Amazon EC2, así como cómo la integración de un enclave con AWS KMS para restringir el acceso y que solo el Enclave de Nitro pueda usar la clave para desencriptar la imagen.