Fase 1.3
Rocky Linux 9 · SELinux

SELinux · Mandatory Access Control

La diferencia más grande entre Rocky y Ubuntu. SELinux pone una segunda capa de control de acceso que ni root puede saltear. En este proyecto nunca se desactiva. Se configura para que conviva con k3s.

¿Por qué no desactivarlo como hace todo el mundo?

Sin SELinux, un proceso que corre como root tiene acceso a todo. SELinux agrega MAC: cada proceso, archivo y puerto tiene un contexto de seguridad, y el kernel aplica reglas que ni root puede eludir. Así de simple.

DAC Permisos POSIX
  • Usuario, grupo, otros (rwx)
  • Root puede todo
  • Un proceso comprometido como root = sistema comprometido
  • No hay control sobre qué puede hacer un proceso con lo que lee
MAC SELinux
  • Contextos de seguridad en cada objeto del sistema
  • Ni root puede eludir las reglas
  • Un proceso comprometido solo accede a lo que su política permite
  • Cada transición entre contextos está controlada

Caso concreto: si alguien explota Grafana y escala a root, sin SELinux puede leer /etc/shadow. Con enforcing, el proceso tiene contexto container_t y el kernel le niega el acceso a archivos shadow_t, aunque se haya escapado a root. La política gana.

Los tres modos

Enforcing bloquea. Permissive solo registra. Disabled apaga todo. SELinux tiene tres estados operativos. Este proyecto usa exclusivamente enforcing. Es el único modo que tiene sentido en un entorno que se presenta como hardening de producción.

Enforcing
Activo

Bloquea y registra. Todo lo que no está en la política se niega y se loguea. Es el modo que usamos: ni siquiera root puede saltearlo.

Permissive
Solo registro

No bloquea nada, pero registra lo que habría bloqueado. Sirve para debuggear políticas sin romper el sistema.

Disabled
Desactivado

Apagado del todo. Así viene Ubuntu por default. No lo usamos: si lo desactivás, perdés toda la capa de MAC.

No poner SELinux en modo Permissive o Disabled. Si se hace, los problemas de políticas que aparezcan en Oracle Cloud van a ser una sorpresa. Lo correcto es mantener enforcing desde el día 1 y resolver las denegaciones con booleanos o contextos.
03

El rol en Ansible

SELinux en enforcing y booleanos para k3s activados. Cero configuración manual. Ansible lo aplica.

Defaults

roles/selinux/defaults/main.yml
selinux_mode: enforcing

selinux_booleans:
  - name: container_manage_cgroup
    state: true
  - name: virt_use_nfs
    state: true

Tasks

roles/selinux/tasks/main.yml
- name: Instalar policycoreutils-python-utils (semanage, getsebool)
  ansible.builtin.dnf:
    name: policycoreutils-python-utils
    state: present

- name: Verificar que SELinux esté en modo enforcing
  ansible.builtin.selinux:
    policy: targeted
    state: "{{ selinux_mode }}"

- name: Configurar booleanos de SELinux para k3s y servicios
  ansible.posix.seboolean:
    name: "{{ item.name }}"
    state: "{{ item.state }}"
    persistent: true
  loop: "{{ selinux_booleans }}"

- name: Mostrar estado de SELinux
  ansible.builtin.command:
    cmd: sestatus
  changed_when: false
  register: selinux_status

- name: Debug — estado de SELinux
  ansible.builtin.debug:
    var: selinux_status.stdout_lines

La tarea ansible.builtin.selinux es el core del rol. Asegura que la política targeted esté activa en modo enforcing. La política targeted es la estándar en RHEL — protege los servicios más comunes sin requerir reglas para cada proceso del sistema.

04

Booleanos que k3s necesita

Los booleanos son interruptores de la política. Modifican lo que SELinux permite o bloquea para funcionalidades específicas.

container_manage_cgroup
Permitir que containerd gestione cgroups. Sin esto, k3s no puede limitar recursos de los pods.
k3s no arranca si está en false.
virt_use_nfs
Permitir acceso a volúmenes NFS desde procesos de virtualización. Necesario para PVCs con backend NFS.
PVCs con NFS se quedan en Pending.

