Cerrado del /proc a usuarios no administrador -- Seguridad por defecto para el acceso al /proc


#1

1. Introducción

El siguiente artículo presenta una característica de seguridad en servidores recientemente añadida a #CoreAdmin, y que cierra el acceso a la carpeta especial linux /proc. Tras esta configuración, el acceso queda limitado al usuario administrador, y a todos los usuarios dentro del grupo procaccess.

A continuación te explicamos todos los detalles.

2. ¿Por qué es importante cerrar y limitar el acceso al /proc?

La carpeta especial /proc, incluye muchísima información sensible y que puede ser utilizada por un atacante para explorar, escalar y dirigir un ataque mucho mejor.

El directorio no solo proporciona información de otros procesos, también muestra información de mapeo de memoria, librerías, puntos de montaje, información de la memoria, cpu, tipo, juego de instrucciones, gestión de interrupciones, y un largo listado de elementos informativos valiosísimos para un administrador, pero peligrosos para que estén disponibles para un atacante.

Cerrar el /proc no hace invulnerable el sistema, pero dificulta el proceso de ataque.

3. Cuándo se activa la característica de protección

Core-Admin propondrá automáticamente la activación del cerrado del /proc, cuando detecta una serie de factores que así lo aconseja. Este listado puede ir variando. Actualmente se activa cuando se detectan las siguientes aplicaciones desplegadas:

  • Webhosting Management (Gestor de Alojamientos)
  • Samba Admin (Administrador Samba)
  • Mail Admin (Gestor de correo)
  • Docker Manager (Gestor docker)
  • Discourse Manager (Gestor Discourse)
  • OpenVpn Manager (Gestor OpenVPN)
  • Shared Ftp Manager (Gestor de FTP compartido)

También se activa cuando detecta algunos procesos como:

  • Procesos Ganeti
  • Virtualización kvm/qemu
  • Servidores Apache2, Turbulence, PowerDNS

El sistema también realiza varias comprobaciones para evitar activarse para aquellos casos donde no hay posibilidad de reconfiguración automática para hacer que el software afectado pueda seguir funcionando tras la configuración de cierre. El listado incluye:

  • Procesos pacemaker (pacemakerd, crmd).
  • Nodos que alojan contenedores LXC (incluidos nodos Proxmox). Se detectan por la presencia de definiciones de contenedores en /etc/pve/lxc/<vmid>.conf o /var/lib/lxc/<nombre>/config, con independencia de que los contenedores estén arrancados o parados en ese momento.

En estos casos, además de no proponerse la activación, el comprobador rechaza activamente la característica aunque se marque a mano, informando del motivo. Esto es así porque no hay ninguna configuración (ni siquiera añadir usuarios al grupo procaccess) que permita que el software afectado siga funcionando con el /proc cerrado.

4. Qué componente activa esta característica y cómo configurarlo

Toda la responsabilidad para configurar, desactivar y configurar usuarios autorizados para acceder al /proc la realiza el comprobador users_checker (Checker).

  1. Para acceder a su configuración, sigue las siguientes indicaciones. Primero ir a la ficha de la máquina, luego acciones -> mostrar comprobadores y luego:

5. Cómo se realiza el cierre del acceso al /proc

Se usa la configuración estándar de permisos del sistema de ficheros de Linux, junto con un grupo (procaccess) usado para autorizar usuarios, a parte del root, para poder acceder al /proc.

Se puede ver fácilmente la configuración activa ejecutando:

>> ls -la -tr -d /proc
dr-xr-x--- 167 root procaccess 0 abr 14 16:02 /proc

Como se puede observar, la configuración, en esencia, radica en:

  1. Se elimina el acceso a usuarios others (otros).
  2. Se configura un grupo (procaccess) para permitir acceso a sus miembros.
  3. El root sigue siendo propietario, y por tanto, conserva los accesos.

6. Listado de fallos conocidos

A continuación se muestra un listado de fallos conocidos de aplicaciones que necesitan acceso al proc.

