Compartir comentarios
Las respuestas se generan en base a la documentación.

Perfiles de seguridad AppArmor para Docker

AppArmor (Application Armor) es un módulo de seguridad de Linux que protege a un sistema operativo y sus aplicaciones de amenazas de seguridad. Para usarlo, un administrador del sistema asocia un perfil de seguridad de AppArmor con cada programa. Docker espera encontrar una política de AppArmor cargada y aplicada.

Docker genera y carga automáticamente un perfil predeterminado para los contenedores llamado docker-default. El binario de Docker genera este perfil en tmpfs y luego lo carga en el kernel.

Note

Este perfil se utiliza en los contenedores, no en el demonio de Docker.

Existe un perfil para el demonio de Docker Engine, pero actualmente no se instala con los paquetes deb. Si tienes interés en el código fuente del perfil del demonio, se encuentra en contrib/apparmor en el repositorio de código fuente de Docker Engine.

Entender las políticas

El perfil docker-default es el predeterminado para ejecutar contenedores. Protege de forma moderada a la vez que proporciona una amplia compatibilidad de aplicaciones. El perfil se genera a partir de la siguiente plantilla.

Cuando ejecutas un contenedor, este utiliza la política docker-default a menos que la anules con la opción security-opt. Por ejemplo, lo siguiente especifica explícitamente la política predeterminada:

$ docker run --rm -it --security-opt apparmor=docker-default hello-world

Cargar y descargar perfiles

Para cargar un nuevo perfil en AppArmor para usarlo con contenedores:

$ apparmor_parser -r -W /ruta/a/tu_perfil

Luego, ejecuta el perfil personalizado con --security-opt:

$ docker run --rm -it --security-opt apparmor=tu_perfil hello-world

Para descargar un perfil de AppArmor:

# descargar el perfil
$ apparmor_parser -R /ruta/al/perfil

Recursos para escribir perfiles

La sintaxis para la búsqueda de archivos (globbing) en AppArmor es un poco diferente a la de algunas otras implementaciones de globbing. Te sugerimos que eches un vistazo a algunos de los siguientes recursos con respecto a la sintaxis de los perfiles de AppArmor.

Perfil de ejemplo de Nginx

En este ejemplo, creas un perfil de AppArmor personalizado para Nginx. A continuación se muestra el perfil personalizado.

#include <tunables/global>

profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>

  network inet tcp,
  network inet udp,
  network inet icmp,

  deny network raw,

  deny network packet,

  file,
  umount,

  deny /bin/** wl,
  deny /boot/** wl,
  deny /dev/** wl,
  deny /etc/** wl,
  deny /home/** wl,
  deny /lib/** wl,
  deny /lib64/** wl,
  deny /media/** wl,
  deny /mnt/** wl,
  deny /opt/** wl,
  deny /proc/** wl,
  deny /root/** wl,
  deny /sbin/** wl,
  deny /srv/** wl,
  deny /tmp/** wl,
  deny /sys/** wl,
  deny /usr/** wl,

  audit /** w,

  /var/run/nginx.pid w,

  /usr/sbin/nginx ix,

  deny /bin/dash mrwklx,
  deny /bin/sh mrwklx,
  deny /usr/bin/top mrwklx,

  capability chown,
  capability dac_override,
  capability setuid,
  capability setgid,
  capability net_bind_service,

  deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
  # deny write to files not in /proc/<number>/** or /proc/sys/**
  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
  deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
  deny @{PROC}/sysrq-trigger rwklx,
  deny @{PROC}/mem rwklx,
  deny @{PROC}/kmem rwklx,
  deny @{PROC}/kcore rwklx,

  deny mount,

  deny /sys/[^f]*/** wklx,
  deny /sys/f[^s]*/** wklx,
  deny /sys/fs/[^c]*/** wklx,
  deny /sys/fs/c[^g]*/** wklx,
  deny /sys/fs/cg[^r]*/** wklx,
  deny /sys/firmware/** rwklx,
  deny /sys/kernel/security/** rwklx,
}
  1. Guarda el perfil personalizado en el disco en el archivo /etc/apparmor.d/containers/docker-nginx.

    La ruta del archivo en este ejemplo no es un requisito. En producción, puedes usar otra.

  2. Carga el perfil.

    $ sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-nginx
    
  3. Ejecuta un contenedor con el perfil.

    Para ejecutar nginx en modo desasociado (detached):

    $ docker run --security-opt "apparmor=docker-nginx" \
         -p 80:80 -d --name apparmor-nginx nginx
    
  4. Entra al contenedor en ejecución (exec).

    $ docker container exec -it apparmor-nginx bash
    
  5. Prueba algunas operaciones para verificar el perfil.

    root@6da5a2a930b9:~# ping 8.8.8.8
    ping: Lacking privilege for raw socket.
    
    root@6da5a2a930b9:/# top
    bash: /usr/bin/top: Permission denied
    
    root@6da5a2a930b9:~# touch ~/thing
    touch: cannot touch 'thing': Permission denied
    
    root@6da5a2a930b9:/# sh
    bash: /bin/sh: Permission denied
    
    root@6da5a2a930b9:/# dash
    bash: /bin/dash: Permission denied
    

Acabas de desplegar un contenedor protegido con un perfil apparmor personalizado.

Depurar AppArmor

Puedes usar dmesg para depurar problemas y aa-status para comprobar los perfiles cargados.

Usar dmesg

Aquí tienes algunos consejos útiles para depurar cualquier problema que puedas enfrentar con respecto a AppArmor.

AppArmor envía mensajes bastante detallados a dmesg. Por lo general, una línea de AppArmor se parece a la siguiente:

[ 5442.864673] audit: type=1400 audit(1453830992.845:37): apparmor="ALLOWED" operation="open" profile="/usr/bin/docker" name="/home/jessie/docker/man/man1/docker-attach.1" pid=10923 comm="docker" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0

En el ejemplo anterior, puedes ver profile=/usr/bin/docker. Esto significa que el usuario tiene cargado el perfil de docker-engine (demonio de Docker Engine).

Mira otra línea de registro:

[ 3256.689120] type=1400 audit(1405454041.341:73): apparmor="DENIED" operation="ptrace" profile="docker-default" pid=17651 comm="docker" requested_mask="receive" denied_mask="receive"

Esta vez, el perfil es docker-default, que se ejecuta en los contenedores de forma predeterminada a menos que esté en modo privileged (privilegiado). Esta línea muestra que apparmor ha denegado ptrace en el contenedor. Esto es exactamente lo esperado.

Usar aa-status

Si necesitas comprobar qué perfiles están cargados, puedes usar aa-status. La salida se parece a:

$ sudo aa-status
apparmor module is loaded.
14 profiles are loaded.
1 profiles are in enforce mode.
   docker-default
13 profiles are in complain mode.
   /usr/bin/docker
   /usr/bin/docker///bin/cat
   /usr/bin/docker///bin/ps
   /usr/bin/docker///sbin/apparmor_parser
   /usr/bin/docker///sbin/auplink
   /usr/bin/docker///sbin/blkid
   /usr/bin/docker///sbin/iptables
   /usr/bin/docker///sbin/mke2fs
   /usr/bin/docker///sbin/modprobe
   /usr/bin/docker///sbin/tune2fs
   /usr/bin/docker///sbin/xtables-multi
   /usr/bin/docker///sbin/zfs
   /usr/bin/docker///usr/bin/xz
38 processes have profiles defined.
37 processes are in enforce mode.
   docker-default (6044)
   ...
   docker-default (31899)
1 processes are in complain mode.
   /usr/bin/docker (29756)
0 processes are unconfined but have a profile defined.

La salida anterior muestra que el perfil docker-default que se ejecuta en varios PID de contenedores está en modo enforce. Esto significa que AppArmor está bloqueando y auditando activamente en dmesg cualquier cosa fuera de los límites del perfil docker-default.

La salida anterior también muestra que el perfil /usr/bin/docker (demonio de Docker Engine) se está ejecutando en modo complain. Esto significa que AppArmor solo registra en dmesg la actividad fuera de los límites del perfil. (Excepto en el caso de Ubuntu Trusty, donde se imponen algunos comportamientos interesantes).

Contribuir al código de AppArmor de Docker

Los usuarios avanzados y los administradores de paquetes pueden encontrar un perfil para /usr/bin/docker (demonio de Docker Engine) en contrib/apparmor en el repositorio de código fuente de Docker Engine.

El perfil docker-default para contenedores vive en profiles/apparmor.