AI Red Teaming Lab — Parte 3

Build de CoPyRIT y push al Container Registry

Objetivo

En el post anterior construimos la base de infraestructura: Resource Group, Azure Container Registry y Azure OpenAI con GPT-4o desplegado y verificado. En este post tomamos el código de PyRIT, construimos la imagen Docker de CoPyRIT y la subimos al ACR.

Al final de este post tendremos:

  • Docker Engine instalado y funcionando en WSL2
  • El repositorio de PyRIT clonado y analizado
  • La imagen de CoPyRIT construida localmente (pyrit:latest)
  • La imagen publicada en tu Azure Container Registry, lista para el despliegue del Post 4

WSL2 en lugar de Docker Desktop

Antes de entrar en materia, una nota sobre la decisión de entorno que tomamos en este lab.

El camino más obvio para trabajar con Docker en Windows es Docker Desktop una aplicación que instala un daemon de Docker y expone una interfaz gráfica para gestionar contenedores. Sin embargo, Docker Desktop requiere que VirtualMachinePlatform esté habilitado como feature del sistema operativo Windows, y su instalador usa DISM (Deployment Image Servicing and Management) para habilitarlo automáticamente.

En algunos sistemas Windows, especialmente con actualizaciones acumulativas recientes o ciertos estados del store de componentes, ese proceso de DISM falla con el críptico error 0x8000ffff. Es un problema de corrupción del CBS (Component Based Servicing) de Windows, no de Docker en sí. NOTA: Esto lo sé por que después de pelearme unas 2 horas no conseguía instalarlo y para ello necesitaría restablecer prácticamente Windows por que la parte del DISM estaba corrupta y no conseguía recuperarla como tal , así que… quién pueda/quiera con docker desktop puede hacerlo igual realmente saltandose los pasos previos de instalación en wsl2 que hacemos nosotros.

Por lo tanto , como comentaba la alternativa, y la que usamos en este lab, es Docker Engine directamente en WSL2 (Windows Subsystem for Linux 2). WSL2 ya corre un kernel Linux completo en una VM ligera gestionada por Windows y Docker Engine instalado dentro de esa distribución Linux funciona exactamente igual que en una máquina Linux nativa, sin depender del sistema de componentes de Windows.

Prerequisitos

Antes de continuar, verificamos que tenemos :

bash

# WSL2 con Ubuntu 24.04
wsl -d Ubuntu-24.04

# Dentro de Ubuntu — Docker instalado y arrancado
sudo service docker start
docker --version
# Docker version 29.5.3, build d1c06ef

# Azure CLI disponible en WSL2
az --version
az account show --query name --output tsv
# Azure for Students

Si Docker no está instalado en tu WSL2, el proceso de instalación es:

bash

# Dependencias
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg

# Clave GPG y repositorio de Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Instalación
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin

# Arranque del servicio
sudo service docker start

# Añadir tu usuario al grupo docker (evita usar sudo en cada comando)
sudo usermod -aG docker $USER
newgrp docker

Paso 1: Clonar el repositorio de PyRIT

bash

cd ~
git clone https://github.com/microsoft/PyRIT.git
cd PyRIT

Configurar git si no lo tienes (necesario para el script de build):

bash

git config --global user.email "[email protected]"
git config --global user.name "TuNombre"

El script de build hace git rev-parse HEAD para etiquetar la imagen con el hash del commit. Sin configuración de git el comando falla.

Paso 2: Entender la estructura del repositorio

Antes de ejecutar nada, veremos que hay en el repo:

bash

ls ~/PyRIT
CITATION.cff    Makefile      SUPPORT.md    docker          pyproject.toml
CODE_OF_CONDUCT LICENSE       assets        frontend        pyrightconfig.json
MANIFEST.in     README.md     build_scripts gui-deploy.yml  pyrit
NOTICE.txt      SECURITY.md   doc           infra           tests

Las carpetas relevantes para nosotros:

pyrit/ — el código Python del framework. Aquí están los targets, attacks, converters, scorers y el sistema de memoria. Es el núcleo de PyRIT.

frontend/ — el código de CoPyRIT. Una aplicación React con Fluent UI que constituye la interfaz gráfica. Cuando el contenedor arranca en modo gui, este frontend se sirve desde el backend FastAPI.

docker/ — todo lo relacionado con la construcción de la imagen. Contiene el Dockerfile, el script de build build_pyrit_docker.py, el script de arranque start.sh y documentación.

infra/ — templates Bicep para el despliegue en Azure. Los usaremos en el Post 4.

.devcontainer/ — el Dockerfile de la imagen base. Este es el punto de partida del build en dos fases que veremos a continuación.

gui-deploy.yml — el pipeline de CI/CD que usa el Microsoft AI Red Team internamente para desplegar CoPyRIT en Azure Container Apps vía Azure DevOps. Nos sirve como referencia para entender el proceso de despliegue.


Paso 3: La arquitectura en dos fases

Lo primero que llama la atención al inspeccionar el docker/Dockerfile es esta línea:

dockerfile

ARG BASE_IMAGE
FROM ${BASE_IMAGE} AS production

No hay una imagen base fija — el Dockerfile requiere que se le pase BASE_IMAGE como argumento. Esto es porque el build de CoPyRIT funciona en dos fases:

Fase 1 — Imagen base: se construye a partir de mcr.microsoft.com/devcontainers/python:3.14-bookworm y añade todo el entorno de desarrollo: Python 3.11 en un venv con uv, Node.js 24 para el frontend, el driver ODBC de Microsoft para Azure SQL, dependencias de sistema para el SDK de Azure Speech, y configuración del usuario vscode. Esta imagen pesa ~838 MB comprimida.

Fase 2 — Imagen de producción: toma la imagen base como base y añade el código fuente de PyRIT, instala las dependencias Python con todos los extras (speech, opencv, fairness_bias, fastapi, playwright), construye el frontend React, y configura el entrypoint.

Paso 4: Inspeccionar el script de build

bash

cat docker/build_pyrit_docker.py

El script acepta dos modos:

--source pypi --version X.Y.Z   # Instala PyRIT desde PyPI
--source local                   # Instala desde el código fuente local

Para el lab usamos --source local porque queremos la versión más reciente del repo, incluyendo CoPyRIT aunque no esté aún publicada en PyPI. El script:

  1. Verifica si la imagen pyrit-devcontainer ya existe (la construye si no)
  2. Obtiene el hash del commit actual con git rev-parse HEAD
  3. Ejecuta docker build pasando el devcontainer como BASE_IMAGE
  4. Etiqueta la imagen con el hash del commit y con latest

Paso 5: Construir la imagen

bash

cd ~/PyRIT
sudo python3 docker/build_pyrit_docker.py --source local

Si es la primera vez que ejecutas esto, el build tarda 15-30 minutos — el devcontainer descarga Python, Node.js 24, instala el driver ODBC de Microsoft, compila dependencias nativas y construye el frontend React. Una vez construido el devcontainer, los builds posteriores son instantáneos gracias al sistema de caché de Docker (todas las capas CACHED).

Output esperado al completar:

Verifica que las imágenes existen:

bash

sudo docker images | grep pyrit

Sobre el tamaño: La imagen de producción pesa ~7.5 GB sin comprimir (1.79 GB comprimida). Es grande porque incluye Python con todos sus extras, Node.js, drivers ODBC, el SDK de Azure Speech, y el frontend compilado. Para un lab esto es aceptable; en producción se podría optimizar con un build multi-stage más agresivo.

Paso 6: Taggear la imagen para el ACR

Azure Container Registry espera imágenes con el formato <loginServer>/<repositorio>:<tag>. Taggeamos la imagen local:

bash

