Ejecutar un Contenedor con Menos Privilegios

DockerBeginner
Practicar Ahora

Introducción

Los contenedores empaquetan y ejecutan aplicaciones, pero el empaquetado por sí solo no elige una identidad de proceso segura. Este proceso de trabajo preparado de DeskLite genera un pequeño informe de tickets y actualmente se ejecuta como root.

Aprenderás el flujo mínimo de Docker y después volverás a construir y ejecutar el proceso de trabajo con una identidad distinta de root y solo el acceso a archivos que necesita su tarea. Docker y una imagen base fija están preparados; no se requiere una cuenta de registro de imágenes ni experiencia previa con Docker.

Conocer el Proyecto de Contenedor Preparado

En este paso identificarás los archivos que componen la tarea de generación de informes.

Una imagen es un sistema de archivos empaquetado junto con una configuración de inicio. Un contenedor es una instancia en ejecución de una imagen, con sus propios procesos y una capa de sistema de archivos que admite escritura. Un Dockerfile describe cómo construir una imagen. Una etiqueta, como ticket-report:practice, es un nombre práctico para una imagen; puede pasar a identificar una nueva imagen después de reconstruirla.

cd /home/labex/project/ticket-worker
ls

Espera Dockerfile, worker.sh, input y output. El proceso de trabajo preparado lee filas CSV, escribe su cantidad en un informe y después sigue en ejecución para que podamos inspeccionarlo.

cat input/tickets.csv

Hay tres filas de tickets después de la cabecera. El informe esperado es tickets=3.

cat Dockerfile

FROM selecciona la imagen base de la caché, WORKDIR selecciona el directorio de trabajo de la imagen y COPY añade el proceso de trabajo. USER root elige la identidad del proceso. CMD es el comando de inicio predeterminado, representado como una lista. Cambiarás únicamente USER.

La etiqueta base desklite-base:1.37.0 ya está en la caché a partir de una imagen fija de BusyBox. Todas las construcciones y ejecuciones posteriores usan archivos locales. Los comandos de Docker usan sudo para acceder al daemon de este laboratorio. Ese acceso al nivel del sistema anfitrión es potente; es independiente de la identidad de aplicación cuyos privilegios reduciremos dentro del contenedor.

Construir y Ejecutar el Proceso de Trabajo Existente

En este paso establecerás el estado base, que funciona pero tiene privilegios excesivos.

docker build lee el Dockerfile y un contexto de construcción, el directorio de archivos disponibles para COPY. Aquí, . significa el directorio actual. -t asigna la etiqueta local de la imagen:

sudo docker build -t ticket-report:practice .

Espera una construcción correcta. La imagen existe, pero todavía no hay ningún proceso de trabajo en ejecución.

docker run crea un contenedor. -d lo ejecuta en segundo plano y --name le da un nombre reutilizable. --network none desactiva la red porque este informe solo necesita archivos.

Un montaje de enlace expone una ruta del sistema anfitrión en una ruta del contenedor. Cada --mount describe su tipo, origen (src) y destino (dst). Este estado base expone ambos directorios con permiso de escritura porque no está presente la opción readonly. Restringiremos la entrada más adelante.

sudo docker run -d --name ticket-worker --network none --mount type=bind,src=/home/labex/project/ticket-worker/input,dst=/data/input --mount type=bind,src=/home/labex/project/ticket-worker/output,dst=/data/output ticket-report:practice

Docker imprime un ID de contenedor. No es el ID de usuario del proceso de trabajo. El proceso de trabajo original debería generar su informe, que inspeccionaremos a continuación.

Inspeccionar la Identidad Real en Ejecución

En este paso distinguirás un informe correcto de una ejecución con el mínimo privilegio.

docker ps enumera los contenedores en ejecución:

sudo docker ps

Busca ticket-worker. docker logs lee la salida estándar de la aplicación:

sudo docker logs ticket-worker

Espera worker uid=0 y tickets=3. El UID 0 es root. La tarea de negocio funciona, pero usa más privilegios de los necesarios.

docker exec ejecuta un comando de inspección en este contenedor en ejecución. id -u imprime su identificador numérico efectivo de usuario:

sudo docker exec ticket-worker id -u

Espera 0 de nuevo. docker inspect lee la configuración. --format selecciona un campo; copia exactamente la plantilla entre comillas. .Config.User es el usuario configurado:

sudo docker inspect --format '{{.Config.User}}' ticket-worker

Espera root. El ajuste de la imagen y la inspección del proceso real coinciden. Un archivo que dijera «no root» no aportaría estas pruebas.

Reconstruir con el Usuario Preparado Distinto de Root

