Cómo agregar o eliminar capacidades con comandos Docker

DockerBeginner
Practicar Ahora

Introducción

Docker es una herramienta poderosa para la contenerización de aplicaciones, y comprender cómo gestionar las capacidades de los contenedores es crucial para optimizar la seguridad y el rendimiento. Este tutorial le guiará a través del proceso de agregar y eliminar capacidades utilizando comandos de Docker, ayudándole a adaptar su entorno de contenedor a necesidades específicas.

En este laboratorio, aprenderá qué son las capacidades de Docker, cómo mejoran la seguridad de los contenedores y cómo gestionarlas eficazmente. Al final de este tutorial, podrá agregar y eliminar capacidades de sus contenedores Docker con confianza.

Comprensión de las Capacidades de Docker

Las capacidades de Docker son una característica de seguridad que le permite otorgar o revocar permisos específicos del kernel de Linux a un contenedor. Antes de comenzar a experimentar con las capacidades, comprendamos qué son y por qué son importantes.

¿Qué son las Capacidades de Docker?

Las capacidades en Docker se basan en el sistema de capacidades del kernel de Linux, que divide los privilegios tradicionalmente asociados con el usuario root en unidades distintas. Este enfoque es más seguro que el modelo tradicional de privilegio root de todo o nada.

De forma predeterminada, los contenedores Docker se ejecutan con un conjunto limitado de capacidades, proporcionando un equilibrio razonable entre funcionalidad y seguridad. Sin embargo, es posible que deba agregar o eliminar capacidades según los requisitos de su aplicación.

Examinemos las capacidades predeterminadas de un contenedor Docker. Ejecute el siguiente comando para iniciar un contenedor y ver sus capacidades:

docker run --rm -it ubuntu:22.04 capsh --print

Debería ver una salida similar a esta:

Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Ambient set =
Current IAB: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
Securebits: 00/0x0/1'b0
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
 secure-no-ambient-raise: no (unlocked)
uid=0(root) gid=0(root) groups=0(root)

Esta salida muestra las capacidades otorgadas al contenedor de forma predeterminada. Estas capacidades controlan lo que el contenedor puede hacer dentro del sistema.

Por qué Importan las Capacidades

La gestión adecuada de las capacidades de Docker es crucial para:

  1. Seguridad Mejorada: Al limitar las capacidades, reduce el daño potencial si un contenedor se ve comprometido.
  2. Control Detallado: Puede permitir operaciones privilegiadas específicas sin otorgar acceso root completo.
  3. Principio del Mínimo Privilegio: Los contenedores solo deben tener las capacidades que necesitan para funcionar correctamente.

A continuación, exploremos cómo agregar capacidades a los contenedores Docker.

Agregar Capacidades a los Contenedores Docker

En este paso, aprenderemos a agregar capacidades específicas a un contenedor Docker utilizando la bandera --cap-add. Esto es útil cuando su aplicación requiere ciertos privilegios que no están incluidos en el conjunto predeterminado.

Sintaxis básica para agregar capacidades

La sintaxis básica para agregar una capacidad a un contenedor Docker es:

docker run --cap-add=<CAPABILITY> <IMAGE> <COMMAND>

Intentemos agregar la capacidad NET_ADMIN, que permite al contenedor realizar varias operaciones relacionadas con la red:

docker run --rm -it --cap-add=NET_ADMIN ubuntu:22.04 capsh --print

La salida mostrará que la capacidad NET_ADMIN se ha agregado al contenedor:

Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap,cap_net_admin=ep

Observe la adición de cap_net_admin al final de la lista de capacidades.

Agregar múltiples capacidades

A menudo, es posible que deba agregar múltiples capacidades a un contenedor. Puede hacerlo especificando la bandera --cap-add varias veces:

docker run --rm -it --cap-add=NET_ADMIN --cap-add=SYS_TIME ubuntu:22.04 capsh --print

Este comando agrega las capacidades NET_ADMIN y SYS_TIME al contenedor. La capacidad SYS_TIME permite al contenedor modificar el reloj del sistema.

Ejemplo práctico: Modificación de interfaces de red

Para demostrar un uso práctico de las capacidades, creemos un contenedor con la capacidad NET_ADMIN e intentemos modificar la configuración de la interfaz de red:

docker run --rm -it --cap-add=NET_ADMIN ubuntu:22.04 /bin/bash

Ahora está dentro del contenedor con un shell bash. Instalemos el paquete iproute2 para trabajar con interfaces de red:

apt-get update && apt-get install -y iproute2

Ahora, intente crear una interfaz de red ficticia:

ip link add dummy0 type dummy
ip link show dummy0

Debería ver la salida que muestra la interfaz de red ficticia recién creada:

6: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 2a:d5:cd:70:91:f4 brd ff:ff:ff:ff:ff:ff

