Skip to main content

2.1 Host

Informaci贸n

Cuando hablamos de Host nos referimos a cualquier m谩quina que est茅 corriendo el demonio de Docker y sea la capa que se interpone entre el demonio y la propia infraestructura.

En muchos entornos productivos, se suelen usar herramientas que permiten la orquestaci贸n de contenedores de una forma m谩s sencilla, por ejemplo Kubernetes, Docker Swarm, Openshift... Cada una de esas herramientas tiene su propio enfoque de orquestaci贸n de contenedores y sus propias recomendaciones de seguridad y de buenas pr谩cticas. Nosotros en este cap铆tulo queremos centrarnos en un entorno simple y puro, donde solo contamos con el sistema operativo del host y el demonio de Docker para orquestar nuestros contenedores.

Muchas de las recomendaciones y estrategias que veremos a continuaci贸n son f谩cilmente portables a sistemas de orquestaci贸n de contenedores.

2.1.1 Bastionado

Aunque el bastionado es un proceso ciertamente complejo, podemos plantear al menos dos escenarios bien diferenciados a la hora de trabajar con Docker en entornos de desarrollo modernos (Agile, DevOps鈥).

Un primer entorno podr铆a ser local. La m谩quina que se utiliza para las actividades de desarrollo de software, QA, etc.. donde esa m谩quina suele utilizarse como ordenador personal/trabajo con entorno gr谩fico, acceso a aplicaciones de mensajer铆a como Slack, etc... y que no se dedica al despliegue de aplicaciones para recibir tr谩fico de fuera. Este entorno que se suele denominar local es donde m谩s volatilidad y dinamismo tenemos con respecto a los contenedores, ya que los equipos suelen trabajar con diferentes contenedores y relaciones entre ellos, especialmente si se usan microservicios o se desarrollan librer铆as.

Otro entorno podr铆a ser aquella(s) m谩quina(s) donde desplegamos los contenedores que esperamos sean estables a lo largo del tiempo y que soporten tr谩fico de internet o realicen un trabajo productivo definido. Este tipo de entornos suelen ser m谩quinas espec铆ficas que se usan solo para esto y que normalmente suelen ir evolucionando y modificando su estado a lo largo del tiempo, a trav茅s de trabajos y pipelines de integraci贸n continua o despliegue continuo. En entornos de trabajo moderno solemos encontrarnos al menos con 2 entornos staging y live. Siendo live el m谩s cr铆tico para nuestros objetivos empresariales y de seguridad.

Progresi贸n de entornos: local, development, staging y finalmente liveProgresi贸n de entornos: local, development, staging y finalmente live

En muchas ocasiones, no prestamos la suficiente atenci贸n al entorno local que por su naturaleza distinta para cada individuo, hace que sea dif铆cil de armonizar y bastionar desde un punto de vista IT / CISO. Un primer consejo ser铆a el hacer uso de m谩quinas virtuales dentro de nuestro entorno local que nos permitan aislar mucho m谩s el impacto de seguridad que pueda tener una mala configuraci贸n de Docker en nuestra m谩quina host.

Muestra como los contenedores y docker se ejecutan dentro de una m谩quina virtual que vive dentro de la m谩quina hostMuestra como los contenedores y docker se ejecutan dentro de una m谩quina virtual que vive dentro de la m谩quina host

Adem谩s sumariamos esta ventaja adicional, ya que podr铆amos desplegar esos contenedores en nuestra m谩quina virtual, compartiendo el mismo sistema operativo de nuestro entorno de live o staging, evitando as铆 posibles limitaciones de Docker con MacOs o Windows. Obviamente este enfoque nos obliga a tener una m谩quina con recursos suficientes, y tambi茅n cierto manejo de m谩quinas virtuales y su ciclo de vida, as铆 como los vol煤menes compartidos o los recursos entre host <-> virtual host <-> docker <-> container. Este tipo de enfoque es especialmente interesante cuando queremos tener un entorno m谩s de laboratorio para explorar im谩genes de terceros o cuando no confiemos en el contenido de las mismas

