Skip to main content

AppArmor: Control de Acceso Basado en Políticas

apparmor

GitLab

Documentación

Es un módulo de seguridad para el kernel Linux que proporciona un control de acceso basado en políticas de seguridad. Incluido en el kernel desde la versión 2.6.36, viene siendo mantenido por Canonical.

Protege proactivamente el sistema operativo y las aplicaciones contra amenazas externas o internas impidiendo que las fallas sean explotadas.

Permite que el administrador del sistema defina un conjunto de permisos para aplicaciones específicas, limitando lo que estas aplicaciones pueden hacer, como acceder a archivos, usar recursos de red o interactuar con otros procesos.

alt text

Este módulo viene precargado junto con la distribución elegida (principalmente las basadas en Debian y Ubuntu) evitando que el usuario precise instalarlo. Está habilitado todo el tiempo solo esperando ser configurado. Otras distribuciones pueden optar por otras alternativas como SELinux.

Para ver si tu distribución posee AppArmor disponible solo ejecuta.

root@cks-master:~# systemctl status apparmor.service
● apparmor.service - Load AppArmor profiles
Loaded: loaded (/lib/systemd/system/apparmor.service; enabled; vendor preset: enabled)
Active: active (exited) since Tue 2024-09-10 06:45:55 UTC; 1 day 13h ago
Docs: man:apparmor(7)
https://gitlab.com/apparmor/apparmor/wikis/home/
Main PID: 627499 (code=exited, status=0/SUCCESS)
Tasks: 0 (limit: 4680)
Memory: 0B
CGroup: /system.slice/apparmor.service
...

Ejecutando el comando abajo podemos ver si está cargado y los perfiles existentes.

root@cks-worker:~# aa-status
apparmor module is loaded.
##### BLOQUE SOBRE PERFIL ####
48 profiles are loaded. # TOTAL
42 profiles are in enforce mode. # La lista de los que están en modo enforce
/snap/snapd/21759/usr/lib/snapd/snap-confine
/snap/snapd/21759/usr/lib/snapd/snap-confine//mount-namespace-capture-helper
/usr/bin/man
/usr/lib/NetworkManager/nm-dhcp-client.action
/usr/lib/NetworkManager/nm-dhcp-helper
/usr/lib/connman/scripts/dhclient-script
/usr/lib/snapd/snap-confine
/usr/lib/snapd/snap-confine//mount-namespace-capture-helper
/usr/sbin/chronyd
/usr/sbin/tcpdump
/{,usr/}sbin/dhclient
cri-containerd.apparmor.d
docker-default
lsb_release
man_filter
man_groff
nvidia_modprobe
nvidia_modprobe//kmod
snap-update-ns.google-cloud-cli
snap-update-ns.lxd
snap.lxd.activate
snap.lxd.benchmark
snap.lxd.buginfo
snap.lxd.check-kernel
snap.lxd.daemon
snap.lxd.hook.configure
snap.lxd.hook.install
snap.lxd.hook.remove
snap.lxd.lxc
snap.lxd.lxc-to-lxd
snap.lxd.lxd
snap.lxd.migrate
ubuntu_pro_apt_news
ubuntu_pro_esm_cache
ubuntu_pro_esm_cache//apt_methods
ubuntu_pro_esm_cache//apt_methods_gpgv
ubuntu_pro_esm_cache//cloud_id
ubuntu_pro_esm_cache//dpkg
ubuntu_pro_esm_cache//ps
ubuntu_pro_esm_cache//ubuntu_distro_info
ubuntu_pro_esm_cache_systemctl
ubuntu_pro_esm_cache_systemd_detect_virt
6 profiles are in complain mode. # La lista de los que están en modo complain
snap.google-cloud-cli.anthoscli
snap.google-cloud-cli.bq
snap.google-cloud-cli.docker-credential-gcloud
snap.google-cloud-cli.gcloud
snap.google-cloud-cli.gsutil
snap.google-cloud-cli.kubectl
###############################

