AppArmor es un Linux Security Module (LSM) que se engancha en el kernel para imponer políticas de seguridad antes de que el kernel ejecute cualquier syscall sensible. A diferencia del modelo de permisos Unix clásico —donde el proceso hereda los privilegios del usuario que lo lanzó—, AppArmor opera a nivel de programa: define exactamente qué archivos puede leer o escribir un binario concreto, qué sockets puede abrir, qué capacidades puede usar, independientemente de si corre como root.
El mecanismo interno es un perfil por ejecutable: una lista de reglas compilada en bytecode y cargada en el kernel. Cuando un proceso intenta una operación, el LSM hook intercepta la syscall, busca el perfil asociado al ejecutable, evalúa si la operación está permitida, y deniega o registra según el modo activo. La compilación de perfiles la hace apparmor_parser, que transforma la sintaxis legible en /etc/apparmor.d/ a estructuras internas del kernel.
La diferencia fundamental con SELinux es el modelo de etiquetado. SELinux etiqueta todos los objetos del sistema (archivos, sockets, procesos) con contextos de seguridad, y las políticas expresan relaciones entre esos contextos —es más expresivo pero órdenes de magnitud más complejo de gestionar. AppArmor etiqueta solo los procesos (por su ruta de ejecutable), lo que hace los perfiles más legibles y mantenibles a costa de menor granularidad.
[otras familias] SELinux viene activo por defecto en RHEL/Fedora; AppArmor en Ubuntu y, desde Debian Buster, también disponible y funcional en Debian (aunque no todos los paquetes instalan perfiles por defecto).
Usas AppArmor activamente cuando un servicio empieza a fallar con Permission denied pero los permisos Unix son correctos, o cuando quieres aislar un proceso con privilegios elevados. El error de diagnóstico más frecuente es ignorarlo completamente: ves el EACCES, revisas ls -la, todo parece bien, y pierdes media hora hasta que alguien menciona AppArmor. Cuando tienes un servicio comprometido, un perfil en enforce es la diferencia entre un proceso confinado y un sistema entero comprometido.
Si configuras mal un perfil en modo enforce para un servicio crítico, ese servicio simplemente no arranca o falla en tiempo de ejecución de formas no obvias —nginx devuelve 502, MySQL no puede leer sus datafiles, sshd no puede escribir en /var/run. Por eso existe el modo complain: registra violaciones sin bloquear, lo que te permite construir el perfil observando el comportamiento real antes de enforcearlo.
# Verificar estado global de AppArmor y perfiles cargados
aa-status
# Salida relevante:
# apparmor module is loaded.
# 34 profiles are loaded.
# 32 profiles are in enforce mode.
# 2 profiles are in complain mode.
# Instalar herramientas de desarrollo de perfiles (no vienen por defecto)
apt install apparmor-utils apparmor-profiles apparmor-profiles-extra
# Ver qué perfil tiene asociado nginx ahora mismo
aa-status | grep nginx
# Supongamos que nginx no tiene perfil activo todavía.
# Vamos a crear uno desde cero para un binario custom: /usr/local/bin/reportgen
# aa-genprof lanza el binario, captura syscalls en modo complain, y genera el perfil
aa-genprof /usr/local/bin/reportgen
# Mientras aa-genprof espera, en otra terminal ejecutas el programa normalmente:
# /usr/local/bin/reportgen --input /var/data/sales.csv --output /tmp/report.pdf
# Vuelves y pulsas (S)can para que aa-genprof lea los logs y proponga reglas.
# Por cada acceso detectado, decides Allow/Deny/Glob/etc.
# Al terminar, guarda el perfil en /etc/apparmor.d/usr.local.bin.reportgen
# Revisar el perfil generado antes de activarlo
cat /etc/apparmor.d/usr.local.bin.reportgen
# El perfil recién creado ya está en enforce (aa-genprof lo carga al salir).
# Si necesitas más iteraciones, ponlo en complain primero:
aa-complain /usr/local/bin/reportgen
# Ejecutar el programa en producción real durante un rato, luego refinar:
aa-logprof
# aa-logprof lee los logs de violaciones y propone nuevas reglas para añadir al perfil.
# Mismo flujo interactivo: Accept/Deny/Glob para cada acceso no cubierto.
# Cuando el perfil ya cubre todos los accesos legítimos, pasar a enforce:
aa-enforce /usr/local/bin/reportgen
# Verificar que el perfil está activo en enforce
aa-status | grep reportgen
# Si un servicio systemd falla, los logs de AppArmor aparecen en el journal:
journalctl -xe | grep -i apparmor
# En sistemas con auditd instalado, también en:
grep apparmor /var/log/audit/audit.log | tail -20
# Una línea de log típica de violación tiene este aspecto:
# audit: type=1400 ... apparmor="DENIED" operation="open" \
# profile="/usr/local/bin/reportgen" name="/etc/ssl/certs/ca-certificates.crt" \
# pid=12345 comm="reportgen" requested_mask="r" denied_mask="r"
# "name" es el recurso bloqueado, "requested_mask" es lo que pedía el proceso.
# Con eso sabes exactamente qué regla añadir al perfil.
# Para añadir manualmente esa regla sin pasar por aa-logprof:
# Edita /etc/apparmor.d/usr.local.bin.reportgen y dentro del bloque {} añade:
# /etc/ssl/certs/ca-certificates.crt r,
# Luego recarga el perfil sin reiniciar nada:
apparmor_parser -r /etc/apparmor.d/usr.local.bin.reportgen
# Para deshabilitar temporalmente un perfil (útil en diagnóstico urgente):
aa-disable /usr/local/bin/reportgen
# Esto crea un symlink en /etc/apparmor.d/disable/ y recarga.
# Documenta siempre que lo haces y cuándo planeas re-activarlo.
# Para nginx, el paquete apparmor-profiles-extra incluye un perfil base.
# Copiarlo al directorio activo y cargarlo:
cp /usr/share/apparmor/extra-profiles/usr.sbin.nginx \
/etc/apparmor.d/usr.sbin.nginx
apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
aa-status | grep nginx
Lo que acaba de pasar línea a línea
aa-status es el punto de entrada obligatorio antes de tocar nada. Te da el inventario completo: cuántos perfiles hay cargados, cuáles están en enforce, cuáles en complain, y qué procesos están confinados ahora mismo por cada perfil. Sin eso vuelas a ciegas.
aa-genprof trabaja en dos fases que merecen atención. En la primera, carga el ejecutable en modo complain y espera. En la segunda, cuando pulsas (S)can, lee /var/log/syslog o el journal buscando líneas apparmor="ALLOWED" (en complain, los accesos se registran como permitidos pero se loguean como si fueran violaciones). El resultado es un perfil con la superficie mínima observada durante esa sesión —lo que significa que si reportgen tiene rutas de código que no se ejercitaron, el perfil no las cubrirá y fallarán en producción. Por eso el ciclo aa-complain → uso real → aa-logprof es más seguro para servicios complejos.
aa-logprof a diferencia de aa-genprof no lanza el proceso: solo lee logs existentes y propone enmiendas al perfil. Es la herramienta que usas en la segunda y tercera iteración, cuando el servicio ya lleva días corriendo en complain y has acumulado un log representativo de accesos reales. La distinción importa porque aa-genprof en sistemas de producción puede ser intrusivo.
La línea apparmor_parser -r es la forma correcta de recargar un perfil modificado. El flag -r (replace) sustituye el perfil en memoria del kernel sin necesidad de reiniciar el servicio ni el sistema. Cada vez que edites manualmente un perfil en /etc/apparmor.d/, necesitas ese comando para que el cambio tenga efecto —el kernel no monitorea el directorio automáticamente.
El log de violación que aparece comentado tiene todos los campos necesarios para escribir la regla correcta: profile te dice qué perfil está involucrado, name es la ruta exacta del recurso, requested_mask es la operación (r para read, w para write, x para execute, k para lock). Con esos tres campos, la regla a añadir es prácticamente mecánica: <name> <requested_mask>,.
aa-disable crea un symlink en /etc/apparmor.d/disable/ que apparmor_parser interpreta como “no cargar este perfil”. Es reversible con aa-enable, pero en producción es una señal de alarma: si deshabilitas un perfil porque algo falla, estás eliminando la contención, no solucionando el problema. El flujo correcto es ponerlo en complain, identificar la regla faltante en los logs, añadirla, y volver a enforce.
El perfil de nginx que viene en apparmor-profiles-extra cubre las rutas estándar de Debian (/etc/nginx/, /var/log/nginx/, /var/www/), pero si tienes root configurado fuera de esas rutas —algo habitual en setups con múltiples virtualhost en /srv/ o /data/—, el perfil bloqueará las lecturas de archivos estáticos y verás 403 o 502 sin ninguna pista en los logs de nginx. El primer aa-logprof después de activarlo casi siempre produce una lista de rutas adicionales que necesitas añadir.