Tu servicio arranca bien en staging. Luego Debian 13 en producción lo convierte en un ladrillo: “Permiso denegado”, archivos faltantes que definitivamente existen, sockets que se niegan a enlazarse,
fallos misteriosos que desaparecen en el instante en que paras AppArmor. Felicidades, te has topado con un control de seguridad que hace su trabajo—solo que aún no hace tu trabajo.
Este es el manual que uso cuando AppArmor encierra un demonio: identifica la denegación exacta, mapea al perfil que la impuso, añade la regla más estrecha posible y prueba la corrección.
Deshabilitar AppArmor no es “temporal”. Es cómo lo temporal se vuelve permanente.
Qué es AppArmor (y por qué Debian 13 lo hace sentir más estricto)
AppArmor es un módulo de seguridad de Linux que confina programas usando perfiles. Un perfil indica qué puede leer, escribir, ejecutar y qué capacidades puede usar un programa.
Si el programa intenta algo no permitido, el kernel lo deniega y registra la denegación. Eso es todo. Sin magia, sin “seguridad IA”, solo política y aplicación.
Si vienes de la escuela de “ejecutamos todo como root en un contenedor”, AppArmor es una llamada de atención. Los contenedores no son permisos. Root en un contenedor sigue
necesitando que el kernel del host diga “sí”. AppArmor forma parte de ese “sí/no”.
En Debian 13 típicamente te encontrarás con AppArmor cuando:
- Despliegas un servicio personalizado que toca archivos fuera de ubicaciones estándar.
- Añades un nuevo plugin, destino de backup, o ruta de clave TLS bajo
/srvo/mnt. - Cambias un demonio de TCP a sockets Unix (o al revés).
- Mueves logs a un nuevo directorio porque “queremos orden”. (Frases célebres finales.)
- Endureces systemd y cambias accidentalmente el entorno del proceso de una forma que el perfil no anticipó.
Modelo mental: AppArmor no está “bloqueando tu servicio”. Tu servicio está pidiendo algo que nunca se le concedió. Tu trabajo es conceder lo mínimo necesario y verificar
que no ampliaste el radio de impacto.
Una cita que vale la pena tener en la pared—porque es operaciones en una frase:
La esperanza no es una estrategia.
(atribución: idea parafraseada a menudo asociada con el Gen. Gordon R. Sullivan; usada ampliamente en ingeniería y operaciones)
Manual de diagnóstico rápido
Cuando un servicio falla y sospechas de AppArmor, no “pruebes cosas” al azar. Haz estas tres comprobaciones en orden. Es más rápido y deja evidencia.
1) Confirma que AppArmor está activo y en modo enforcing
Si AppArmor está desactivado globalmente, estás persiguiendo otro problema. Si está activado pero el perfil está en modo complain, verás logs pero no aplicación—también es otro problema.
2) Identifica el evento de denegación y el nombre del perfil
La línea de log de la denegación te dice el profile= y la ruta objetivo, la operación y los permisos solicitados. Esa línea es tu petición de cambio. Sin línea de denegación, no hay cambio de política.
3) Decide: arreglar el perfil, cambiar el comportamiento del servicio, o cambiar el layout de archivos
Si el servicio intenta leer un secreto desde un directorio de lectura pública, eso no es “un bug de AppArmor”. Es un problema de layout. Si el servicio necesita acceso, añade una regla estrecha.
Si no debería necesitar acceso, arregla el servicio.
Regla rápida: una denegación suele significar una regla faltante. Diez denegaciones en diez rutas diferentes suele significar que el binario ejecutado no es el que crees, o que el perfil es incorrecto.
Tareas prácticas: comandos, salidas, decisiones (12+)
Todo lo siguiente se puede ejecutar en un host Debian con AppArmor instalado. Sustituye nombres de servicio y rutas, pero conserva el método. El método es lo importante.
Task 1: Check if AppArmor is enabled in the kernel
cr0x@server:~$ cat /sys/module/apparmor/parameters/enabled
Y
Qué significa: Y significa que el LSM AppArmor está habilitado en el kernel.
Decisión: Si es N o el archivo no existe, para. No te está bloqueando AppArmor en este arranque.
Task 2: Verify AppArmor service health
cr0x@server:~$ systemctl status apparmor
● apparmor.service - Load AppArmor profiles
Loaded: loaded (/lib/systemd/system/apparmor.service; enabled; preset: enabled)
Active: active (exited) since Tue 2025-12-28 08:11:29 UTC; 2h 4min ago
Docs: man:apparmor(7)
Process: 512 ExecStart=/lib/apparmor/apparmor.systemd reload (code=exited, status=0/SUCCESS)
Qué significa: Se cargaron los perfiles correctamente.
Decisión: Si esto falla, arregla eso primero; de lo contrario las ediciones de perfil no se aplicarán.
Task 3: List loaded profiles and their mode
cr0x@server:~$ sudo aa-status
apparmor module is loaded.
46 profiles are loaded.
44 profiles are in enforce mode.
2 profiles are in complain mode.
0 profiles are in kill mode.
Qué significa: Tienes aplicación real.
Decisión: Si el perfil relevante está en modo complain, tu servicio aún puede funcionar mientras registra denegaciones; decide si quieres pasar a enforce tras arreglar reglas.
Task 4: Confirm whether a specific service’s process is confined
cr0x@server:~$ systemctl show -p MainPID myservice.service
MainPID=1842
cr0x@server:~$ sudo cat /proc/1842/attr/current
/usr/bin/myservice (enforce)
Qué significa: El proceso está confinado por el perfil /usr/bin/myservice en modo enforce.
Decisión: Edita el perfil que coincida con lo que ves en attr/current, no con lo que crees que debería usarse.
Task 5: Pull recent AppArmor denials from journald
cr0x@server:~$ sudo journalctl -k --since "30 min ago" | grep -i apparmor | tail -n 5
Dec 28 10:01:22 server kernel: audit: type=1400 audit(1766916082.123:311): apparmor="DENIED" operation="open" class="file" profile="/usr/bin/myservice" name="/etc/myservice/secret.key" pid=1842 comm="myservice" requested_mask="r" denied_mask="r" fsuid=998 ouid=0
Qué significa: Tu servicio intentó abrir un archivo para lectura y fue denegado.
Decisión: O permites lectura exactamente a ese archivo (o patrón de directorio), o mueves el secreto a una ruta ya permitida por la política. No adivines—usa la denegación.
Task 6: Use the audit log if journald is noisy or rotated
cr0x@server:~$ sudo grep -i "apparmor=\"DENIED\"" /var/log/audit/audit.log | tail -n 3
type=AVC msg=audit(1766916082.123:311): apparmor="DENIED" operation="open" class="file" profile="/usr/bin/myservice" name="/etc/myservice/secret.key" pid=1842 comm="myservice" requested_mask="r" denied_mask="r"
Qué significa: Misma denegación, desde el subsistema audit.
Decisión: Si no tienes auditd instalado, considera usarlo en servidores donde necesites registro de seguridad duradero; journald por sí solo puede ser suficiente, pero no siempre se conserva.
Task 7: Locate the profile file on disk
cr0x@server:~$ sudo grep -R "profile /usr/bin/myservice" -n /etc/apparmor.d | head -n 2
/etc/apparmor.d/usr.bin.myservice:12:profile /usr/bin/myservice flags=(attach_disconnected) {
Qué significa: El perfil vive en /etc/apparmor.d/usr.bin.myservice.
Decisión: Edita este archivo (o un include local) en lugar de inventar un nuevo nombre de perfil.
Task 8: Generate rules interactively with aa-logprof
cr0x@server:~$ sudo aa-logprof
Reading log entries from /var/log/audit/audit.log.
Updating AppArmor profiles in /etc/apparmor.d.
Profile: /usr/bin/myservice
Execute: /usr/bin/myservice
Severity: unknown
[(A)llow]/(D)eny/(I)gnore/(N)ew/(G)lob/(Q)uit
Qué significa: AppArmor analizó tu denegación y ofrece un cambio de política.
Decisión: Prefiere Allow solo cuando el acceso es legítimo y esperado. Si no estás seguro, elige Ignore, reproduce el problema con más logging y vuelve a revisarlo.
Task 9: Reload a single profile after editing
cr0x@server:~$ sudo apparmor_parser -r /etc/apparmor.d/usr.bin.myservice
Qué significa: No mostrar salida es normal en caso de éxito.
Decisión: Si ves errores de parseo, corrige la sintaxis antes de reiniciar el servicio. Un perfil roto puede dejar cargada una política antigua.
Task 10: Confirm the new rule is loaded (and not just edited)
cr0x@server:~$ sudo aa-status | grep -n "/usr/bin/myservice"
21: /usr/bin/myservice
Qué significa: El perfil está cargado. No prueba que la nueva regla esté en efecto, pero demuestra que el perfil existe y está activo.
Decisión: Ahora reinicia el servicio y verifica que la denegación cese.
Task 11: Restart the service and watch logs live
cr0x@server:~$ sudo systemctl restart myservice.service
cr0x@server:~$ sudo journalctl -u myservice.service -n 50 --no-pager
Dec 28 10:06:41 server myservice[2011]: Loaded TLS key from /etc/myservice/secret.key
Dec 28 10:06:41 server myservice[2011]: Listening on /run/myservice.sock
Dec 28 10:06:41 server systemd[1]: Started myservice.service - My Service.
Qué significa: El servicio ahora lee la clave y enlaza su socket.
Decisión: Mantén la regla. Si todavía falla, vuelve a la línea de denegación del kernel/audit—el log del servicio no es autoridad sobre AppArmor.
Task 12: Prove you didn’t introduce a flood of new denials
cr0x@server:~$ sudo journalctl -k --since "5 min ago" | grep -i "apparmor=\"DENIED\"" | tail -n 10
Qué significa: Idealmente: sin salida.
Decisión: Si aparecen nuevas denegaciones, arreglaste un camino pero el servicio tiene más necesidades. Itera con la misma disciplina: una denegación, una regla mínima.
Task 13: Temporarily switch a profile to complain mode (for diagnosis)
cr0x@server:~$ sudo aa-complain /usr/bin/myservice
Setting /usr/bin/myservice to complain mode.
Qué significa: El perfil registra pero no aplica. Esto puede desbloquear un incidente mientras reúnes evidencia.
Decisión: Usa el modo complain como paso diagnóstico, no como estilo de vida. Ponte un recordatorio para volver a enforce después de actualizar el perfil.
Task 14: Put it back into enforce mode
cr0x@server:~$ sudo aa-enforce /usr/bin/myservice
Setting /usr/bin/myservice to enforce mode.
Qué significa: La aplicación vuelve.
Decisión: Si el servicio se rompe otra vez, te faltó una regla Allow. Eso es esperado—ve a recolectar las nuevas denegaciones y sigue ajustando.
Task 15: Find “which file is missing” problems that are actually confinement
cr0x@server:~$ sudo strace -f -e trace=file -p 1842 2>&1 | head -n 8
openat(AT_FDCWD, "/etc/myservice/secret.key", O_RDONLY|O_CLOEXEC) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "/etc/ssl/certs/ca-certificates.crt", O_RDONLY|O_CLOEXEC) = 3
Qué significa: El kernel está denegando el acceso a archivos. Eso se alinea con denegaciones de AppArmor.
Decisión: Cuando los desarrolladores insistan “el archivo existe”, muéstrales la syscall que devuelve EACCES y la línea de audit. Termina el debate rápido.
Task 16: Validate the unit’s execution path is what the profile expects
cr0x@server:~$ systemctl cat myservice.service
# /etc/systemd/system/myservice.service
[Service]
ExecStart=/usr/local/bin/myservice --config /etc/myservice/config.yaml
Qué significa: El binario está en /usr/local/bin, no en /usr/bin.
Decisión: Si el perfil cargado es para /usr/bin/myservice pero el servicio ejecuta /usr/local/bin/myservice, estás editando el perfil equivocado. Arregla la coincidencia del perfil o la ruta.
Cómo leer denegaciones como un profesional
Una línea de denegación de AppArmor parece intimidante hasta que aprendes qué campos importan. Esto es en lo que debes enfocarte:
- profile= la política que tomó la decisión. Este es el archivo que editarás.
- operation= qué intentó el proceso: open, create, unlink, connect, bind, ptrace, mount, etc.
- name= la ruta objetivo (para operaciones de archivo) u nombre del objeto.
- requested_mask= lo que el proceso pidió, como
r(read),w(write),k(lock),m(memory map),x(execute). - denied_mask= lo que se negó (normalmente coincide con requested_mask en una denegación limpia).
- comm= nombre del comando; útil cuando hay wrappers o scripts de shell implicados.
- pid= te permite ligar con systemd, strace y el árbol de procesos.
La denegación no es una sugerencia. Es la evidencia exacta de lo que se bloqueó.
Tu proceso puede fallar por muchas razones; cambia AppArmor solo cuando el kernel indique que AppArmor lo denegó.
Chiste #1: Si “arreglas” AppArmor apagándolo, no arreglaste AppArmor—arreglaste tu conciencia al quitarlo.
Tipos de denegación que verás más en servicios reales
Lecturas de archivos: configs, certs, credenciales, archivos de plugin. Suelen arreglarse con una regla r estrecha.
Escrituras de archivos: logs, pidfiles, caches, bases de datos. Aquí es donde la gente se descuida. No permitas escrituras a directorios amplios “solo para que funcione”.
Sockets Unix: bind en /run, conectar a sockets de Docker/containerd, hablar con servicios del sistema. AppArmor puede restringir tanto la creación como la conexión.
Señales y ptrace: health checks, watchdogs, debuggers. Suelen aparecer durante respuesta a incidentes cuando alguien adjunta herramientas a un proceso confinado.
Permite lo que necesitas: ediciones de perfil que no conviertan todo en “permitir todo”
El cambio de perfil correcto es aburrido. Concede una capacidad, una ruta, una operación y nada más. El cambio equivocado es “/** rw,” que básicamente
compra una caja fuerte y la deja abierta porque el teclado es molesto.
Prefiere reglas de archivo estrechas sobre comodines de directorio
Si el servicio necesita exactamente una clave secreta:
cr0x@server:~$ sudo sed -n '1,80p' /etc/apparmor.d/usr.bin.myservice
#include <tunables/global>
profile /usr/bin/myservice flags=(attach_disconnected) {
#include <abstractions/base>
/usr/bin/myservice mr,
/etc/myservice/secret.key r,
}
Qué significa: El perfil permite lectura a un archivo. Eso es fácil de auditar y revertir.
Decisión: Elige la ruta más pequeña que coincida. Usa globs de directorio solo cuando el servicio realmente necesite múltiples archivos rotativos.
Haz que las escrituras sean intencionales: logs, estado, cache
Un demonio típico necesita:
- Config de solo lectura bajo
/etc - Estado escribible bajo
/var/lib - Logs escribibles bajo
/var/log(o stdout a journald) - Socket o pid de runtime bajo
/run
Alinea el layout del filesystem con esas expectativas. Si insistes en escribir estado en /etc o /opt, pelearás con AppArmor por siempre.
Y te lo merecerás.
Usa abstracciones, pero no te escondas detrás de ellas
Las abstracciones de AppArmor (como abstractions/base) pueden ser útiles. También facilitan permitir más de lo necesario por accidente.
Mi regla: incluye abstracciones cuando encajen claramente con el rol del servicio, luego añade reglas explícitas para las partes únicas.
No confundas “funciona” con “seguro”
Siempre puedes hacerlo funcionar añadiendo reglas amplias. Estás aquí porque gestionas sistemas de producción, y los sistemas de producción tienen enemigos: errores, atacantes y el tú del futuro.
AppArmor ayuda con los tres, pero solo si no lo saboteas.
Chiste #2: “Lo puse temporalmente en modo complain” es la versión de seguridad de “sostendré este cable en vivo por un segundo”.
Systemd, servicios y qué perfil se aplica realmente
La mayoría de incidentes AppArmor en entornos Debian no son “AppArmor es demasiado estricto.” Son “editamos el perfil equivocado” o “systemd ejecuta un binario distinto al que creemos.”
Systemd añade wrappers: archivos de entorno, scripts pre-start, usuarios dinámicos, directorios de runtime.
Conoce la cadena de ejecución
AppArmor usualmente se asocia por ruta ejecutable. Si tu unidad ejecuta un wrapper de shell que luego hace exec del binario real, puedes estar confinando la shell, no el demonio. O nada en absoluto.
La prueba siempre está en /proc/<pid>/attr/current.
Cuidado con /usr/local vs /usr/bin
El empaquetado de Debian suele poner binarios gestionados en /usr/bin. Las instalaciones hechas a mano prefieren /usr/local/bin.
Si tu perfil está nombrado para uno pero ejecutas el otro, perseguirás fantasmas.
El endurecimiento de systemd puede cambiar el síntoma
Opciones como ProtectSystem=, ReadOnlyPaths= y PrivateTmp= también pueden causar “permiso denegado”, pero esas no son denegaciones de AppArmor.
Por eso el diagnóstico rápido empieza con la línea de denegación del kernel. Necesitas al culpable correcto antes de mover herramientas.
Tres mini-historias corporativas desde las trincheras
Incidente #1: La suposición equivocada (“Es solo un problema de permisos del sistema de archivos”)
Una empresa mediana desplegó una actualización de Debian en una flota que ejecutaba un gateway API interno. Una región empezó a devolver 503 esporádicos.
El on-call hizo lo que la mayoría hacemos bajo presión: buscó lo obvio.
Los logs del gateway decían que no podía leer su clave TLS. El archivo existía, la propiedad parecía correcta, y un rápido sudo -u gateway cat /path/to/key funcionó.
Alguien concluyó que era una condición de carrera en el despliegue y re-ejecutó el trabajo de gestión de configuración.
El problema persistió. Naturalmente, añadieron más reintentos.
Finalmente un ingeniero con más calma revisó el log del kernel y encontró denegaciones de AppArmor para operation="open" en esa ruta de clave.
La clave se había movido de la ubicación antigua a un nuevo directorio que encajaba con sus convenciones ordenadas—pero no con el perfil de AppArmor.
El momento “aha” fue incómodo: todos los permisos Unix eran correctos. AppArmor era la capa que denegaba el acceso, y no le importaba que un cat manual funcionara
porque la prueba manual se realizó fuera del contexto confinado.
La solución fue una única regla que permitía lectura a la nueva ruta de la clave. La solución duradera fue cultural: no aceptes “permiso denegado” como diagnóstico solo de sistema de archivos
en un sistema con control de acceso obligatorio habilitado. Revisa la línea de denegación primero.
Incidente #2: La optimización que salió mal (consolidación de rutas de logs)
Otro equipo quiso “optimizar” el envío de logs. En lugar de journald, redirigieron varios servicios para escribir logs JSON en un directorio compartido en un disco rápido,
y luego los hacían tail con un agente. Parecía ordenado: un lugar para todos los logs, rotación coherente, dashboards felices.
En Debian 13, varios servicios empezaron a fallar durante el arranque con errores IO genéricos. Los ingenieros persiguieron rendimiento de disco, luego SELinux (no ejecutaban SELinux),
luego el sandboxing de systemd. Los fallos eran inconsistentes porque solo ciertos caminos de código escribían logs temprano en el arranque.
La causa raíz fueron perfiles AppArmor que permitían escribir a la ruta de logs estándar de cada servicio, pero no al nuevo directorio compartido.
La “optimización” había cambiado silenciosamente el destino de escritura para múltiples demonios. AppArmor hizo exactamente lo que debía: impedir que un servicio confinado escriba donde quiera.
La solución ingenua fue añadir acceso amplio de escritura al directorio compartido para cada servicio. Eso habría hecho que la consolidación funcionara—y también habría creado una superficie de manipulación de datos entre servicios.
Un servicio comprometido podría sobrescribir los logs de otro, que es un regalo encantador para un atacante que disfruta borrar rastros.
La solución correcta fue evitar directorios escribibles compartidos entre demonios no relacionados, o usar subdirectorios por servicio con reglas Allow estrechas y controles de propiedad.
El equipo terminó usando journald para la mayoría y solo externalizó JSON local para herramientas específicas.
Menos orden en disco. Mucho más orden en postura de riesgo.
Incidente #3: La práctica aburrida que salvó el día (recolección de denegaciones antes del cambio)
Un equipo fintech ejecutaba un procesador de pagos con un proceso de cambios estricto. Su enfoque era dolorosamente poco glamoroso: antes de activar la aplicación de AppArmor para un servicio,
lo ejecutaban en modo complain en producción por un día, recogían denegaciones, las revisaban y luego pasaban a enforce.
Durante una actualización de dependencias rutinaria, el servicio empezó a acceder a una nueva ruta de bundle CA debido a cambios en la pila TLS.
En modo complain, las denegaciones se registraron de inmediato. Nada se rompió todavía y no hubo impacto al cliente.
Añadieron una regla de lectura estrecha para esa ubicación del CA bundle, recargaron el perfil y volvieron a ejecutar pruebas de humo.
Solo entonces pusieron enforce. Cuando el cambio alcanzó la flota completa, fue un no-evento.
La práctica no fue ingeniosa. No requirió heroísmos. Requirió disciplina: trata la política como un artefacto de producción, pruébala como código y despliega como código.
El resultado fue previsibilidad, que es lo que realmente quieres a las 3 a.m.
Errores comunes: síntoma → causa raíz → solución
1) El servicio dice “archivo no encontrado”, pero el archivo existe
Síntoma: La app registra ENOENT o “archivo faltante”, pero puedes ver el archivo en disco.
Causa raíz: AppArmor deniega el open, y la aplicación mapea mal el error (o estás mirando la ruta equivocada por symlinks/chroot).
Solución: Confirma con journalctl -k o /var/log/audit/audit.log; permite la ruta exacta o arregla el servicio para leer desde una ubicación permitida.
2) Editaste un perfil y no pasó nada
Síntoma: Añades reglas, reinicias el servicio, sigue denegado.
Causa raíz: El archivo editado no es el perfil cargado, o el perfil no se recargó, o el proceso está ejecutando una ruta ejecutable distinta.
Solución: Revisa /proc/<pid>/attr/current. Recarga con apparmor_parser -r. Valida la ruta del binario en ExecStart de systemd.
3) “La solución” fue desactivar AppArmor y el incidente terminó (por ahora)
Síntoma: Apagar AppArmor hace que todo funcione.
Causa raíz: La política carece de accesos legítimos. Desactivar la aplicación quita por completo las barreras.
Solución: Usa modo complain brevemente si hace falta, recoge denegaciones, actualiza el perfil y vuelve a enforce.
4) El servicio no puede enlazar su socket Unix bajo /run
Síntoma: El arranque falla al crear o bindear /run/myservice.sock.
Causa raíz: El perfil no permite crear esa ruta de socket, o el directorio de runtime de systemd difiere del esperado.
Solución: Añade reglas para /run/myservice.sock con permisos correctos; asegúrate de que RuntimeDirectory= coincida con la ruta que permites.
5) Tras añadir un wildcard, el servicio funciona pero la revisión de seguridad bloquea el release
Síntoma: Un revisor rechaza el perfil por reglas amplias como /etc/** r o /** rw.
Causa raíz: Política demasiado amplia escrita bajo presión.
Solución: Reemplaza por reglas de archivos explícitas, reglas por directorio para rutas propias y capacidades mínimas. Reproduce denegaciones para encontrar necesidades específicas.
6) DNS, HTTP saliente o conexiones a BD fallan pero no aparecen denegaciones de archivos
Síntoma: Llamadas de red fallan o hacen timeout; los logs de la app son vagos.
Causa raíz: AppArmor puede restringir operaciones de red según el perfil y características; o el problema es sandboxing de systemd/firewall.
Solución: Busca denegaciones con operation="connect" o network. Si no hay denegaciones de AppArmor, inspecciona el hardening del unit de systemd y las reglas de firewall.
7) Ves denegaciones para un nombre de proceso que no reconoces
Síntoma: Las denegaciones referencian comm="(algo)" que no es tu servicio.
Causa raíz: Tu servicio hace exec de binarios auxiliares (curl, openssl, scripts), y el perfil restringe la ejecución.
Solución: Decide si el servicio debe ejecutar helpers. Si sí, permite binarios específicos con modos de ejecución correctos; si no, elimina ese comportamiento del servicio.
Listas de verificación / plan paso a paso
Checklist A: Cuando producción está caída y necesitas señal rápido
- Comprueba el confinamiento:
/proc/<pid>/attr/current. Si diceunconfined, deja de culpar a AppArmor. - Extrae denegaciones del kernel:
journalctl -kfiltrado porapparmor="DENIED". - Correlaciona la marca temporal con la falla del servicio en
journalctl -u. - Decide la regla Allow más pequeña legítima. Si no la puedes justificar, no la añadas.
- Recarga el perfil, reinicia el servicio, confirma que las denegaciones paran.
- Pon un temporizador para revertir cualquier uso de complain durante el incidente.
Checklist B: Bucle seguro de desarrollo de políticas (el que te mantiene empleado)
- Pon el perfil del servicio en modo complain en un host canario.
- Ejercita el servicio: arranque, estado estable, backups, rotación, upgrades, failover.
- Ejecuta
aa-logprofy revisa cada regla como si fuera un cambio de firewall. - Prefiere rutas de archivo explícitas; evita directorios compartidos escribibles.
- Recarga el perfil, cambia a enforce en el canario, vuelve a probar.
- Despliega a una porción pequeña, monitorea denegaciones y luego expande.
- Guarda cambios de perfil en tu sistema normal de cambios (Git, CI, revisiones). Trátalos como código.
Checklist C: Un layout sensato que reduce fricción con AppArmor
- Configs:
/etc/myservice/(solo lectura en tiempo de ejecución salvo casos deliberados) - Secretos:
/etc/myservice/o/var/lib/myservice/(solo lectura, propiedad estricta) - Estado:
/var/lib/myservice/(escribible) - Cache:
/var/cache/myservice/(escribible, descartable) - Logs: journald preferido; si no,
/var/log/myservice/por servicio - Sockets/pids:
/run/myservice/creados por systemdRuntimeDirectory=
Hechos interesantes y contexto histórico (breve y concreto)
- AppArmor nació como un producto comercial de Immunix; después entró en el ecosistema Linux principal gracias a la implicación de Novell.
- A diferencia del modelo basado en etiquetas de SELinux, AppArmor es basado en rutas: las políticas referencian rutas del sistema de archivos, lo que las hace legibles y también vulnerables a peculiaridades de rutas si eres descuidado.
- Los perfiles de AppArmor soportan “modo complain”, que registra violaciones sin aplicar—útil para construir políticas a partir del comportamiento observado.
- El framework Linux Security Modules (LSM) es lo que permite que AppArmor, SELinux y otros enganchen en las comprobaciones de permisos del kernel.
- Las políticas de AppArmor pueden usar “hats” (subperfiles) para cambiar temporalmente el confinamiento en partes de un programa, aunque esto es menos común en despliegues modernos de servicios.
- Los patrones de adopción de Debian históricamente difirieron de los de Ubuntu; Ubuntu hizo de AppArmor un pilar por defecto pronto, lo que influyó en herramientas y prácticas comunitarias.
- El auge de systemd cambió el flujo de trabajo de resolución de problemas: journald se convirtió en el lugar principal para ver denegaciones, no solo los archivos syslog clásicos.
- AppArmor puede restringir más que archivos: señales, interacciones dbus y ciertas características del kernel aparecen como denegaciones en incidentes reales.
Preguntas frecuentes
1) ¿Cómo sé si es AppArmor y no permisos Unix?
Busca una línea de denegación del kernel/audit con apparmor="DENIED". Si existe al mismo tiempo que la falla, es AppArmor.
Si no existe, probablemente trates con permisos del sistema de archivos, sandboxing de systemd o bugs de la aplicación.
2) ¿Debo usar modo complain en producción?
Sí, brevemente y de forma deliberada: el modo complain es bueno para reunir denegaciones sin romper tráfico.
Pero trátalo como una herramienta de ventana de cambios. Una vez las reglas sean correctas, vuelve a enforce.
3) ¿Por qué sudo -u myuser cat /path funciona, pero el servicio no puede leer el archivo?
Porque el servicio está confinado y tu prueba manual generalmente no lo está. Las decisiones de AppArmor dependen del contexto del proceso confinado, no solo del UID/GID.
Verifica con /proc/<pid>/attr/current.
4) ¿Cuál es la forma más segura de añadir acceso a secretos?
Permite lectura exactamente al archivo secreto (no a todo el directorio), y mantiene propiedad y modos estrictos.
Si necesitas rotación, permite el glob mínimo que coincida con el esquema de rotación (por ejemplo, patrón específico de basename).
5) ¿Puedo simplemente permitir /etc/** r para detener estos problemas?
Puedes, y “funcionará”, y también permitirá silenciosamente leer configs y secretos no relacionados.
Si un servicio se compromete, el acceso amplio facilita el robo de datos. Sé preciso en su lugar.
6) Mi servicio se ejecuta desde /usr/local/bin. ¿Por qué no aplica el perfil empaquetado?
La mayoría de perfiles se asocian a una ruta ejecutable. Si el perfil es para /usr/bin/myservice pero ejecutas /usr/local/bin/myservice,
no estás usando el perfil que crees. Ajusta la unidad a la ruta esperada o crea/modifica el perfil correcto para el binario real.
7) Arreglé una denegación y ahora veo otra. ¿Es normal?
Sí. Los servicios a menudo fallan rápido en la primera operación bloqueada. Una vez desbloquees esa, aparece la siguiente permisión faltante.
Itera hasta que arranque y el estado estable funcionen sin denegaciones en modo enforce.
8) ¿Cómo evito repetir esto en cada actualización?
Trata los perfiles como parte del servicio, no como algo secundario. Guarda cambios en control de versiones, pruébalos en CI cuando sea posible,
y ejecuta canarios en modo complain después de actualizaciones para recolectar nuevo comportamiento antes de aplicar en toda la flota.
9) ¿Es AppArmor “más débil” que SELinux?
Modelo distinto, compensaciones distintas. El enfoque basado en rutas de AppArmor suele ser más accesible y rápido de adoptar para confinamiento a nivel de servicio.
“Más débil” suele significar “menos implementado uniformemente y mantenido estrictamente”, no inherentemente incapaz.
Conclusión: siguientes pasos que puedes hacer hoy
Si Debian 13 está bloqueando tu servicio, no desactives AppArmor. Úsalo como herramienta de depuración y como cinturón de seguridad.
Encuentra la denegación. Identifica el perfil que aplica. Añade la regla mínima que coincida con el comportamiento legítimo. Recarga. Prueba con logs.
Pasos prácticos:
- Elige un servicio con fallos y captura sus líneas de denegación desde
journalctl -ko/var/log/audit/audit.log. - Verifica el confinamiento vía
/proc/<pid>/attr/current, luego localiza el archivo correspondiente bajo/etc/apparmor.d/. - Ejecuta
aa-logprof, acepta solo reglas que puedas explicar, recarga el perfil y vuelve a probar. - Una vez estable, asegúrate de que el perfil esté en modo enforce y monitorea nuevas denegaciones tras cambios.
El objetivo no es hacer que AppArmor deje de molestar. El objetivo es hacer que tu servicio se comporte de forma predecible bajo confinamiento—porque la previsibilidad es lo que mantiene los incidentes pequeños.