##### BLOQUE SOBRE PROCESOS ####
6 processes have profiles defined. # Procesos en el sistema que poseen perfiles
6 processes are in enforce mode. # De esos 6 procesos tenemos los 6 en modo enforce
/usr/sbin/chronyd (1371)
/usr/sbin/chronyd (1372)
/usr/local/apache2/bin/httpd (194962) cri-containerd.apparmor.d
/usr/local/apache2/bin/httpd (194975) cri-containerd.apparmor.d
/usr/local/apache2/bin/httpd (194976) cri-containerd.apparmor.d
/usr/local/apache2/bin/httpd (194977) cri-containerd.apparmor.d
0 processes are in complain mode. # Nada en complain
0 processes are unconfined but have a profile defined. # Nada en unconfined

Cada aplicación tiene su perfil, en realidad un perfil puede ser vinculado a un binario ejecutable.

Cada perfil, si cargado, puede estar en uno de los 3 modos:

alt text

  • Unconfined: permite que el proceso salga de la política asociada a él.
  • Complain: permite que el proceso salga, pero será creado un log.
  • Enforce: El proceso solamente hará lo que puede hacer.

Para más comandos con AppArmor podemos instalar apparmor-utils.

root@cks-worker:~# apt-get install apparmor-utils -y

## Posibles comandos
root@cks-worker:~# aa-
aa-audit aa-cleanprof aa-decode aa-enabled aa-exec aa-logprof aa-remove-unknown aa-teardown aa-update-browser
aa-autodep aa-complain aa-disable aa-enforce aa-genprof aa-mergeprof aa-status aa-unconfined
  • aa-complain: para colocar un perfil en modo complain.
  • aa-enforce: para colocar un perfil en modo enforce.
  • aa-unconfined: para colocar un perfil en modo unconfined.
  • aa-genprof y aa-genprof: para crear y eliminar un perfil para un ejecutable.
  • aa-logprof: Procesa las entradas de logs para incluir reglas en el perfil.

Un poco más sobre AppArmor puede encontrarse en https://debian-handbook.info/browse/stable/sect.apparmor.html.

Los perfiles son almacenados en /etc/apparmor.d/.

root@cks-worker:~# ls -l /etc/apparmor.d/
total 104
drwxr-xr-x 2 root root 4096 Sep 10 06:46 abi
drwxr-xr-x 4 root root 12288 Sep 10 06:46 abstractions
drwxr-xr-x 2 root root 4096 Aug 30 21:40 disable
drwxr-xr-x 2 root root 4096 Feb 11 2020 force-complain
drwxr-xr-x 2 root root 4096 Sep 10 06:46 local
-rw-r--r-- 1 root root 1313 Oct 10 2023 lsb_release
-rw-r--r-- 1 root root 1108 Oct 10 2023 nvidia_modprobe
-rw-r--r-- 1 root root 3500 Jan 31 2023 sbin.dhclient # <<<<
drwxr-xr-x 5 root root 4096 Sep 10 06:46 tunables
-rw-r--r-- 1 root root 1724 Jul 18 15:20 ubuntu_pro_apt_news
-rw-r--r-- 1 root root 6853 Jul 18 15:20 ubuntu_pro_esm_cache
-rw-r--r-- 1 root root 3202 Feb 25 2020 usr.bin.man # <<<<
-rw-r--r-- 1 root root 29450 Jul 26 02:58 usr.lib.snapd.snap-confine.real # <<<<
-rw-r--r-- 1 root root 1881 Apr 20 2020 usr.sbin.chronyd # <<<<
-rw-r--r-- 1 root root 1575 Feb 11 2020 usr.sbin.rsyslogd # <<<<
-rw-r--r-- 1 root root 1674 Feb 8 2024 usr.sbin.tcpdump # <<<<

Observa que los nombres de los perfiles, cuando no están definidos, son las rutas de los binarios sustituyendo / por . .