Salvo los dos últimos (apertium y contenedores LXC), el problema se resuelve añadiendo el usuario de ejecución al grupo procaccess ya sea por línea de comando, usando el propio configurador del comprobador (en el campo Allowed proc access users, indicado en las secciones anteriores), y también usando la aplicación de Usuarios de sistema de Core-Admin.

  1. Fallo conocido de aplicaciones usando npm cuando no tiene acceso al /proc:

    >> npm list
    internal/modules/cjs/loader.js:583
        throw err;
        ^
    
    Error: Cannot find module 'semver'
        at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
        at Function.Module._load (internal/modules/cjs/loader.js:507:25)
        at Module.require (internal/modules/cjs/loader.js:637:17)
        at require (internal/modules/cjs/helpers.js:22:18)
        at Object.<anonymous> (/usr/share/npm/lib/utils/unsupported.js:2:14)
        at Module._compile (internal/modules/cjs/loader.js:689:30)
        at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
        at Module.load (internal/modules/cjs/loader.js:599:32)
        at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
        at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    
  2. Fallo conocido para aplicaciones basadas en java cuando no pueden acceder al /proc:

    >> java --version
    java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory
    
  3. Fallo conocido para aplicaciones usando apertium:

    Apertium (https://apertium.org/) accede al /proc/meminfo durante su ejecución. No parece haber manera de desactivarlo. La aplicación deja de funcionar si no tiene acceso al /proc. La única solución es desactivar la característica de protección del cerrado del /proc.

    >> echo "<p>Hola, me gustaría pedir una edición</p>" | LANG=es_ES.utf8 strace /usr/local/bin/apertium es-ca -u -f html 2>&1 | grep /proc
    open(" **/proc/meminfo** ", O_RDONLY|O_CLOEXEC) = -1  **EACCES (Permission denied)**
    
  4. Fallo conocido para yarn cuando no tiene acceso al /proc:

    Yarn necesita acceso al /proc/meminfo y otros archivos para revisar el estado de la memoria al ejecutar. Si no lo tiene, genera el siguiente error:

    >> yarn --help
    Error: EACCES: permission denied, uv_resident_set_memory
        at process.memoryUsage (internal/process/per_thread.js:136:5)
        at ConsoleReporter.checkPeakMemory (/usr/lib/node_modules/yarn/lib/cli.js:33565:40)
        at ConsoleReporter.initPeakMemoryCounter (/usr/lib/node_modules/yarn/lib/cli.js:33556:10)
        at /usr/lib/node_modules/yarn/lib/cli.js:92291:14
    
  5. Fallo conocido para SASS cuando no tiene acceso al /proc:

    La herramienta de preparación de ficheros css SASS requiere acceder al /proc para obtener información del sistema como parte de su proceso. Si no lo tiene, puede generar errores similares al siguiente:

    ==> ./discourse-3.1.1/log/production.log <==
    Rendered layout layouts/application.html.erb (Duration: 1138.0ms | Allocations: 268897)
    Completed 500 Internal Server Error in 2624ms (ActiveRecord: 0.0ms | Allocations: 560880)
    ActionView::Template::Error (end of file reached)
    
     ==> ./discourse-3.1.1/log/puma.err.log <==
    /var/discourse/comunidad.example.org/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/sass-embedded-1.64.1-x86_64-linux-gnu/lib/sass/embedded/connection.rb:28: warning: ../../runtime/vm/os_thread.cc: 59: error: 
    Failed to retrieve stack bounds
    
  6. Máquinas que alojan contenedores LXC no privilegiados (unprivileged: 1): los contenedores no arrancarán si se activa la limitación de proc, fallando con el siguiente error:

    root@server:~# lxc-start -n 121 -F 
    lxc-start: 121: ../src/lxc/utils.c: get_ns_uid: 562 Permission denied - Failed to open uid_map
    lxc-start: 121: ../src/lxc/conf.c: turn_into_dependent_mounts: 3886 Permission denied - Failed to open 15/proc/self/mountinfo
    lxc-start: 121: ../src/lxc/utils.c: safe_mount: 1220 Permission denied - Failed to mount "proc" onto "/usr/lib/x86_64-linux-gnu/lxc/rootfs/proc"
    lxc-start: 121: ../src/lxc/conf.c: lxc_mount_auto_mounts: 811 Permission denied - Failed to mount "proc" on "/usr/lib/x86_64-linux-gnu/lxc/rootfs/proc" with flags 14
    lxc-start: 121: ../src/lxc/conf.c: lxc_setup: 4403 Failed to setup first automatic mounts
    lxc-start: 121: ../src/lxc/start.c: do_start: 1272 Failed to setup container "121"
    lxc-start: 121: ../src/lxc/sync.c: sync_wait: 34 An error occurred in another process (expected sequence number 3)
    lxc-start: 121: ../src/lxc/start.c: __lxc_start: 2107 Failed to spawn container "121"
    lxc-start: 121: ../src/lxc/tools/lxc_start.c: main: 306 The container failed to start
    lxc-start: 121: ../src/lxc/tools/lxc_start.c: main: 311 Additional information can be obtained by setting the --logfile and --logpriority options
    

    Un detalle importante: añadir usuarios al grupo procaccess NO resuelve este caso, y conviene entender por qué antes de perder tiempo intentándolo. Un contenedor no privilegiado ejecuta su root mapeado a un uid alto del anfitrión (habitualmente el 100000 en adelante, según su lxc.idmap), que no es el propietario del /proc. Y aunque se metiera ese uid en el grupo, lxc-start ejecuta setgroups(0, NULL) (visible en la traza como lxc_drop_groups) justo antes de preparar los montajes del contenedor, descartando cualquier grupo suplementario. El contenedor se queda sin poder leer /proc/self/mountinfo ni montar proc, y aborta el arranque.

    Los contenedores privilegiados (unprivileged: 0) no se ven afectados: su root es el uid 0 real del anfitrión, que es el propietario del /proc y conserva el acceso.

    Hay un efecto secundario que hace este fallo especialmente traicionero: el permiso del /proc sólo se evalúa en el arranque del contenedor. Un contenedor que ya estuviera en marcha cuando se cerró el /proc sigue funcionando con normalidad de forma indefinida, y sólo falla la próxima vez que se reinicie, que puede ser meses después. Es decir, un nodo puede llevar mucho tiempo en esta situación aparentando estar sano.

    En estos casos la solución es desactivar la protección /proc dentro de la máquina donde se ejecutan dichas instancias LXC. A partir de la versión que incluye la detección de nodos LXC, el comprobador ya no propone la característica en estas máquinas y la rechaza si se intenta activar (ver sección 3), y además la herramienta crad-lxc-mgr.pyc detecta y resuelve el problema automáticamente (ver siguiente sección).

7. Automatización para nodos con contenedores LXC

Como el fallo de arranque de un contenedor LXC no da ninguna pista de que la causa sea el cierre del /proc (el mensaje que se obtiene es únicamente Received container state "ABORTING" instead of "RUNNING"), la herramienta crad-lxc-mgr.pyc incorpora el diagnóstico y la corrección.

7.1. Diagnóstico del arranque

Las opciones –debug y –foreground permiten ver el motivo real por el que un contenedor no arranca:

>> crad-lxc-mgr.pyc --start mariadbdev --debug --foreground
  • –foreground ejecuta lxc-start -F, dejando el arranque conectado al terminal en lugar de lanzarlo en segundo plano, que es lo que oculta el error detrás del mensaje genérico de ABORTING.
  • –debug añade -l DEBUG -o <fichero temporal>, de manera que se registra la traza completa del arranque. Si el arranque falla, se vuelca esa traza y, al final, un bloque con sólo las líneas ERROR/WARN, que es donde está la causa. El fichero de log se conserva y se indica su ruta.

7.2. Corrección automática

Si el arranque falla, la herramienta comprueba si la causa es la protección del /proc. Para ello verifica tres cosas: que el /proc esté cerrado a otros, que el contenedor sea no privilegiado, y que la traza contenga las marcas características (Failed to open uid_map, /proc/self/mountinfo, Failed to mount "proc").

Cuando se confirma, explica la causa y ofrece resolverla:

>> crad-lxc-mgr.pyc --start mariadbdev --debug
...
DIAGNOSIS: the start failed because /proc is closed to others (found 'Failed to open uid_map' in the startup trace and /proc is closed to others)
  The users checker option 'close_proc_to_others' closes /proc (dr-xr-x--- root procaccess).
  Unprivileged containers cannot start with that protection enabled: their root is
  a mapped host uid and lxc-start drops supplementary groups, so adding users to
  the procaccess group does NOT solve it.
Disable 'close_proc_to_others' in the users checker, run it and retry the start? [y/N]: y
OK: disabled 'close_proc_to_others' in the users checker
Running the users checker so it restores /proc permissions...
OK: users checker executed
OK: /proc is accessible again

Retrying start of '181'...
OK: start '181' completed successfully after restoring /proc permissions

La secuencia que realiza al confirmar es: desactivar la opción close_proc_to_others del comprobador users_checker, ejecutar dicho comprobador para que restaure los permisos del /proc, verificar que efectivamente ha quedado accesible, y sólo entonces reintentar el arranque del contenedor. Si cualquier paso falla, se detiene informando de qué revisar. El cambio queda registrado en el histórico de acciones de la herramienta.

Como se está reduciendo el nivel de protección de la máquina, la operación pide confirmación. Para uso desatendido se puede añadir la opción -y.

8. Cómo resolver / añadir usuarios al grupo procaccess

Dispones de los siguientes métodos para añadir/gestionar usuarios al procaccess, y que por tanto, tendrán acceso (sólo de lectura):

  1. Añadir el usuario en el listado de “Allowed proc access users”, dentro de la configuración del checker users_checker (tal y como se muestra en las secciones anteriores):

    NOTA: Una vez añadido el usuario, hay que “Ejecutar” el checker para que se aplique la configuración.

    NOTA 2: Tenga en cuenta que este método no muestra todos los usuarios actualmente metidos el grupo procaccess. Sólo muestra los que han sido configurados en el checker para ser añadidos.

    Si necesita gestionar (quitar) y visualizar qué usuarios están actualmente metidos en grupo procaccess, vea los siguientes dos métodos.

  2. Añadir/gestionar usuarios en el grupo procaccess usando la herramienta de administración de usuarios del sistema. La tendrá que desplegar si no la tiene disponible: Maquina -> Acciones -> Mostrar configuración de la máquina -> Aplicaciones -> Buscar Gestión de usuarios del sistema, desplegar, marcar como instalada y darle a editar.

    Una vez disponible, arrancar:

    Y luego diríjase al grupo procaccess para ver los miembros actuales (ordenando la columna de selección):

  3. El último método, es simplemente añadir el usuario directamente desde línea de comandos:

     # para sistemas debian/ubuntu
    >> adduser <usuario> procaccess
    
    # para sistemas centos
    >> usermod -a -G procaccess <usuario>
    

9. Tipo de acceso concedido si estás en el grupo procaccess

En cualquier caso, sea cual sea el método usado, en cuanto un usuario esté dentro del grupo procaccess, tendrá el mismo acceso que en un sistema sin cierre de proc: acceso de lectura a los ficheros, limitado por el resto de componentes que pudieran haber (como hidepid del mount).