Esta operación fallaría sin la capacidad NET_ADMIN. Puede salir del contenedor escribiendo exit.

Visualización de las capacidades del contenedor

Para inspeccionar las capacidades de un contenedor en ejecución, puede usar el comando docker inspect. Primero, iniciemos un contenedor con capacidades agregadas en modo desasociado:

docker run -d --name cap-test --cap-add=NET_ADMIN ubuntu:22.04 sleep 3600

Ahora, inspeccione el contenedor para ver sus capacidades:

docker inspect cap-test | grep -A 20 CapAdd

La salida mostrará que la capacidad NET_ADMIN se ha agregado:

            "CapAdd": [
                "NET_ADMIN"
            ],

Recuerde limpiar después de este paso:

docker stop cap-test
docker rm cap-test

Comprender cómo agregar capacidades a los contenedores Docker le brinda más control sobre lo que pueden hacer sus contenedores, al tiempo que mantiene la seguridad.

Eliminación de Capacidades de los Contenedores Docker

En este paso, aprenderemos a eliminar capacidades de los contenedores Docker utilizando la bandera --cap-drop. Esta es una práctica de seguridad importante que sigue el principio del mínimo privilegio: los contenedores solo deben tener las capacidades que absolutamente necesitan.

Sintaxis básica para eliminar capacidades

La sintaxis básica para eliminar una capacidad de un contenedor Docker es:

docker run --cap-drop=<CAPABILITY> <IMAGE> <COMMAND>

Intentemos eliminar la capacidad CHOWN, que permite cambiar la propiedad de los archivos:

docker run --rm -it --cap-drop=CHOWN ubuntu:22.04 capsh --print

En la salida, notará que cap_chown ya no aparece entre las capacidades:

Current: cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep

Eliminación de múltiples capacidades

Puede eliminar múltiples capacidades especificando la bandera --cap-drop varias veces:

docker run --rm -it --cap-drop=CHOWN --cap-drop=NET_RAW ubuntu:22.04 capsh --print

Este comando elimina las capacidades CHOWN y NET_RAW del contenedor.

Ejemplo práctico: Prueba de restricciones de capacidad

Creemos un contenedor con la capacidad CHOWN eliminada e intentemos cambiar la propiedad del archivo:

docker run --rm -it --cap-drop=CHOWN ubuntu:22.04 /bin/bash

Dentro del contenedor, creemos un archivo de prueba e intentemos cambiar su propiedad:

touch test_file
ls -l test_file
chown nobody:nogroup test_file

Debería ver un mensaje de error que indica que la operación no está permitida:

chown: changing ownership of 'test_file': Operation not permitted

Esto demuestra que la eliminación de la capacidad CHOWN impide que el contenedor cambie la propiedad del archivo, aunque el contenedor se esté ejecutando como root. Escriba exit para salir del contenedor.

Uso de --cap-add y --cap-drop

Puede usar las banderas --cap-add y --cap-drop en el mismo comando para controlar con precisión las capacidades de su contenedor:

docker run --rm -it --cap-add=NET_ADMIN --cap-drop=CHOWN ubuntu:22.04 capsh --print

Este comando agrega la capacidad NET_ADMIN mientras elimina la capacidad CHOWN.

Eliminación de todas las capacidades y adición de capacidades específicas

Para una máxima seguridad, puede eliminar todas las capacidades y luego agregar solo las específicas que su aplicación necesita:

docker run --rm -it --cap-drop=ALL --cap-add=NET_BIND_SERVICE ubuntu:22.04 capsh --print

Este comando crea un contenedor con solo la capacidad NET_BIND_SERVICE, que permite la vinculación a puertos privilegiados (por debajo de 1024).

Prueba de un contenedor con todas las capacidades eliminadas

Creemos un contenedor con todas las capacidades eliminadas y observemos las restricciones:

docker run -d --name no-caps --cap-drop=ALL ubuntu:22.04 sleep 3600

Ahora, adjuntemos al contenedor e intentemos realizar varias operaciones:

docker exec -it no-caps /bin/bash

Dentro del contenedor, intente hacer ping a un host externo:

apt-get update && apt-get install -y iputils-ping
ping -c 1 google.com

Probablemente verá un error porque el contenedor no tiene las capacidades necesarias para crear sockets de red sin formato requeridos para ping.

Salga del contenedor escribiendo exit y luego limpie:

docker stop no-caps
docker rm no-caps

Al comprender cómo eliminar capacidades de los contenedores Docker, puede mejorar significativamente la seguridad de sus aplicaciones en contenedores al limitar estrictamente lo que cada contenedor puede hacer.

Combinación de la Gestión de Capacidades con las Mejores Prácticas de Seguridad