Dentro del directorio /etc/apparmor.d/, también podemos encontrar subdirectorios.

  • abstractions/: Contiene reglas comunes, como acceso básico a archivos o permisos de red, que pueden ser reutilizadas en varios perfiles.

  • tunables/: Define variables configurables, como directorios predeterminados que pueden variar de sistema a sistema (por ejemplo, el directorio del home).

  • disable/: Este directorio almacena enlaces simbólicos a perfiles que fueron explícitamente desactivados. Cuando un enlace a un perfil existe en este directorio, AppArmor lo ignora durante la carga.

    root@cks-worker:/etc/apparmor.d/disable# ls -lha
    total 8.0K
    drwxr-xr-x 2 root root 4.0K Aug 30 21:40 .
    drwxr-xr-x 8 root root 4.0K Sep 10 06:46 ..
    lrwxrwxrwx 1 root root 33 Aug 30 21:40 usr.sbin.rsyslogd -> /etc/apparmor.d/usr.sbin.rsyslogd
  • force-complain/: Contiene enlaces simbólicos a perfiles que están en modo "complain".

  • local/: Almacena configuraciones y ajustes locales para perfiles. Este directorio se usa para sobrescribir o añadir reglas específicas a perfiles predeterminados sin modificar el propio archivo del perfil. Si observamos este directorio tenemos prácticamente los archivos con los mismos nombres de los perfiles.

    root@cks-worker:/etc/apparmor.d/local# ls -l
    total 4
    -rw-r--r-- 1 root root 1111 Oct 10 2023 README
    -rw-r--r-- 1 root root 0 Aug 30 21:41 lsb_release
    -rw-r--r-- 1 root root 0 Aug 30 21:41 nvidia_modprobe
    -rw-r--r-- 1 root root 0 Aug 30 21:40 sbin.dhclient
    -rw-r--r-- 1 root root 0 Aug 30 21:40 ubuntu_pro_apt_news
    -rw-r--r-- 1 root root 0 Aug 30 21:40 ubuntu_pro_esm_cache
    -rw-r--r-- 1 root root 0 Aug 30 21:41 usr.bin.man
    -rw-r--r-- 1 root root 0 Aug 30 21:41 usr.lib.snapd.snap-confine.real
    -rw-r--r-- 1 root root 0 Aug 30 21:44 usr.sbin.chronyd
    -rw-r--r-- 1 root root 0 Aug 30 21:40 usr.sbin.rsyslogd
    -rw-r--r-- 1 root root 0 Aug 30 21:41 usr.sbin.tcpdump

Creando un Perfil para un Binario

Vamos a crear un perfil para curl y probar.

# Probando inicialmente
david@cks-worker:~$ curl devsecops.puziol.com.br
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>cloudflare</center>
</body>
</html>
* Connection #0 to host devsecops.puziol.com.br left intact

# Creando el perfil
# Al final será preguntado si debemos escanear los logs para aplicar posibles permisos.
# Voy a finalizar sin aplicar nada.
root@cks-worker:~# aa-genprof curl
Writing updated profile for /usr/bin/curl.
Setting /usr/bin/curl to complain mode.

Before you begin, you may wish to check if a
profile already exists for the application you
wish to confine. See the following wiki page for
more information:
https://gitlab.com/apparmor/apparmor/wikis/Profiles

Profiling: /usr/bin/curl

Please start the application to be profiled in
another window and exercise its functionality now.

Once completed, select the "Scan" option below in
order to scan the system logs for AppArmor events.

For each AppArmor event, you will be given the
opportunity to choose whether the access should be
allowed or denied.

[(S)can system log for AppArmor events] / (F)inish
Setting /usr/bin/curl to enforce mode.

Reloaded AppArmor profiles in enforce mode.

Please consider contributing your new profile!
See the following wiki page for more information:
https://gitlab.com/apparmor/apparmor/wikis/Profiles

Finished generating profile for /usr/bin/curl.

# Probando
root@cks-worker:~# curl devsecops.puziol.com.br -v
* Could not resolve host: devsecops.puziol.com.br
* Closing connection 0
curl: (6) Could not resolve host: devsecops.puziol.com.br

Curl no posee ningún permiso y la regla predeterminada es denegar. Una vez creado el perfil es colocado automáticamente en modo enforce.

Vamos a hacer algunos análisis.