Independientemente de usar m谩quinas virtuales o no, deberemos seguir ciertas recomendaciones.

Actualizaciones

Es fundamental mantener nuestro host actualizado tanto el sistema operativo como el demonio de Docker. Hist贸ricamente la actualizaci贸n del demonio de Docker podr铆a arrastrar ca铆das de servicio ya que los contenedores se paran cuando paramos Docker (para su actualizaci贸n). Un modo sencillo para evitarlo es hacer uso de la capacidad live-restore que no para los contenedores al cerrarse el demonio y que reconecta con ellos una vez se levanta el demonio de nuevo.

dockered --live-restore

Antivirus

Cada vez es m谩s com煤n en el mundo empresarial el uso de antivirus. En la propia documentaci贸n de Docker se menciona que los escaneos sobre las carpetas que usan los contenedores pueden generar errores inesperados. La soluci贸n en este caso ser铆a excluir los directorios de Docker pero hacer esc谩neres cuando los contenedores no est谩n corriendo, usando cron jobs o una planificaci贸n adecuada.

2.1.2 Gesti贸n de disco

Particiones espec铆ficas para los contenedores

Aunque Docker cuenta con un sistema bastante eficiente de gesti贸n de las im谩genes usando chunks y hashes, es muy habitual que nuestros contenedores ocupen cada vez m谩s y m谩s espacio en disco. Una buena recomendaci贸n ser铆a mover la ubicaci贸n por defecto que usa Docker para la permanencia en disco var/lib/docker a un disco o partici贸n independiente donde podamos hacer un buen mantenimiento y monitoreo del espacio.

Purgar Docker regularmente

Por la naturaleza de Docker, y de los contenedores ef铆meros, es com煤n ocupar el espacio en disco con elementos que ya no son 煤tiles. Una forma de purgar es usando el comando prune de docker y sus diversas variaciones.

Si todos los contenedores y sus elementos relacionados (vol煤menes, redes, im谩genes..) son prescindibles podemos hacer:

# Paramos todos los contenedores
docker kill $(docker ps -q)
# Borramos todos los contenedores
docker rm $(docker ps -a -q)
# Purgamos el sistema completamente (incluyendo vol煤menes)
docker system prune --volumes

2.1.3 El poder de root

Si profundizamos en los pilares de Docker (cap铆tulo 1.2) es f谩cil llegar a la conclusi贸n de que en realidad al compartir el kernel si somos root en el contenedor tambi茅n lo somos en el host.

Siendo la premisa siempre evitar la ejecuci贸n de un contenedor como root.

Repeat after me:聽鈥渇riends don鈥檛 let friends run containers as root!鈥 Node.js Docker Cheat Sheet

Root siempre es Root

Si no hacemos nada de forma proactiva o utilizamos un orquestador de contenedores bien configurado, es bastante probable que estemos lanzando los contenedores como root:

docker run -it busybox
/ # id
uid=0(root) gid=0(root) groups=10(wheel)

Contenedores Rootless

Si hacemos uso de Linux namespaces y la documentaci贸n oficial de Docker vemos que ya existen formas para poder ejecutar un contenedor sin ser root. En algunas ocasiones las im谩genes que usamos ya definen este comportamiento usando la instrucci贸n USER (cap铆tulo 3.1.2), pero otras veces no y por ello necesitamos hacer uso del argumento --user del demonio de Docker, como veremos a continuaci贸n.

Modo non-root (con el usuario nobody):

docker run -it --user nobody busybox
/ # id
uid=65534(nobody) gid=65534(nobody)

De todas formas si utilizamos un orquestador como Kubernetes, encontramos que ya existen mecanismos que nos permiten gestionar este tipo de casos, simplemente ajustando las especificaciones de seguridad:

spec:
securityContext:
runAsNonRoot: true

Demonio de Docker Rootless

Desde la versi贸n 19 de Docker, podemos hacer uso del modo rootless a nivel del demonio y desde la 20 se considera estable. Este modo tiene algunas limitaciones, pero tiene algunas ventajas que no podemos dejar escapar sin m谩s.

As铆 pues, la forma de instalarlo ser铆a casi id茅ntica al demonio de Docker convencional (ejecutalo como usuario sin privilegios) aunque tendremos que instalar previamente otras dependencias como newuidmap y newgidmap:

curl -sSL https://get.docker.com/rootless | sh

Lo que nos ofrece esta versi贸n del demonio de Docker, es poder ejecutar el demonio bajo un usuario sin privilegios, l贸gicamente este comportamiento tambi茅n se extiende a los contenedores que maneja el demonio.

Ahora bien, tiene algunas desventajas que aparecen al perder los privilegios. Como cabr铆a esperarse no se podr谩 usar --net=host, algunos drivers de almacenamiento dejar谩n de funcionar y adem谩s perderemos capacidades de exponer puertos privilegiados (< 1024) entre otras..

Pero no deja de ser una buena opci贸n si no te limita la p茅rdida de privilegios, especialmente en entornos productivos.

2.1.4 Monitorizar

A煤n no siendo un entorno productivo, es siempre importante monitorizar nuestros contenedores para detectar anomal铆as que podr铆an alertarnos de potenciales vulnerabilidades.

Si contamos con pocos contenedores o no necesitamos una visi贸n a largo plazo siempre podemos hacer uso del comando stats de Docker para sacar las m茅tricas de nuestros contenedores

En el cap铆tulo 4 hablamos sobre herramientas que pueden ayudarnos a gestionar m煤ltiples aspectos:

  • Con Dockprom (cap铆tulo 4.9) podemos monitorizar nuestros contenedores f谩cilmente y de una forma visual con Grafana pero adem谩s contamos con la posibilidad de incluir alertas
  • Con K6 (cap铆tulo 4.10) podemos f谩cilmente detectar problemas a la hora de escalar nuestros contenedores, especialmente con servicios web. Para entender la fragilidad a la que nos encontramos, expuestos cuando no hacemos pruebas de carga las vulnerabilidades como CWE-1333: Inefficient Regular Expression Complexity basadas en una Expresi贸n regular deficiente, pueden provocar una denegaci贸n de servicio
  • Revisar los logs de nuestros contenedores es tambi茅n importante ya sea usando los drivers de Docker o alguna soluci贸n m谩s compleja.

2.1.5 Parches de seguridad

Security is a process, not a product. Bruce Schneier

Cuando damos el paso de usar contenedores, a帽adimos m谩s capas a nuestra infraestructura. Ya que la virtualizaci贸n es un ladrillo m谩s en la pared que vamos construyendo.

Esto supone un reto a la hora de parchear y actualizar ya que cada parte (Sistema operativo, sistema de orquestaci贸n, im谩genes, librer铆as...) deber谩 ser actualizado de forma frecuente.

Aunque es un proceso que parece sencillo, est谩 intr铆nsecamente lleno de retos especialmente si estamos manteniendo sistemas arcaicos que hace uso de interfaces antiguas o obsoletas.

Esto se ha convertido en un problema cada vez m谩s importante y ganado importancia en el OWASP top 10 (A09-2017 Using Components with Known Vulnerabilities y A06-2021 Vulnerable and Outdated Components) ya que el tiempo pasa y las vulnerabilidades descubiertas siguen creciendo en nuestra contra.

consejo

La mejor forma de abordar este tema es darle el peso que se merece y crear un plan sostenible en el tiempo que permita de una forma automatizada nos ayude con la tediosa tarea de actualizar y parchear de una manera frecuente a medida que las vulnerabilidades van apareciendo en nuestro extenso sistema de dependencias.