Kubernetes v1.37 aterriza el 26 de agosto de 2026 y con él SELinuxMount pasa a GA y activado por defecto. En concreto: si tus nodos ejecutan SELinux en modo enforcing y tienes dos pods con niveles SELinux distintos compartiendo un mismo PersistentVolumeClaim en un mismo nodo, uno de los dos deja de arrancar. No cambió ningún manifiesto. No cambió ninguna imagen. El kubelet simplemente reporta etiquetas SELinux en conflicto sobre el volumen y el pod se queda en ContainerCreating hasta que su vecino desaparece. Mi postura, antes que nada: este es el cambio correcto, y prefiero que te comas la rotura en un clúster de staging esta semana a que pongas seLinuxChangePolicy: Recursive en los workloads afectados y des el tema por resuelto.
kubectl get csidrivers -o custom-columns='NAME:.metadata.name,SELINUX_MOUNT:.spec.seLinuxMount'La mecánica merece diez segundos porque explica por qué aquí no hay término medio. El camino antiguo hacía que el runtime de contenedores recorriera el volumen y reetiquetara cada inodo al arrancar el pod, con un coste proporcional a cuántos ficheros tengas, y a pagar otra vez en cada reinicio. Quien haya visto un pod con un PVC gordo quedarse en ContainerCreating sobre un sistema de ficheros remoto probablemente ha pagado esa factura sin saber cómo se llamaba. El camino nuevo monta el volumen con una opción de contexto y deja que el kernel aplique una única etiqueta a todo el superbloque. Tiempo constante. Pero un superbloque lleva exactamente un contexto SELinux, así que montar el mismo sistema de ficheros dos veces con dos contextos distintos se rechaza a nivel de kernel. Red Hat lleva más de una década documentando ese comportamiento. Kubernetes no está decidiendo romperte nada aquí: está dejando de pedirle al kernel algo que el kernel nunca le iba a dar.
Y aquí viene lo que me parece interesante, que no es la fontanería de almacenamiento. El patrón que se rompe funcionaba solo porque el reetiquetado era fichero a fichero. El pod A reetiquetaba el volumen a su nivel, llegaba el pod B y sobrescribía las etiquetas con las suyas, y los dos seguían leyendo. Nadie diseñó eso. No está en ningún KEP, no está en el registro de decisiones de arquitectura de nadie, simplemente nunca se cayó. Si ejecutas PHP sobre Kubernetes con un deployment de php-fpm y un sidecar de backup o de envío de logs montando el mismo claim bajo otro contexto de seguridad, estás dependiendo de un efecto colateral que ha sobrevivido a la implementación que lo producía. Esta forma la conocemos de nuestro propio ecosistema: en todo código hay un rincón donde algo funciona y la respuesta honesta al porqué es que siempre ha funcionado.
El argumento más fuerte en contra de mi postura no es la nostalgia, son dos cosas concretas. Primera: los montajes con contexto rechazan los cambios de etiqueta fichero a fichero. Ejecuta chcon dentro de uno de esos volúmenes y obtienes Operation not supported, porque la etiqueta nunca se escribió en disco. Si alguna parte de tu workload gestiona sus propias etiquetas de fichero, Recursive no es una táctica dilatoria para ti, es la configuración correcta: ponla y deja de preocuparte. Segunda, y esta es la que me incomoda: la detección es ciega de una forma que nadie ha resuelto. Kubernetes v1.36 trae un selinux-warning-controller que puedes activar con --controllers=*,selinux-warning-controller, y emite selinux_warning_controller_selinux_volume_conflict con los dos pods conflictivos como labels. Combínalo con el contador del kubelet volume_manager_selinux_volume_context_mismatch_warnings_total y sabrás qué pods están en conflicto ahora mismo. El problema es el ahora mismo. Ni el controlador ni ningún scrape improvisado de la API ve la pareja que solo colisionará cuando el scheduler los coloque juntos el martes que viene.
Aun así me quedo con actualizar, por una razón que no tiene nada que ver con la latencia de arranque. El reetiquetado recursivo es lo que hizo explotable a CVE-2021-25741 con la forma que tuvo: engañas al kubelet para que exponga parte del sistema de ficheros del host, y el runtime, muy servicial, reetiqueta esos ficheros del host para que tu pod pueda leerlos. Con un montaje por contexto, la política simplemente deniega al pod. Esa es una propiedad de seguridad que devuelves cada vez que espolvoreas el opt-out sobre un workload que no lo necesitaba. Y hay una consecuencia fea: poner seLinuxChangePolicy: Recursive también hace que el warning controller deje de reportar ese pod, por diseño. Así que la métrica sobre la que montaste el despliegue se queda en silencio mientras el trabajo aplazado sigue exactamente donde estaba. Si sacas algo del alcance, ponle una anotación y un número de ticket, porque la observabilidad no se va a acordar por ti.
La mayoría vais a leer todo esto y no vais a hacer nada, correctamente, y conviene decirlo en voz alta. Si /sys/fs/selinux/enforce no existe en tus nodos, el kubelet se salta todo ese camino de código. Incluso con SELinux en enforcing, solo entran en juego los volúmenes cuyo objeto CSIDriver tenga .spec.seLinuxMount a true, y en el árbol esa lista es solo fc e iscsi. Los volúmenes emptyDir, configMap, secret y projected siguen reetiquetándose de forma recursiva, así que tu configuración y tus credenciales quedan intactas. El driver CSI de AWS EBS es un buen ejemplo de por qué adivinar es mala idea en ambas direcciones: su chart de Helm solo renderiza seLinuxMount: true cuando node.selinux está definido, así que una instalación por defecto no cambia nada para los volúmenes EBS. Ve a mirar tu propio fichero de values en lugar de fiarte de cualquiera de las dos mitades de esa frase.
Lo que sigo rumiando es que una auditoría puntual no puede demostrar que un clúster está a salvo, porque el fallo es función del scheduling, no de tu YAML. Puedes enumerar los conflictos de hoy con una sola llamada a kubectl y aun así recibir una alerta en noviembre, cuando un drenaje de nodo junte dos pods por primera vez. La política de admisión es la respuesta honesta, ya sea con MutatingAdmissionPolicy, un webhook o Kyverno acotado a los namespaces que de verdad comparten claims, pero esa es una política que tienes que escribir antes de saber qué workloads la necesitan. Así que, colegas: ¿cómo demostrarías tú, de verdad, que un conflicto dependiente del scheduler no puede ocurrir en tu clúster, sin recurrir a activar el feature gate en staging y esperar? No he encontrado una respuesta que me convenza, y me gustaría robarte la tuya.
Un punto de partida práctico si quieres un solo comando: lista tus drivers y mira cuáles se han apuntado. Todo lo demás se deduce de esa respuesta.
Comentarios
Aún no hay comentarios — escribe el primero.
Inicia la conversación
Sin cuenta ni contraseña — introduce tu correo y te enviamos un enlace de acceso de un solo uso. ¿Primera vez? Todo se configura automáticamente.
Tu valoración se aplicará automáticamente al iniciar sesión.
Revisa tu bandeja de entrada
Hemos enviado un enlace de acceso a …. Ábrelo en este dispositivo — esta pestaña te conectará automáticamente.
¿No llega nada? Mira en la carpeta de spam — y marca el correo como «No es spam» para que la próxima vez llegue directo.