root@cks-worker:~# aa-status
apparmor module is loaded.
49 profiles are loaded. # Tenemos uno más
43 profiles are in enforce mode.
/snap/snapd/21759/usr/lib/snapd/snap-confine
/snap/snapd/21759/usr/lib/snapd/snap-confine//mount-namespace-capture-helper
/usr/bin/curl #<<<<
/usr/bin/man
...

# Ahora vamos a verificar los archivos creados.
# En la raíz fue creado
root@cks-worker:~# ls -l /etc/apparmor.d/ | grep curl
-rw------- 1 root root 141 Sep 12 13:15 usr.bin.curl

# Pero en local/ no, es decir, no se crea automáticamente en este directorio.
root@cks-worker:~# ls -l /etc/apparmor.d/local/ | grep curl

# Y tenemos nuestro perfil inicial bien limitado.
root@cks-worker:~# cat /etc/apparmor.d/usr.bin.curl
# Last Modified: Thu Sep 12 13:14:48 2024
#include <tunables/global>

/usr/bin/curl {
#include <abstractions/base>

/usr/bin/curl mr,

}

Las denegaciones de AppArmor son registradas en /var/log/syslog (o /var/log/audit/audit.log para violaciones de política no-DBus si auditd está instalado).

El kernel limitará la tasa de denegaciones de AppArmor, lo que puede causar problemas durante la creación del perfil. Puedes evitar esto instalando auditd o ajustando la limitación de tasa en el kernel:

sysctl -w kernel.printk_ratelimit=0

Vamos a utilizar el propio log de AppArmor para incluir reglas en curl. Más adelante vamos a verificar esos logs para crear un perfil de forma manual.

# AppArmor verificará entradas del syslog y ofrecerá añadir permisos
root@cks-worker:/etc/apparmor.d# aa-logprof
Reading log entries from /var/log/syslog.
Updating AppArmor profiles in /etc/apparmor.d.
Enforce-mode changes:

Profile: /usr/bin/curl
Path: /etc/ssl/openssl.cnf
New Mode: owner r
Severity: 2

# ESTÁ OFRECIENDO COLOCAR LA ENTRADA 1 OPENSSL EN EL PERFIL Y VOY A ACEPTAR PUES PARECE TENER SENTIDO.