En este paso final, exploraremos cómo combinar la gestión de capacidades de Docker con otras mejores prácticas de seguridad para crear contenedores seguros y de mínimo privilegio.

Creación de un contenedor con un perfil de capacidades personalizado

Creemos un ejemplo más complejo que combine la gestión de capacidades con otras características de seguridad:

docker run -d --name secure-container \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --read-only \
  --tmpfs /tmp \
  ubuntu:22.04 sleep 3600

Este comando:

  • Elimina todas las capacidades
  • Agrega solo la capacidad NET_BIND_SERVICE
  • Hace que el sistema de archivos del contenedor sea de solo lectura
  • Crea un sistema de archivos temporal en /tmp para operaciones de escritura

Inspeccionemos este contenedor para ver su configuración:

docker inspect secure-container | grep -A 5 CapAdd
docker inspect secure-container | grep -A 5 CapDrop
docker inspect secure-container | grep ReadonlyRootfs

Debería ver la salida que confirma estas configuraciones de seguridad:

            "CapAdd": [
                "NET_BIND_SERVICE"
            ],
            "CapDrop": [
                "ALL"
            ],
"ReadonlyRootfs": true,

Prueba de las restricciones

Conectémonos a nuestro contenedor seguro y probemos las restricciones:

docker exec -it secure-container /bin/bash

En el contenedor, intente modificar un archivo del sistema:

echo "test" > /etc/test

Debería ver un error porque el sistema de archivos es de solo lectura:

bash: /etc/test: Read-only file system

Ahora, intente escribir en el directorio /tmp:

echo "test" > /tmp/test
cat /tmp/test

Esto debería funcionar porque montamos un tmpfs escribible en /tmp:

test

Salga del contenedor escribiendo exit.

Uso de capacidades con usuarios no root

Para mayor seguridad, puede ejecutar contenedores como usuarios no root mientras sigue administrando las capacidades. Primero, creemos un nuevo contenedor que combine un usuario no root con capacidades específicas:

docker run -d --name nonroot-container \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --user 1000:1000 \
  ubuntu:22.04 sleep 3600

Tenga en cuenta que, aunque agregamos la capacidad NET_BIND_SERVICE y se está ejecutando como un usuario no root, las capacidades de Linux solo se aplican a los procesos que se ejecutan como root de forma predeterminada. Para permitir que los usuarios no root usen las capacidades, se necesitarían configuraciones adicionales como binarios setuid o capacidades ambientales.

Capacidades en docker-compose

Si usa docker-compose para administrar múltiples contenedores, puede especificar capacidades en su archivo docker-compose.yml:

version: "3"
services:
  webapp:
    image: ubuntu:22.04
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /tmp

Esto proporciona una forma consistente de administrar las capacidades en sus implementaciones de contenedores.

Limpieza

Limpiemos los contenedores que creamos:

docker stop secure-container nonroot-container
docker rm secure-container nonroot-container

Resumen de las mejores prácticas

Aquí hay algunas de las mejores prácticas para administrar las capacidades de Docker:

  1. Elimine todas las capacidades y agregue solo lo que necesita
  2. Combine la gestión de capacidades con otras características de seguridad:
    • Sistema de archivos de solo lectura
    • Usuarios no root
    • Perfiles Seccomp
    • AppArmor o SELinux
  3. Audite regularmente las capacidades del contenedor
  4. Mantenga Docker y las imágenes de contenedor actualizadas
  5. Use herramientas de escaneo de vulnerabilidades de contenedores

Siguiendo estas prácticas, puede mejorar significativamente la seguridad de sus contenedores Docker.

Resumen

En este laboratorio, ha aprendido a administrar eficazmente las capacidades de los contenedores Docker para mejorar la seguridad y la funcionalidad. Aquí hay un resumen de lo que logró:

  1. Comprendió qué son las capacidades de Docker y por qué son importantes para la seguridad de los contenedores.
  2. Aprendió a agregar capacidades a los contenedores utilizando la bandera --cap-add, lo que permite a los contenedores realizar operaciones privilegiadas específicas.
  3. Practicó la eliminación de capacidades con la bandera --cap-drop, implementando el principio del mínimo privilegio.
  4. Exploró las mejores prácticas para combinar la gestión de capacidades con otras características de seguridad para crear entornos de contenedores seguros.

Al aplicar estas técnicas, puede crear contenedores que tengan precisamente los permisos que necesitan, ni más ni menos. Este enfoque reduce significativamente la superficie de ataque potencial de sus aplicaciones en contenedores, al tiempo que garantiza que tengan la funcionalidad necesaria para operar correctamente.

Continúe explorando las características de seguridad de Docker y recuerde auditar y actualizar regularmente las configuraciones de sus contenedores para mantener una sólida postura de seguridad en sus entornos en contenedores.