En este paso establecerás la identidad de aplicación predeterminada de la imagen.

La imagen base de la caché ya incluye nobody, con UID 65534. No se necesita crear un usuario ni una contraseña. Abre el Dockerfile:

nano Dockerfile

Cambia únicamente la instrucción USER. El archivo completo debe ser:

FROM desklite-base:1.37.0
WORKDIR /app
COPY worker.sh /app/worker.sh
USER nobody
CMD ["sh", "/app/worker.sh"]

Guarda con Ctrl+O, Enter y sal con Ctrl+X.

sudo docker build -t ticket-report:practice .

La etiqueta ahora identifica la imagen reconstruida. Los contenedores existentes no pasan automáticamente a usar una imagen reconstruida; el contenedor antiguo con root sigue siendo la instancia anterior. En el siguiente paso lo reemplazarás y darás a la nueva identidad el acceso de salida requerido.

Reemplazar el Contenedor con Montajes Restringidos

En este paso combinarás la identidad distinta de root con permisos de archivo que permitan trabajar.

docker rm elimina un contenedor. -f lo detiene primero si está en ejecución. Elimina únicamente el contenedor con nombre de este ejercicio:

sudo docker rm -f ticket-worker

El directorio del informe pertenecía inicialmente a root. Como en el laboratorio de permisos de archivos, un proceso que no sea root necesita propiedad y permisos para escribir su salida. chown -R cambia recursivamente el directorio y su informe existente al UID y GID 65534. El modo 750 da acceso completo al propietario sin acceso público de escritura:

sudo chown -R 65534:65534 output
sudo chmod 750 output

Ahora crea el reemplazo. El montaje de entrada añade readonly; el montaje de salida sigue admitiendo escritura. Esto impide modificar la entrada y permite generar el informe:

sudo docker run -d --name ticket-worker --network none --mount type=bind,src=/home/labex/project/ticket-worker/input,dst=/data/input,readonly --mount type=bind,src=/home/labex/project/ticket-worker/output,dst=/data/output ticket-report:practice

Espera un nuevo ID de contenedor. La imagen ahora proporciona el usuario distinto de root; no se necesita sobrescribirlo con root en la ejecución.

Verificar la Identidad, el Acceso y la Salida de Negocio

En este paso comprobarás las tres partes del resultado en el contenedor de reemplazo.

sudo docker exec ticket-worker id -u

Espera 65534. Comprueba la salida real del proceso de trabajo:

sudo docker logs ticket-worker

Espera worker uid=65534 y tickets=3. Lee el archivo de salida real a través del contenedor:

sudo docker exec ticket-worker cat /data/output/report.txt

Espera tickets=3. Esto demuestra que reducir los privilegios no impidió realizar la tarea.

Inspecciona los montajes. La plantilla entre comillas selecciona .Mounts y le da formato JSON:

sudo docker inspect --format '{{json .Mounts}}' ticket-worker

Busca /data/input con "RW":false y /data/output con "RW":true. RW significa lectura/escritura. Los mismos bytes de entrada están disponibles para lectura, pero el montaje deniega la escritura.

Intenta crear un archivo de forma inocua para probar esa restricción. touch crearía un archivo vacío si estuviera permitido:

sudo docker exec ticket-worker touch /data/input/probe.txt

Espera un error Read-only file system y un código de salida distinto de cero. Esta denegación es intencionada. No hagas que la entrada permita escritura para eliminarla; el informe ya se generó correctamente usando entrada de solo lectura.

Confirmar que el Proceso de Trabajo Final Sigue en Ejecución

En este paso dejarás el proceso de trabajo previsto listo para su inspección.

sudo docker ps

El contenedor llamado ticket-worker debería estar en ejecución. Confirma una vez más el usuario configurado y el informe:

sudo docker inspect --format '{{.Config.User}}' ticket-worker
sudo docker exec ticket-worker cat /data/output/report.txt

Espera nobody y tickets=3. La comprobación del curso también compara la imagen en ejecución con tu etiqueta de entrega, comprueba el PID real del proceso de trabajo y prueba la entrada y la salida originales. Reconstruir un Dockerfile sin reemplazar el contenedor es insuficiente.

Este ejercicio reduce los privilegios de la identidad de aplicación y el acceso a archivos. No convierte el acceso al daemon de Docker en un acceso sin privilegios ni establece una política completa de endurecimiento de contenedores para producción.

Resumen

Construiste una imagen real con una identidad de aplicación distinta de root, ejecutaste esa imagen con entrada restringida y acceso operativo a la salida, y verificaste el proceso y el informe reales. La configuración de la imagen, la identidad en ejecución, los modos de montaje y el comportamiento funcional contribuyen al estado final.