[1 - #include <abstractions/openssl>]
2 - #include <abstractions/ssl_keys>
3 - owner /etc/ssl/openssl.cnf r,
(A)llow / [(D)eny] / (I)gnore / (G)lob / Glob with (E)xtension / (N)ew / Audi(t) / (O)wner permissions off / Abo(r)t / (F)inish
Adding #include <abstractions/openssl> to profile.

Profile: /usr/bin/curl
Path: /etc/host.conf
New Mode: owner r
Severity: unknown

# CONTINUARÁ PREGUNTANDO SI DEBO COLOCAR NAMESERVICE TAMBIÉN, ESTA VEZ NO VOY A ACEPTAR PARA PROBAR.
# DE AQUÍ EN ADELANTE VOY A DENEGAR TODO

[1 - #include <abstractions/nameservice>]
2 - owner /etc/host.conf r,
(A)llow / [(D)eny] / (I)gnore / (G)lob / Glob with (E)xtension / (N)ew / Audi(t) / (O)wner permissions off / Abo(r)t / (F)inish

Denying via an include file isnt supported by the AppArmor tools

Profile: /usr/bin/curl
Path: /etc/host.conf
New Mode: owner r
Severity: unknown

[1 - #include <abstractions/nameservice>]
2 - owner /etc/host.conf r,
(A)llow / [(D)eny] / (I)gnore / (G)lob / Glob with (E)xtension / (N)ew / Audi(t) / (O)wner permissions off / Abo(r)t / (F)inish

Denying via an include file isnt supported by the AppArmor tools

Profile: /usr/bin/curl
Path: /etc/host.conf
New Mode: owner r
Severity: unknown

[1 - #include <abstractions/nameservice>]
2 - owner /etc/host.conf r,
(A)llow / [(D)eny] / (I)gnore / (G)lob / Glob with (E)xtension / (N)ew / Audi(t) / (O)wner permissions off / Abo(r)t / (F)inish

= Changed Local Profiles =

The following local profiles were changed. Would you like to save them?

[1 - /usr/bin/curl]
(S)ave Changes / Save Selec(t)ed Profile / [(V)iew Changes] / View Changes b/w (C)lean profiles / Abo(r)t
Writing updated profile for /usr/bin/curl.

# Vamos a verificar lo que colocó que fue el openssl
root@cks-worker:/etc/apparmor.d# cat usr.bin.curl
# Last Modified: Thu Sep 12 13:46:48 2024
#include <tunables/global>

/usr/bin/curl {
#include <abstractions/base>
#include <abstractions/openssl>

/usr/bin/curl mr,

}

# Aún no fue suficiente, pues sin resolver los nombres no va a llegar allá,
root@cks-worker:/etc/apparmor.d# curl devsecops.puziol.com.br
curl: (6) Could not resolve host: devsecops.puziol.com.br

# Vamos a permitir el resto

root@cks-worker:/etc/apparmor.d# aa-logprof

root@cks-worker:/etc/apparmor.d# cat usr.bin.curl
# Last Modified: Thu Sep 12 13:52:25 2024
#include <tunables/global>

/usr/bin/curl {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/openssl>

/usr/bin/curl mr,

}

# Lo conseguimos
root@cks-worker:/etc/apparmor.d# curl devsecops.puziol.com.br
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>cloudflare</center>
</body>
</html>

Activando y Desactivando un Perfil

root@cks-worker:# aa-disable curl
Disabling /usr/bin/curl.

# Un enlace simbólico fue creado
root@cks-worker:/etc/apparmor.d# ls -l /etc/apparmor.d/disable/
total 0
lrwxrwxrwx 1 root root 28 Sep 12 13:53 usr.bin.curl -> /etc/apparmor.d/usr.bin.curl
lrwxrwxrwx 1 root root 33 Aug 30 21:40 usr.sbin.rsyslogd -> /etc/apparmor.d/usr.sbin.rsyslogd

Para activar es necesario colocar el perfil en alguno de los modos.

root@cks-worker:/etc/apparmor.d# aa-enforce curl
Setting /usr/bin/curl to enforce mode.

# Y el enlace no existirá más en disable
root@cks-worker:/etc/apparmor.d# ls -l /etc/apparmor.d/disable/
total 0
lrwxrwxrwx 1 root root 33 Aug 30 21:40 usr.sbin.rsyslogd -> /etc/apparmor.d/usr.sbin.rsyslogd

Eliminar un perfil completamente implica eliminar el archivo.

root@cks-worker:/etc/apparmor.d# rm -rf usr.bin.curl

## El comando aa-remove-unknown elimina todos los perfiles que no están en uso.

Extra

Los perfiles de AppArmor son archivos de texto simple. Rutas absolutas, así como globbing de archivo, pueden ser usados al especificar el acceso al archivo.

La mayoría de las reglas de acceso a archivo especifican el tipo de acceso que es permitido:

  • 'r' (lectura)
  • 'w' (escritura)
  • 'x' (ejecución)
  • 'm' (mapa de memoria como ejecutable)
  • 'k' (bloqueo de archivo)
  • 'l' (creación de hard links)
  • 'ix' para ejecutar otro programa con la nueva política de herencia de programa.
  • 'Px' (ejecutar en otro perfil, después de limpiar el entorno)
  • 'Cx' (ejecutar en un perfil hijo, después de limpiar el entorno)
  • 'Ux' (ejecutar sin confinamiento, después de limpiar el entorno).

AppArmor ofrece soporte a controles de acceso para:

  • files
  • Linux capabilities
  • network
  • mount, remount and umount
  • root_pivot
  • ptrace
  • signal
  • DBus
  • unix domain sockets

Variables (por ejemplo, @{HOME}) pueden ser definidas y manipuladas fuera del perfil ( #include <tunables/global>para @{PROC} y @{HOME})

Reglas de denegación explícitas son soportadas para sustituir reglas de permiso, por ejemplo, el acceso a @{HOME}/bin/bad.sh es denegado con auditoría debido a, audit deny @{HOME}/bin/** mrwkl, aunque el acceso general a @{HOME} sea permitido con @{HOME}/** rw.

Aprovechando este gancho vamos a explicar esa /usr/bin/curl mr.

usr.bin/curl

/usr/bin/curl {
#include <abstractions/base>

/usr/bin/curl mr,

}

Esto significa que el archivo puede ser mapeado en memoria (m) y en modo lectura (r).

Aún tenemos más utilidades de línea de comandos para ayudarnos en la creación de perfiles.

root@cks-worker:/etc/apparmor.d# apt install apparmor-easyprof apparmor-notify apparmor-utils -y

Usando el easyprof podemos crear un template inicial, nada muy fuera del alcance del genprof pero puede ser usado caso necesario para mantener un estándar.

# Este comando solamente genera la salida siendo necesario crear el archivo.
root@cks-worker:/etc/apparmor.d# aa-easyprof /usr/bin/curl
# vim:syntax=apparmor
# AppArmor policy for curl
# ###AUTHOR###
# ###COPYRIGHT###
# ###COMMENT###

#include <tunables/global>

# No template variables specified

"/usr/bin/curl" {
#include <abstractions/base>

# No abstractions specified

# No policy groups specified

# No read paths specified

# No write paths specified
}
root@cks-worker:/etc/apparmor.d# aa-easyprof /usr/bin/curl > /etc/apparmor.d/usr.bin.curl

Diferentemente del aa-genprof, el aa-easyprof no carga el perfil en AppArmor, siendo necesario cargar manualmente. Verifica con aa-status antes de aplicar el comando abajo y verás que el perfil no estará disponible.

root@cks-worker:/etc/apparmor.d# apparmor_parser /etc/apparmor.d/usr.bin.curl

Otra manera de crear un perfil es usando el comando aa-autodep. Si fuera para curl tendríamos aa-autodep curl.

Una herramienta que nos ayudará a ver los logs producidos por AppArmor es aa-notify.

# -s número de días
root@cks-worker:/etc/apparmor.d# aa-notify -s 1
AppArmor denials: 33 (since Wed Sep 11 15:27:25 2024)

# Dejándolo más verboso tenemos una muestra de todas las denegaciones que ya tuvimos por el perfil de curl anteriormente.

root@cks-worker:/etc/apparmor.d#
root@cks-worker:/etc/apparmor.d# aa-notify -s 1 -v
Profile: /usr/bin/curl
Operation: open
Name: /etc/ssl/openssl.cnf
Denied: r
Logfile: /var/log/kern.log
(3 found, most recent from 'Thu Sep 12 13:43:28 2024')

Profile: /usr/bin/curl
Operation: create
Denied: create
Family: inet6
Socket type: dgram
Logfile: /var/log/kern.log
(6 found, most recent from 'Thu Sep 12 13:51:13 2024')

Profile: /usr/bin/curl
Operation: open
Name: /etc/host.conf
Denied: r
Logfile: /var/log/kern.log
(6 found, most recent from 'Thu Sep 12 13:51:13 2024')

Profile: /usr/bin/curl
Operation: open
Name: /run/systemd/resolve/stub-resolv.conf
Denied: r
Logfile: /var/log/kern.log
(6 found, most recent from 'Thu Sep 12 13:51:13 2024')

Profile: /usr/bin/curl
Operation: open
Name: /etc/nsswitch.conf
Denied: r
Logfile: /var/log/kern.log
(6 found, most recent from 'Thu Sep 12 13:51:13 2024')

Profile: /usr/bin/curl
Operation: create
Denied: create
Family: inet
Socket type: dgram
Logfile: /var/log/kern.log
(6 found, most recent from 'Thu Sep 12 13:51:13 2024')

AppArmor denials: 33 (since Wed Sep 11 15:27:36 2024)
For more information, please see: https://wiki.ubuntu.com/DebuggingApparmor

La manera más fácil de comenzar a crear un perfil es colocarlo en modo complain. Permitir que haga todo, pero generar logs para que podamos ir permitiendo a lo largo del camino. También podemos siempre usar aa-logprof para esto como se mencionó anteriormente.

Aunque los perfiles de AppArmor sean default-deny por defecto, añadir reglas de denegación explícitas puede proteger contra errores.

Apparmor con Docker y Kubernetes

Como hoy la contenerización está en alza creo que vale la pena profundizar más en este asunto.

Vamos a crear un perfil sin usar aa-genprof. No estamos colocando ningún binario específico para este perfil, apenas permisos.

Vamos a hacer algunos comentarios en mayúsculas para diferenciar de los #includes.

#include <tunables/global>

# ESTE NOMBRE SERÁ USADO PARA REFERENCIAR EL PERFIL CUANDO QUERAMOS USAR
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,

# ELIMINANDO ACCESOS A LOS DIRECTORIOS ABAJO PARA ESCRITURA Y CREACIÓN DE ENLACES
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,

# ELIMINANDO ACCESO A LOS SHELLS
deny /bin/dash mrwklx,
deny /bin/sh mrwklx,
deny /usr/bin/top mrwklx,

# AÑADIENDO ALGUNAS CAPABILITIES
capability chown,
capability dac_override,
capability setuid,
capability setgid,
capability net_bind_service,

# ELIMINANDO PERMISO DE ESCRITURA EN TODOS LOS ARCHIVOS EN /proc, PERO NO EN LOS SUBDIRECTORIOS
deny @{PROC}/* w,
# ELIMINANDO PERMISO DE ESCRITURA EN TODOS LOS DIRECTORIOS /proc/<número>/
deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
# ELIMINANDO PERMISO DE ESCRITURA EN TODOS LOS DIRECTORIOS /proc/sys/** MENOS EL /proc/sys/kernel
deny @{PROC}/sys/[^k]** w,
# ELIMINANDO PERMISO DE ESCRITURA EXCEPTO EN shm** Y /proc/sys/kernel
deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,
# ELIMINANDO PERMISOS DE ESCRITURA, BLOQUEO, CREACIÓN DE ENLACES Y EJECUCIÓN EN /proc/mem,kmem,kcore,sysrg-trigger
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/mem rwklx,
deny @{PROC}/kmem rwklx,
deny @{PROC}/kcore rwklx,

# NO PERMITIR EL COMANDO MOUNT
deny mount,

# ELIMINANDO PERMISOS DE ESCRITURA, BLOQUEO, CREACIÓN DE ENLACES Y EJECUCIÓN EN /sys/
deny /sys/[^f]*/** wklx,
deny /sys/f[^s]*/** wklx,
# SOLAMENTE DENTRO DE /sys/fs
deny /sys/fs/[^c]*/** wklx,
deny /sys/fs/c[^g]*/** wklx,
deny /sys/fs/cg[^r]*/** wklx,
# ELIMINANDO PERMISOS DE LECTURA, ESCRITURA, BLOQUEO, CREACIÓN DE ENLACES, Y EJECUCIÓN
deny /sys/firmware/** rwklx,
deny /sys/kernel/security/** rwklx,
}

Vamos a crear este perfil y hacerlo disponible.

root@cks-worker:~# vim /etc/apparmor.d/docker-nginx
# Cargar el perfil
root@cks-worker:~# apparmor_parser /etc/apparmor.d/docker-nginx

Vamos a ejecutar nginx de diferentes maneras y observar.

# Aquí no estamos pasando ningún perfil
root@cks-worker:~# docker run nginx

# Aquí vamos a usar la opción de seguridad para pasar un perfil que ni existe.
# Observa que no es posible iniciar pasando un perfil inexistente.

root@cks-worker:~# docker run --security-opt apparmor=teste nginx
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: unable to apply apparmor profile: apparmor failed to apply profile: write /proc/self/attr/apparmor/exec: no such file or directory: unknown.
ERRO[0000] error waiting for container:

# Existe el perfil de docker que es docker-default además de aquel que cargamos docker-nginx.
root@cks-worker:~# aa-status | grep docker
docker-default
docker-nginx

# Ejecutando con docker-default en modo detached para liberar el contenedor y hacer análisis.
root@cks-worker:~# docker run --security-opt apparmor=docker-default -d nginx
cf92415f4a8f7f1a233a3c80f49efb4e90d863f898149cf5ede7cefb76bd0127

root@cks-worker:~# docker exec -it cf92415f4a8f7f1a233a3c80f49efb4e90d863f898149cf5ede7cefb76bd0127 bash
root@cf92415f4a8f:/# touch teste # Fue posible crear un archivo en /root
root@cf92415f4a8f:/# cat /proc/kcore
cat: /proc/kcore: Permission denied
root@cf92415f4a8f:/#

# Probando con docker-nginx. Vamos primero sin detached para ver la salida.
# Tendremos un permission denied, pero el contenedor aún continúa vivo.

root@cks-worker:~# docker run --security-opt apparmor=docker-nginx nginx
/docker-entrypoint.sh: No files found in /docker-entrypoint.d/, skipping configuration
/docker-entrypoint.sh: 13: cannot create /dev/null: Permission denied
2024/09/12 18:49:21 [notice] 1#1: using the "epoll" event method
2024/09/12 18:49:21 [notice] 1#1: nginx/1.27.1
2024/09/12 18:49:21 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2024/09/12 18:49:21 [notice] 1#1: OS: Linux 5.15.0-1067-gcp
2024/09/12 18:49:21 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/09/12 18:49:21 [notice] 1#1: start worker processes
2024/09/12 18:49:21 [notice] 1#1: start worker process 9
2024/09/12 18:49:21 [notice] 1#1: start worker process 10

# Vamos a ejecutar nuevamente, pero en modo detached y entrar en el contenedor para probar.
root@cks-worker:~# docker run --security-opt apparmor=docker-nginx -d nginx
c8d38a06828b6cfef268e044489f11fe528f7b85a820a3efc8a6915eff8d4b53

root@cks-worker:~# docker exec -it c8d38a06828b6cfef268e044489f11fe528f7b85a820a3efc8a6915eff8d4b53 bash

root@c8d38a06828b:~# touch teste
touch: cannot touch 'teste': Permission denied # No fue posible crear en /root
root@c8d38a06828b:~# touch /tmp/test # ni en tmp
touch: cannot touch '/tmp/test': Permission denied
root@c8d38a06828b:~# mkdir /teste
root@c8d38a06828b:~# cd /teste/
root@c8d38a06828b:/teste# touch teste # Pero en una carpeta que no fue bloqueada sí.
root@c8d38a06828b:/teste# cat /proc/kcore
cat: /proc/kcore: Permission denied

Entendiendo cómo funciona en Docker podemos pasar para Kubernetes. Si queremos utilizar esto en kubernetes, es necesario que:

  • El Container runtime soporte AppArmor.
  • Apparmor esté instalado en cada uno de los nodos.
  • El perfil que se vaya a utilizar esté disponible en cada uno de los nodos.
  • El perfil de AppArmor sea especificado a nivel de contenedor o a nivel de pod.

Vamos a probar.

root@cks-master:~# cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: nginx
name: nginx
spec:
# Nivel de pod.
# securityContext:
# appArmorProfile:
# type: Localhost
# localhostProfile: docker-nginx
containers:
- image: nginx
name: nginx
resources: {}
# Nivel de contenedor
securityContext:
appArmorProfile:
type: Localhost
localhostProfile: docker-nginx
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

root@cks-master:~# k apply -f nginx.yaml
pod/nginx created

root@cks-master:~# k exec -it pods/nginx -- bash
# Dentro del contenedor del pod
root@nginx:/# touch /root/teste
touch: cannot touch '/root/teste': Permission denied
root@nginx:/# touch /tmp/teste
touch: cannot touch '/tmp/teste': Permission denied
root@nginx:/#

Si se especifica un perfil que no existe el pod quedará con el estado de Blocked esperando que el perfil esté disponible.