LSM: el framework de politicas de seguridad
El framework de ganchos con el que el kernel permite conectar politicas de seguridad obligatorias: como se colocan los hooks security tras la comprobacion DAC, como un modulo registra sus funciones, la diferencia entre el control de acceso discrecional y el obligatorio, SELinux con sus etiquetas y type enforcement, AppArmor basado en rutas, Landlock para procesos sin privilegios y los BPF-LSM que escriben politica como programas eBPF.
Los permisos clásicos de Unix son discrecionales: el dueño de un fichero decide quién lo lee, y root puede saltarse cualquier regla. Ese modelo, suficiente durante décadas, se rompe cuando el atacante es el proceso privilegiado: si un servidor web comprometido corre como root, la discreción del dueño no protege de nada. Hace falta una capa por encima, una política obligatoria que ni el dueño ni root puedan eludir, escrita por el administrador del sistema y aplicada por el kernel en cada operación sensible. El framework que hace posible enchufar esa capa se llama LSM, Linux Security Modules, y es la infraestructura sobre la que se construyen SELinux, AppArmor, Landlock y las políticas escritas en eBPF. No es una política: es el lugar donde las políticas se conectan.
- Entender LSM como un framework de ganchos, no como una política concreta.
- Situar cada hook
security_*tras la comprobación discrecional y antes de la acción. - Distinguir el control de acceso discrecional (DAC) del obligatorio (MAC).
- Comparar SELinux, AppArmor, Landlock y los BPF-LSM como implementaciones sobre el mismo framework.
Un gancho en cada punto sensible
La idea de LSM es quirúrgica. En cada punto del kernel donde ocurre algo relevante para la seguridad —abrir un fichero, ejecutar un binario, crear un socket, cargar un módulo— hay una llamada a una función security_*. Esa función se ejecuta después de la comprobación de permisos clásica (DAC) y antes de realizar la acción, y consulta a los módulos de seguridad registrados si permiten la operación. Si alguno la niega, la syscall falla con EACCES aunque los permisos Unix la autorizasen:
/* security/security.c: el gancho que se ejecuta en cada execve */
int security_bprm_check(struct linux_binprm *bprm)
{
int ret;
ret = call_int_hook(bprm_check_security, bprm);
if (ret)
return ret;
return ima_bprm_check(bprm);
}
call_int_hook recorre la lista de módulos que registraron ese gancho y devuelve el primer rechazo. En los kernels actuales esa lista se implementa con static calls para que el coste sea casi nulo cuando ningún módulo usa el hook. Un módulo de seguridad no es más que un conjunto de estas funciones registradas:
static struct security_hook_list mis_ganchos[] __ro_after_init = {
LSM_HOOK_INIT(bprm_check_security, mi_bprm_check),
LSM_HOOK_INIT(file_open, mi_file_open),
LSM_HOOK_INIT(socket_connect, mi_socket_connect),
};
static const struct lsm_id mi_lsmid = { /* identidad del modulo (6.4+) */
.name = "milsm",
.id = LSM_ID_MILSM,
};
static int __init mi_lsm_init(void)
{
security_add_hooks(mis_ganchos, ARRAY_SIZE(mis_ganchos), &mi_lsmid);
pr_info("milsm: politica activa\n");
return 0;
}
DEFINE_LSM(milsm) = {
.name = "milsm",
.init = mi_lsm_init,
};
DAC frente a MAC
La distinción que LSM introduce es la columna vertebral de toda la seguridad de contenedores y servidores. El control discrecional (DAC) lo decide el dueño del recurso: son los bits rwx, los ACL, el chmod. Se llama discrecional porque queda a discreción del propietario, y root lo sortea siempre. El control obligatorio (MAC) lo decide una política central que el administrador define y que nadie —ni el dueño, ni root— puede desobedecer. Un proceso etiquetado para servir páginas web no podrá leer /etc/shadow aunque corra como root y los permisos del fichero lo permitieran, porque la política MAC no lo contempla. DAC pregunta quién eres; MAC pregunta qué se te ha autorizado a hacer, con independencia de quién seas.
flowchart TD
S[syscall open sobre etc shadow] --> D{comprobacion DAC permisos unix}
D -->|denegado| F1[EACCES]
D -->|permitido| H[security_file_open hook LSM]
H --> M{la politica MAC lo autoriza}
M -->|no| F2[EACCES aunque root]
M -->|si| OK[se abre el fichero]Las grandes implementaciones
Sobre el mismo framework conviven filosofías muy distintas. SELinux, nacido en la NSA, etiqueta todo —cada proceso, fichero, socket y puerto lleva un contexto de seguridad— y aplica type enforcement: una matriz de política define qué tipo de proceso puede tocar qué tipo de objeto. Es exhaustivo y potente, pero su política es densa:
$ ls -Z /etc/shadow
system_u:object_r:shadow_t:s0 /etc/shadow
$ ps -eZ | grep nginx
system_u:system_r:httpd_t:s0 ... nginx
# httpd_t no tiene regla para leer shadow_t: denegado, corra como corra
AppArmor, por contraste, razona sobre rutas en vez de etiquetas: un perfil enumera qué ficheros puede tocar cada programa. Es más sencillo de escribir y auditar, a costa de ser menos hermético cuando hay enlaces o remontajes:
# /etc/apparmor.d/usr.bin.miapp
profile miapp /usr/bin/miapp {
/etc/miapp/** r, # solo lectura de su config
/var/log/miapp.log w, # escritura de su log
deny /etc/shadow r, # jamas las credenciales
}
Landlock, incorporado en 5.13, invierte el modelo: no lo configura el administrador para todo el sistema, sino que un proceso sin privilegios se restringe a sí mismo, como hace seccomp con las syscalls pero aplicado al acceso a ficheros. Es la pieza que permite a una aplicación normal sandboxearse sola. Y los BPF-LSM (5.7) cierran el círculo con el nivel 50: permiten adjuntar programas eBPF a los ganchos LSM, escribiendo política de seguridad como código verificado y cargable en caliente, sin recompilar el kernel ni escribir un módulo:
SEC("lsm/file_open")
int BPF_PROG(bloquea_shadow, struct file *file)
{
/* denegar cualquier apertura de un fichero marcado como secreto */
if (es_secreto(file))
return -EPERM; /* un valor no nulo veta la operacion */
return 0;
}
SELinux (etiquetas)
MAC por type enforcement: todo lleva un contexto y una matriz define qué tipo toca qué tipo. Máxima cobertura y rigor, política compleja de escribir y depurar.
AppArmor (rutas)
MAC por perfiles de ruta: cada programa declara qué ficheros puede tocar. Sencillo de leer y mantener, algo menos hermético que las etiquetas.
BPF-LSM (eBPF)
Política como programa eBPF adjunto a los hooks, verificada y cargable en caliente. Flexibilidad total sin módulo ni recompilar, con la potencia del verificador detrás.
Apilar módulos
Históricamente solo podía haber un módulo “mayor” activo, pero el kernel moderno permite apilar varios: la política efectiva es la conjunción de todos —basta que uno niegue para vetar—. El orden y la selección se fijan en el arranque con el parámetro lsm=, y los pequeños módulos como yama (restringe ptrace), lockdown (limita al propio root frente al kernel) o capability se combinan con uno grande:
# orden de invocacion de los modulos, del kernel al arranque
lsm=lockdown,capability,yama,landlock,bpf,selinux
Cada gancho invoca los módulos en ese orden y el primero que rechaza corta la cadena. Así conviven la comprobación de capabilities (que también es un LSM), un confinamiento de ptrace, tu política eBPF y SELinux, sumando defensas en lugar de excluirse.
Puede sorprender, pero la comprobación de capabilities de la lección anterior está implementada como un módulo LSM llamado capability, siempre presente y primero en la cadena. Los ganchos security_capable y compañía no son un caso especial del kernel: son el LSM base sobre el que los demás se apilan. El framework es tan general que el propio modelo de capabilities es solo una política más conectada a él.
Hay una decisión de diseño en LSM que trasciende la ingeniería y toca la filosofía política de cómo se construye un sistema operativo que ha de servir a mundos irreconciliables. Cuando en los años dos mil surgió la necesidad de un control de acceso obligatorio en Linux, había varias propuestas rivales —SELinux desde la NSA, con su rigor de etiquetas; AppArmor, más pragmático y legible; otras más— y cada bando defendía que la suya era la forma correcta de asegurar un sistema. Linus Torvalds tomó entonces una decisión que define el carácter del proyecto: el kernel no elegiría ninguna. En vez de consagrar una política de seguridad en el corazón del sistema e imponerla a todos, el kernel ofrecería únicamente los ganchos —los puntos donde una política puede conectarse— y dejaría que cada quien enchufara la suya. Esta abstención es profunda. El kernel se declara deliberadamente agnóstico sobre qué significa seguridad: no sabe, ni quiere saber, si es correcto que un servidor web lea las credenciales del sistema; solo garantiza que, sea cual sea la respuesta que tu política dé, habrá un punto exacto donde imponerla sin fisuras. LSM no es un sistema de seguridad; es un meta-sistema, un armazón vacío que espera ser llenado con la moral que cada organización elija. La NSA puede desplegar su desconfianza exhaustiva de etiquetas, una distribución de escritorio puede preferir la legibilidad de las rutas, un desarrollador puede escribir su propia política en eBPF esa misma tarde, y todos usan el mismo kernel sin que ninguno imponga su visión a los demás. Esta es la misma humildad epistémica que atraviesa todo el nivel, elevada a principio arquitectónico: igual que seccomp reconoce que no puede enumerar todos los ataques y por eso invierte la carga hacia la lista blanca, LSM reconoce que no existe una única definición correcta de seguridad y por eso se niega a codificar ninguna, ofreciendo en su lugar el sustrato neutral donde cada definición pueda encarnarse. El kernel más usado del planeta protege sistemas incompatibles entre sí precisamente porque tuvo la sabiduría de no decidir qué es proteger, y limitarse a construir, con precisión de cirujano, el lugar donde esa decisión ajena se aplica.
- Ejecuta
sestatusoaa-statussegún tu distribución y averigua qué LSM gobierna tu máquina y en qué modo (enforcingopermissive). - Lee el contexto de seguridad de tres ficheros con
ls -Zy de tu shell conid -Z, e identifica el tipo o perfil que los confina. - Provoca una denegación —intenta que un proceso confinado toque algo prohibido— y localiza el registro en
ausearch -m avco endmesg. - Explica en tres líneas por qué una política MAC puede impedir que un proceso root lea un fichero que los permisos Unix le autorizan.
- Argumenta qué ventaja aporta escribir una política como BPF-LSM frente a un módulo LSM tradicional, conectando esta lección con el verificador del nivel 50.