Para verificar los booleanos manualmente después de correr el playbook:

verificar booleanos
$ getsebool -a | grep -E 'container_manage_cgroup|virt_use_nfs'
container_manage_cgroup --> on
virt_use_nfs --> on
05

Contextos que vas a ver seguido

Cada archivo, proceso y puerto tiene un contexto SELinux. Estos son los tres que aparecen una y otra vez en el lab.

container_file_t
Archivos que los contenedores leen/escriben: volúmenes, config maps, sockets.
Ejemplo: Pod volumes, ConfigMap mounts
container_runtime_exec_t
Binarios del runtime: containerd, runc, cri-o.
Ejemplo: /usr/bin/containerd
ssh_port_t
Puerto SSH. Se actualiza con semanage al mover de 22 a 2222.
Ejemplo: tcp/2222

Para ver el contexto de un archivo o directorio, se usa ls -Z. Para cambiarlo temporalmente, chcon. Para hacer el cambio persistente, semanage fcontext + restorecon.

contextos
# Ver contexto de un archivo
$ ls -Z /etc/ssh/sshd_config
system_u:object_r:etc_t:s0 /etc/ssh/sshd_config
# Ver contexto de un proceso
$ ps -eZ | grep containerd
system_u:system_r:container_runtime_t:s0 890 ? 00:12 containerd
06

Chequear que SELinux ande

Playbook terminado → confirmar que SELinux está enforcing y los booleanos activos.

verify-selinux.sh
# Modo actual
$ getenforce
Enforcing
# Estado completo
$ sestatus
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
Current mode: enforcing
Policy from config file: targeted
# Booleanos de k3s activos
$ getsebool container_manage_cgroup virt_use_nfs
container_manage_cgroup --> on
virt_use_nfs --> on
# Denegaciones recientes (debería estar vacío si todo funciona)
$ ausearch -m avc -ts recent 2>/dev/null | head -5
(sin output — sin denegaciones recientes)
Si ausearch muestra denegaciones (AVC), usar audit2why < /var/log/audit/audit.log para obtener una explicación legible de qué booleano o contexto falta.
07

Cuando SELinux rompe algo

Casi siempre es un booleano en false o un contexto incorrecto. Estos son los errores que van a aparecer con k3s.

k3s no inicia: permission denied en containerd
Causa: container_manage_cgroup está en false.
Solución: setsebool -P container_manage_cgroup on
Un pod no puede escribir en un PersistentVolume
Causa: El volumen no tiene contexto container_file_t.
Solución: chcon -Rt container_file_t /ruta/al/volumen
SSH no responde después de cambiar el puerto a 2222
Causa: SELinux no conoce el puerto 2222 como ssh_port_t.
Solución: semanage port -a -t ssh_port_t -p tcp 2222
Algo falla sin error claro: denegación silenciosa
Causa: SELinux bloquea en enforcing y no siempre grita en stdout.
Solución: ausearch -m avc -ts recent | audit2why
08

SELinux vs AppArmor

Ubuntu trae AppArmor, Rocky trae SELinux. No son lo mismo y no se reemplazan. Tener experiencia con ambos marca diferencia.

RHEL/Rocky SELinux
  • Etiquetas en cada objeto del filesystem (inodo)
  • Política centralizada (targeted)
  • Booleanos para ajustar sin tocar la política
  • Viene enforcing por defecto en Rocky 9
  • Mayor granularidad, mayor complejidad
Ubuntu AppArmor
  • Perfiles por aplicación basados en path
  • Modo complain/deny por perfil individual
  • Más simple de configurar
  • Menos granularidad: basa las reglas en rutas de archivo
  • Viene con perfiles más permisivos por defecto

¿Por qué el proyecto usa SELinux? Porque es el estándar en entornos RHEL, que dominan el mercado enterprise. Tener un proyecto con SELinux enforcing demuestra que se entiende la capa de seguridad que los entornos corporativos exigen.

Siguiente: Firewalld

Con SELinux protegiendo el acceso a nivel de kernel, la siguiente capa es firewalld, el firewall de red que controla qué puertos y servicios son accesibles desde fuera de la VM.

Firewalld & Seguridad de Red