Skip to main content

Container Immutability

¿Qué significa esto?

El contenedor no será modificado durante su tiempo de vida. Nada será agregado o removido del contenedor. Caso sea necesario, otra imagen será creada con la debida modificación y una nueva instancia será creada.

La ventaja de este enfoque es que siempre sabemos el estado en que se encuentra el contenedor. Entrar en una aplicación y alterar una configuración no será posible replicar nuevamente.

Los contenedores inmutables nos permiten:

  • Utilizar métodos avanzados de deployment
  • Facilidad en el rollback
  • Mayor confiabilidad
  • Mejor seguridad (a nivel de contenedor), pues siempre podemos crear uno nuevo teniendo certeza de que no existe ningún proceso malicioso corriendo.

La inmutabilidad y la facilidad de quemar nuevas imágenes es uno de los motivos que hicieron los contenedores tan populares.

Lo que podemos hacer para tener certeza de que los contenedores están inmutables.

  • Remover bash/shell para que no sea posible interactuar con el contenedor.
  • Tornar el file system de solo lectura para que no sea posible escribir
  • Correr con un usuario y no con root.

Eso es lo que mandan las buenas prácticas y cómo hacer el hardening de un contenedor.

Pero ¿qué pasa si no tenemos el control de las imágenes de los contenedores? En ese caso siempre es posible utilizar la imagen como base y alterar generando una nueva.

Si aún así no fuera posible, es posible hacer algunas cosas.

Podemos alterar el command y pasar todos los comandos que queramos y por último llamar al comando que correrá el contenedor.

  containers:
- image: nginx
name: nginx
# recomiendo quitar todo permiso de escritura recursivamente antes de llamar al ejecutable del nginx
command:
- "chmod a-w -R / && nginx"
resources: {}

Aún podemos resolver esto usando el startup probe que es bien similar al readiness o liveness probes, pero esos dos últimos solamente serán ejecutados después de que el startup probe obtenga éxito, caso contrario el contenedor quedará reiniciándose todo el tiempo. De esta forma no necesitaremos ni conocer el entrypoint del contenedor.

  containers:
- image: nginx
name: nginx
startupProbe:
exec:
command:
- chmod
- a-w -R /
initialDelaySeconds: 1
periodSeconds: 5

Estos métodos arriba son posibles, pero no es porque sea posible que debemos hacer eso.

La mejor manera sería imponer read only en el /root usando el SecurityContext y/o PodSecurityPolicies.

  containers:
- image: httpd
name: httpd
securityContext:
readOnlyRootFilesystem: true

Sin embargo, este pod necesitará escribir en /usr/local/apache2/logs. Cuando esto suceda podemos montar un emptyDir.

  containers:
- image: httpd
name: httpd
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- mountPath: /usr/local/apache2/logs
name: logs
volumes:
- name: logs
emptyDir: {}

Si transportamos esto al mundo de docker sería como ejecutar el comando abajo.

docker run --read-only --tmpfs /run my-container

Aún podríamos usar el recurso del init container que inicia antes de la aplicación, prepara el terreno y solamente después de que termina será disparado el contenedor de la aplicación. Si el init container prepara el volumen que será usado por la aplicación dándole a ella solamente recursos de lectura puede ser un buen escenario.

alt text

Obviamente es necesario proteger que usuarios cambien el spec de un pod usando RBAC.