sudo docker tag pyrit:latest acrcyberlabpyrit.azurecr.io/copyrit:latest

Por qué copyrit como nombre del repositorio: Seguimos la convención del pipeline oficial de Microsoft (gui-deploy.yml) que referencia la imagen como copyrit.

Verificamos el tag:

bash

sudo docker images | grep acrcyberlabpyrit

Paso 7: Autenticar contra el ACR y hacer push

El ACR que creamos en el Post 2 tiene --admin-enabled false — si os acordáis , ya dijimos que deshabilitamos el usuario administrador estático. Esto significa que no podemos autenticarnos con usuario/password fijos; necesitamos un token de acceso emitido por Azure AD.NOTA: Más adelante veréis que tendremos un post para el tema de la autenticación , ya que usaremos caddy por que entra era imposible debido a restricciones de la suscripción de azure for student.

El comando az acr login normalmente hace esto de forma transparente, pero en WSL2 hay un problema: busca Docker en el PATH del sistema Windows, no en el PATH de Linux. El resultado es el error DOCKER_COMMAND_ERROR.

La solución es obtener el token manualmente y pasárselo a Docker directamente:

bash

# Obtener el refresh token del ACR
ACR_TOKEN=$(az acr login --name acrcyberlabpyrit --expose-token --output tsv --query accessToken)

# Autenticar Docker con ese token
echo $ACR_TOKEN | sudo docker login acrcyberlabpyrit.azurecr.io \
  --username 00000000-0000-0000-0000-000000000000 \
  --password-stdin

Sobre el username 00000000-0000-0000-0000-000000000000: No es un placeholder es el username especial que ACR usa cuando la autenticación es vía token de Azure AD en lugar de credenciales de usuario.

Output esperado:

Login Succeeded

Ahora el push:

bash

sudo docker push acrcyberlabpyrit.azurecr.io/copyrit:latest

La imagen pesa 1.79 GB comprimida — el push tardará 5-15 minutos dependiendo de tu conexión. Verás el progreso capa a capa:


Paso 8: Verificar que la imagen está en el ACR

bash

az acr repository show-tags \
  --name acrcyberlabpyrit \
  --repository copyrit \
  --output table

Output esperado:

Result
--------
latest

También se puede verificar desde el portal de Azure: Container Registry → acrcyberlabpyrit → Repositories → copyrit.

El modo de arranque: PYRIT_MODE

Antes de acabar poor hoy, vale la pena entender cómo funciona el entrypoint del contenedor, el start.sh ,porque condiciona cómo lo configuraremos en el Post 4.

El contenedor soporta dos modos de arranque controlados por la variable de entorno PYRIT_MODE:

PYRIT_MODE=jupyter — arranca JupyterLab en el puerto 8888. Útil para explorar los notebooks de documentación de PyRIT de forma interactiva.

PYRIT_MODE=gui — arranca CoPyRIT en el puerto 8000. Este es el modo que nos interesa para el lab.

En modo gui, el script también gestiona la configuración de la base de datos:

  • Si AZURE_SQL_SERVER está definido → usa Azure SQL
  • Si no → usa SQLite local

Para el lab usaremos SQLite (sin Azure SQL) para simplificar la configuración y ahorrar crédito de Azure for Students.

El contenedor también lee la variable PYRIT_ENV_CONTENTS — si está definida, escribe su contenido en ~/.pyrit/.env dentro del contenedor. Así es como pasamos las credenciales de Azure OpenAI al contenedor sin hardcodearlas en la imagen.


Arquitectura del Lab hasta ahora

Próximos pasos

En el siguiente post vamos a desplegar CoPyRIT en Azure Container Apps: crearemos el Container App, configuraremos la autenticación con Entra ID, pasaremos las credenciales de Azure OpenAI vía variables de entorno, y verificaremos que CoPyRIT arranca y conecta con GPT-4o.

YA QUEDA MENOS!!!

Entradas relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *