1. Fundamentos y arquitectura de PAM

Los módulos de autenticación conectables o PAM (Pluggable Authentication Modules) son el mecanismo con el que GNU/Linux desacopla las aplicaciones que necesitan autenticar a un usuario, como login, sshd, sudo, su o passwd, de los métodos concretos con los que se realiza esa autenticación.

Sin PAM, cada aplicación tendría que implementar por su cuenta la lectura de /etc/shadow, la comprobación de la caducidad de la cuenta, el bloqueo tras intentos fallidos o la verificación de un segundo factor. Con PAM, la aplicación delega estas decisiones en una biblioteca común, libpam, que lee la política definida por el administrador y ejecuta los módulos indicados en ella. La implementación utilizada en las distribuciones actuales es Linux-PAM.

Este diseño tiene consecuencias directas para el bastionado:

  • Centralización: una política, como el bloqueo tras intentos fallidos, se aplica a todos los servicios que comparten la misma configuración.

  • Independencia de la aplicación: se puede añadir un segundo factor a sshd o a sudo sin modificar ni recompilar estos programas.

  • Riesgo concentrado: un error en un archivo de PAM puede impedir el acceso de todos los usuarios, incluido el administrador, o, peor aún, permitir el acceso sin contraseña. Cualquier cambio debe probarse antes de cerrar la sesión con privilegios.

PAM frente a NSS

PAM decide si un usuario puede autenticarse y abrir una sesión. La resolución de quién es el usuario, es decir, su UID, su grupo o su directorio personal, corresponde al Name Service Switch (NSS), estudiado en el bloque de contenidos sobre gestión de usuarios y grupos. Ambos colaboran, pam_unix consulta las cuentas a través de NSS y valida la contraseña contra /etc/shadow.

En entornos empresariales: SSSD

En redes corporativas rara vez se usan cuentas puramente locales. Lo habitual es delegar la autenticación en un directorio LDAP o Active Directory a través del demonio SSSD, que aporta a la vez el módulo de PAM pam_sss (para la autenticación) y el de NSS (para resolver los usuarios). PAM y NSS siguen cooperando igual, pero las cuentas residen en el directorio central en lugar de en /etc/passwd.


1.1. Flujo de una autenticación

Arquitectura de PAM: aplicación
Figura 1. Arquitectura de PAM: aplicación, libpam, archivo de servicio y pilas de módulos

Conviene ver primero, a grandes rasgos, qué ocurre cuando una aplicación autentica a un usuario a través de PAM. El proceso, que resume la figura, sigue estos pasos:

  1. La aplicación inicia una transacción, el diálogo con PAM que dura toda la autenticación, llamando a pam_start() con el nombre del servicio, por ejemplo sshd.

  2. libpam lee el archivo de configuración de ese servicio, /etc/pam.d/sshd. Si no existe, recurre a /etc/pam.d/other, la configuración por defecto para los servicios que no tienen un archivo propio.

  3. Cuando la aplicación solicita una operación, como pam_authenticate(), libpam ejecuta en orden la pila de módulos que corresponde, es decir, la lista ordenada de módulos de ese tipo. Los tipos de gestión y las pilas se explican en la sección 2.

  4. Cada módulo devuelve un código de resultado, de éxito o de error, y el campo de control de su regla decide cómo influye ese resultado en el resultado global, como se detalla en la sección 4.

  5. La aplicación recibe un único resultado, éxito o error, y actúa en consecuencia.

Los módulos se comunican con el usuario a través de una función de conversación que proporciona la aplicación. Por eso un mismo módulo puede pedir una contraseña en una terminal, en una ventana gráfica o en el intercambio keyboard-interactive de SSH.

2. Tipos de gestión y orden de ejecución

En la sección 1.1 se vio que libpam ejecuta «la pila del tipo correspondiente». Ese tipo de gestión indica en qué fase del acceso interviene cada módulo, ya sea comprobar las credenciales, decidir si la cuenta puede usarse en ese momento, cambiar la contraseña o preparar y cerrar la sesión. Separar el acceso en fases es lo que permite que un mismo módulo, como pam_unix, participe en varias de ellas con cometidos distintos, y que el administrador combine los módulos de forma ordenada dentro de cada una.

PAM organiza las reglas en cuatro tipos de gestión, cada uno asociado a una función de la API que invoca la aplicación:

Tipo Función de la API Pregunta que responde Módulos habituales

auth

pam_authenticate() y pam_setcred()

¿El usuario es quien dice ser?

pam_unix, pam_faillock, pam_google_authenticator, pam_wheel

account

pam_acct_mgmt()

¿Puede usar la cuenta ahora, desde aquí?

pam_unix, pam_nologin, pam_access, pam_time, pam_faillock

password

pam_chauthtok()

¿La nueva credencial cumple la política?

pam_pwquality, pam_pwhistory, pam_unix

session

pam_open_session() y pam_close_session()

¿Qué hay que preparar al abrir y cerrar la sesión?

pam_limits, pam_umask, pam_env, pam_loginuid, pam_systemd

Cada tipo tiene su propia pila, que se evalúa de forma independiente cuando la aplicación invoca la función correspondiente.


2.1. Ejemplo: inicio de sesión de maria en una consola

Los cuatro tipos se entienden mejor siguiéndolos sobre un caso concreto. Cuando maria inicia sesión en una consola de texto, desde que teclea su nombre de usuario hasta que obtiene el prompt del intérprete de comandos, PAM recorre las cuatro pilas en el orden en que la aplicación de login va invocando sus funciones:

  1. auth: pam_nologin comprueba que el sistema no está en mantenimiento y pam_unix solicita y verifica la contraseña de maria.

  2. account: pam_unix comprueba en /etc/shadow que la cuenta no ha expirado. Si la contraseña ha caducado, devuelve PAM_NEW_AUTHTOK_REQD y la aplicación obliga a cambiarla.

  3. password: solo se ejecuta si hay que cambiar la contraseña, pam_pwquality valida su robustez y pam_unix almacena el nuevo hash.

  4. session: pam_loginuid fija el identificador de auditoría, pam_limits aplica los límites de recursos, pam_umask fija la máscara y pam_systemd registra la sesión en systemd-logind.

La autenticación por clave pública de SSH no ejecuta la pila auth

Cuando un usuario accede a sshd con una clave pública, la verificación criptográfica la realiza el propio sshd, y PAM solo ejecuta las pilas account y session. Por eso el bloqueo por intentos fallidos de pam_faillock o el bloqueo de la contraseña con passwd -l no impiden el acceso con claves. Para impedirlo hay que actuar en la pila account, por ejemplo expirando la cuenta con usermod -e 1, como se explicó en el bloque de contenidos sobre gestión de usuarios y grupos.

3. Archivos de configuración

La política que libpam ejecuta no reside en el código de cada aplicación, sino en un conjunto de archivos de configuración que el administrador lee y modifica. Son, en la práctica, el objeto sobre el que se trabaja al bastionar PAM: casi todas las medidas de las secciones siguientes consisten en añadir o ajustar reglas en estos archivos. Por eso conviene conocer primero dónde residen, cómo se escribe cada regla y cómo se gestionan de forma automatizada en Debian y Ubuntu, que es lo que se describe en los tres apartados siguientes.

3.1. Ubicación

La configuración de PAM no está en un único lugar, sino repartida entre el directorio de los servicios, los archivos de configuración de cada módulo y las propias bibliotecas de los módulos. Conviene saber qué contiene cada ubicación:

  • /etc/pam.d/: contiene un archivo por servicio, con el mismo nombre que el que la aplicación pasa a pam_start(): sshd, login, sudo, su, passwd, cron… Es el formato utilizado por todas las distribuciones actuales.

  • /etc/pam.conf: formato histórico en un único archivo, con el nombre del servicio como primer campo de cada línea. Solo se consulta si /etc/pam.d/ no existe.

  • /etc/pam.d/other: configuración que se aplica a cualquier servicio sin archivo propio.

  • /etc/security/: archivos de configuración de los propios módulos, como faillock.conf, pwquality.conf, access.conf, limits.conf o time.conf.

El servicio other en Debian y Ubuntu

En Debian y Ubuntu, /etc/pam.d/other incluye las pilas common-*, por lo que un servicio sin configuración propia autentica como cualquier otro. En la familia Red Hat, other contiene pam_deny.so y deniega todo. Este segundo enfoque, de denegación por defecto, es el más seguro, porque un servicio mal instalado nunca debería heredar una política de acceso que nadie ha revisado.

Los archivos de /etc/pam.d/ deben pertenecer a root:root con permisos 644. Un usuario capaz de escribir en ellos puede añadir pam_permit.so a la pila auth y acceder como cualquier usuario sin contraseña, una técnica de persistencia catalogada en MITRE ATT&CK como Modify Authentication Process: Pluggable Authentication Modules (T1556.003).

3.2. Sintaxis de una regla

Cada línea no comentada de un archivo de /etc/pam.d/ es una regla, que hace que un módulo participe en una de las pilas. Una regla se compone de cuatro campos separados por espacios, que se leen de izquierda a derecha: el tipo de gestión, el control que determina cómo influye su resultado, el módulo que se ejecuta y, de forma opcional, los argumentos que lo configuran. El orden en que se escriben las reglas de un mismo tipo es el orden en que libpam las ejecuta.

tipo      control                      módulo        argumentos
auth      [success=1 default=ignore]   pam_unix.so   nullok
Campo Descripción Ejemplo Explicación técnica

Tipo

Pila a la que pertenece la regla

auth

Un guion inicial, como -session, indica que no se registre un error si el módulo no está instalado.

Control

Cómo influye el resultado del módulo

requisite

Palabra clave o lista [valor=acción …​], explicada en la sección 4.

Módulo

Nombre o ruta del módulo

pam_unix.so

Sin ruta, se busca en el directorio de módulos del sistema.

Argumentos

Opciones del módulo

nullok

Cada módulo define las suyas. Las opciones debug y use_authtok son comunes a muchos de ellos.

Además de reglas, un archivo puede incorporar otros:

  • @include archivo: inserta literalmente todas las reglas del archivo indicado. Es el mecanismo que usan Debian y Ubuntu.

  • include en el campo de control, como en auth include system-auth: incluye solo las reglas del tipo indicado. Es habitual en la familia Red Hat.

  • substack: como include, pero las acciones done y die del archivo incluido solo terminan la subpila y no la pila completa.

3.3. Los archivos common-* y pam-auth-update

En Debian y Ubuntu, casi todos los servicios incluyen cuatro archivos compartidos: common-auth, common-account, common-password y common-session. Estos archivos no deben editarse a mano, porque los genera la herramienta pam-auth-update a partir de los perfiles que instalan los paquetes en /usr/share/pam-configs/. Cuando se instala o actualiza un paquete con un perfil PAM, como libpam-pwquality, libpam-sss o libpam-modules, pam-auth-update regenera los archivos y descarta los cambios manuales realizados.

La forma correcta de añadir un módulo a las pilas comunes es crear un perfil propio en /usr/share/pam-configs/. Un perfil es un archivo de texto que indica qué reglas añadir y a qué pilas, y que pam-auth-update integra en los archivos common-* al activarlo. Por ejemplo, este perfil añade pam_faillock a las pilas auth y account:

Name: Descripción que aparece en el menú de pam-auth-update   (1)
Default: yes                                                  (2)
Priority: 1024                                                (3)
Auth-Type: Primary
Auth:                                                         (4)
    requisite pam_faillock.so preauth
Account-Type: Primary
Account:                                                      (5)
    required pam_faillock.so
1 Texto descriptivo del perfil, que aparece en el menú interactivo de pam-auth-update.
2 Con yes, el perfil se activa de serie al instalarlo.
3 Posición del perfil en la pila, donde los de mayor prioridad se colocan primero.
4 Reglas que se insertarán en la pila common-auth. El Auth-Type: Primary de la línea anterior marca el módulo como método de autenticación principal.
5 Reglas que se insertarán en la pila common-account. Un perfil puede incluir además bloques Password: y Session:.

Una vez guardado el perfil en /usr/share/pam-configs/, se activa o se desactiva con pam-auth-update, que regenera los archivos common-* incorporando o retirando sus reglas:

sudo pam-auth-update --enable nombre_del_perfil     # Activa un perfil
sudo pam-auth-update --disable nombre_del_perfil    # Lo desactiva
sudo pam-auth-update                                # Menú interactivo

Con los conceptos explicados hasta aquí, ya puede realizar el Ejercicio 1. Exploración de la configuración de PAM.

4. El campo de control

El campo de control es el segundo campo de cada regla, visto en la sección 3.2, y, junto con el orden, es lo que define la lógica de seguridad de una pila, ya que decide cómo influye el resultado de cada módulo en el resultado global y si la evaluación continúa o se detiene. Entenderlo es imprescindible para el bastionado, porque un control mal elegido puede tanto bloquear a los usuarios legítimos como dejar entrar a quien no debería. PAM admite dos formas de expresarlo, que se describen en los apartados siguientes. La primera son cuatro palabras clave simples, que cubren la mayoría de los casos. La segunda es una sintaxis avanzada [valor=acción], más expresiva.

4.1. Palabras clave simples

Las cuatro palabras clave simples son la forma más habitual y legible de escribir el control. Cada una resume un comportamiento distinto ante el éxito o el fallo del módulo:

Control Si el módulo tiene éxito Si el módulo falla

required

Continúa con la pila

Continúa, pero el resultado global será un error

requisite

Continúa con la pila

Termina la pila inmediatamente y devuelve el error

sufficient

Termina la pila con éxito, si no ha fallado antes ningún required

Continúa. El fallo se ignora

optional

Continúa. Solo influye si es el único módulo de la pila

Continúa. Solo influye si es el único módulo de la pila

required continúa ejecutando la pila tras un fallo para no revelar al atacante en qué paso ha fallado la autenticación. requisite detiene la pila de inmediato, lo que es útil cuando continuar no tiene sentido o es peligroso, por ejemplo para no pedir la contraseña de una cuenta ya bloqueada.

4.1.1. Recorrido de una pila paso a paso

Para ver cómo se combinan el orden y el control, considere esta pila account de ejemplo, que libpam recorre de arriba abajo cuando la aplicación llama a pam_acct_mgmt():

account  required   pam_nologin.so    (1)
account  requisite  pam_time.so       (2)
account  required   pam_unix.so       (3)
1 Deniega el acceso a los usuarios normales si existe el archivo /etc/nologin.
2 Comprueba si la hora actual está dentro del horario permitido en /etc/security/time.conf.
3 Verifica que la cuenta no esté caducada ni expirada.

El resultado depende de en qué módulo se produzca el fallo:

  • Todo correcto: se ejecutan pam_nologin, pam_time y pam_unix, los tres con éxito. Al llegar al final de la pila sin ningún fallo, el resultado global es éxito y la cuenta se acepta.

  • Fuera de horario: pam_nologin tiene éxito, pero pam_time falla. Como es requisite, la pila se detiene en el acto y devuelve error, y pam_unix ni siquiera llega a ejecutarse. Tiene sentido, porque si el usuario no puede entrar a esa hora, comprobar la caducidad de la cuenta es irrelevante.

  • Cuenta caducada: pam_nologin y pam_time tienen éxito, pero pam_unix falla. Como es required, la pila no se corta: se termina de recorrer, pero el resultado global queda marcado como error. Si hubiera más módulos a continuación, se ejecutarían igualmente, para no revelar en qué punto concreto se rechazó el acceso.

El recorrido ilustra la regla general. PAM ejecuta los módulos en el orden escrito, y la palabra de control de cada uno decide si su fallo detiene la pila (requisite), la marca sin detenerla (required) o la termina antes de tiempo con éxito (sufficient).

4.2. Sintaxis avanzada [valor=acción]

Las palabras clave simples son abreviaturas de una sintaxis más expresiva, en la que se asocia una acción a cada código de resultado del módulo, como success, auth_err, user_unknown, new_authtok_reqd o default para cualquier otro:

Acción Efecto

ignore

El resultado del módulo no influye en el resultado global

ok

El resultado del módulo se tiene en cuenta. Si es un fallo, el resultado global será un fallo

bad

Se marca el resultado global como fallo y se continúa

die

Se marca el resultado global como fallo y se termina la pila

done

Se marca el resultado global como éxito, si no había fallos previos, y se termina la pila

reset

Se olvida el estado acumulado y se continúa

N

Se saltan las N reglas siguientes de la pila

Las equivalencias con las palabras clave simples son las siguientes:

Palabra clave Equivalente

required

[success=ok new_authtok_reqd=ok ignore=ignore default=bad]

requisite

[success=ok new_authtok_reqd=ok ignore=ignore default=die]

sufficient

[success=done new_authtok_reqd=done default=ignore]

optional

[success=ok new_authtok_reqd=ok default=ignore]

4.3. Análisis de common-auth en Ubuntu 24.04

auth  [success=1 default=ignore]  pam_unix.so nullok   (1)
auth  requisite                   pam_deny.so          (2)
auth  required                    pam_permit.so        (3)
1 Si la contraseña es correcta, se salta la siguiente regla. Si falla, el resultado se ignora y se continúa con la regla siguiente.
2 Solo se alcanza si pam_unix ha fallado. pam_deny siempre falla y, al ser requisite, termina la pila con error.
3 Solo se alcanza si pam_unix ha tenido éxito. Como el resultado de pam_unix se descartó con ignore, hace falta un módulo que fije explícitamente un resultado positivo, y pam_permit siempre tiene éxito.

Este patrón de salto, denegación y permiso se repite en las cuatro pilas comunes y permite que pam-auth-update inserte módulos alternativos, como LDAP o Kerberos, cada uno con un salto distinto hacia pam_permit.

El argumento nullok

nullok permite autenticar a cuentas cuyo campo de contraseña está vacío en /etc/shadow. El CIS Benchmark exige eliminarlo de pam_unix. Antes de hacerlo, compruebe que no existen cuentas sin contraseña, como se explicó en el gestión de usuarios y grupos.

5. Módulos más utilizados

Hasta aquí se ha visto cómo PAM organiza y evalúa las reglas. Lo que cada regla ejecuta es un módulo, el archivo .so que realiza el trabajo. El sistema trae decenas de ellos, pero en el bastionado de un servidor se maneja habitualmente un grupo reducido. Esta sección los recoge en dos tablas, la de los módulos de uso general y la de los orientados al bastionado, indicando para cada uno su tipo de pila, su función y dónde se configura.

5.1. Módulos de autenticación y gestión general

Los módulos de esta tabla están presentes en casi cualquier configuración y se encargan de la autenticación básica y de la preparación de la sesión. La mayoría los instala el sistema de serie y actúan sin que el administrador tenga que intervenir.

Módulo Tipos Función Configuración

pam_unix

Todos

Autenticación con /etc/shadow, caducidad de la cuenta y almacenamiento de nuevas contraseñas

Argumentos en las pilas common-*

pam_deny

Todos

Siempre falla y cierra las pilas

Ninguna

pam_permit

Todos

Siempre tiene éxito y fija un resultado positivo tras un salto. Usado fuera de ese patrón, concede acceso sin autenticar

Ninguna

pam_rootok

auth

Tiene éxito si quien invoca es root, de modo que root puede usar su sin contraseña

/etc/pam.d/su

pam_nologin

auth, account

Deniega el acceso a los usuarios distintos de root si existe /var/run/nologin o /etc/nologin

Archivo nologin

pam_succeed_if

Todos

Tiene éxito o falla según atributos de la cuenta, como UID o grupo

Argumentos de la regla

pam_env

auth, session

Define variables de entorno

/etc/environment, /etc/security/pam_env.conf

pam_umask

session

Fija la máscara de creación de archivos

UMASK en /etc/login.defs

pam_limits

session

Aplica límites de recursos por usuario o grupo

/etc/security/limits.conf y limits.d/

pam_motd

session

Muestra el mensaje del día

/etc/motd, /run/motd.dynamic

pam_loginuid

session

Fija el UID de auditoría de la sesión, que se conserva aunque el usuario cambie de identidad con su o sudo

Ninguna

pam_systemd

session

Registra la sesión en systemd-logind

Ninguna

pam_keyinit

session

Crea un nuevo anillo de claves del núcleo para la sesión

Ninguna

No elimine pam_systemd de la pila session

Es el módulo que registra la sesión en systemd-logind, del que dependen el entorno gráfico, el audio, D-Bus y el acceso a los dispositivos. Un cambio en PAM que lo omita puede dejar el login por consola funcionando, pero el escritorio fallará de formas difíciles de diagnosticar.

5.2. Módulos de bastionado

Los módulos de esta tabla son los que se añaden o se ajustan de forma deliberada para endurecer el acceso. Constituyen el núcleo de la configuración de PAM orientada al bastionado y se desarrollan en las secciones siguientes y en los ejercicios.

Módulo Tipos Función Configuración

pam_faillock

auth, account

Bloquea cuentas tras varios intentos fallidos

/etc/security/faillock.conf

pam_pwquality

password

Exige robustez a las nuevas contraseñas

/etc/security/pwquality.conf y pwquality.conf.d/

pam_pwhistory

password

Impide reutilizar contraseñas anteriores

Argumentos o /etc/security/pwhistory.conf

pam_wheel

auth

Restringe su a los miembros de un grupo

/etc/pam.d/su

pam_access

account

Permite o deniega el acceso según usuario, grupo, terminal u origen

/etc/security/access.conf

pam_time

account

Restringe el acceso por franja horaria

/etc/security/time.conf

pam_faildelay

auth

Fija el retardo tras un intento fallido

Argumento delay en microsegundos

pam_google_authenticator

auth

Segundo factor mediante códigos TOTP

~/.google_authenticator de cada usuario

pam_tty_audit

session

Registra en el subsistema de auditoría lo que el usuario teclea

Requiere auditd

pam_mkhomedir

session

Crea el directorio personal en el primer acceso, útil con LDAP

Perfil mkhomedir de pam-auth-update

Módulos obsoletos que aún aparecen en documentación antigua

  • pam_cracklib y pam_tally2 se retiraron de Linux-PAM. Sus sustitutos son pam_pwquality y pam_faillock.

  • pam_securetty sigue existiendo, pero las distribuciones actuales ya no instalan /etc/securetty. El acceso de root se controla con PermitRootLogin en SSH y manteniendo bloqueada su contraseña.

  • pam_lastlog está marcado como obsoleto porque el formato de /var/log/lastlog no admite fechas posteriores a 2038. Ubuntu 24.04 ya no lo incluye en libpam-modules, aunque /etc/pam.d/login todavía lo referencie como optional. Su sustituto es pam_lastlog2, de util-linux.


5.3. Ejemplos de configuración

Los módulos de bastionado de la sección anterior se configuran siguiendo siempre el mismo patrón, que consiste en activar el módulo en el archivo del servicio de /etc/pam.d/ y ajustar su comportamiento en el archivo correspondiente de /etc/security/. Los tres ejemplos siguientes ilustran ese patrón con casos habituales de bastionado, el cierre temporal del acceso, la restricción por origen y los límites de recursos, que se retoman en los ejercicios.

5.3.1. Mantenimiento con nologin

Durante una tarea de mantenimiento conviene impedir que los usuarios entren mientras dura la intervención, sin cerrar el acceso del administrador. El módulo pam_nologin resuelve ese caso con solo crear o borrar un archivo.

echo "Sistema en mantenimiento hasta las 22:00." | sudo tee /etc/nologin > /dev/null
sudo rm /etc/nologin

Mientras el archivo existe, pam_nologin deniega el acceso a cualquier usuario distinto de root y le muestra su contenido. systemd crea automáticamente /run/nologin durante el apagado del sistema para impedir nuevos accesos.

5.3.2. Control de acceso por origen con pam_access

Para que solo los miembros del grupo admins puedan acceder por SSH, y únicamente desde la red de administración, se activa el módulo en /etc/pam.d/sshd:

account  required  pam_access.so

Y se definen las reglas en /etc/security/access.conf. Cada regla tiene tres campos separados por el carácter :. El primero es el permiso, donde + concede el acceso y - lo deniega. El segundo indica a quién se aplica, con los nombres de grupo entre paréntesis para distinguirlos de los de usuario. El tercero indica el origen, que puede ser una red en formato CIDR, la palabra LOCAL para las sesiones iniciadas en la propia máquina o ALL para cualquier origen.

+:(admins):192.168.10.0/24   (1)
+:root:LOCAL                 (2)
-:ALL:ALL                    (3)
1 Permite el acceso a los miembros del grupo admins desde la red 192.168.10.0/24.
2 Permite el acceso a root solo desde una sesión local, nunca remota.
3 Deniega el acceso a cualquier otro usuario u origen y actúa como regla de cierre.

Las reglas se evalúan en orden hasta la primera coincidencia, de modo que el orden importa y la regla de denegación general debe figurar siempre la última, porque cualquier regla escrita después de ella no llegaría a evaluarse.

5.3.3. Límites de recursos con pam_limits

El CIS Benchmark exige impedir los volcados de memoria, que pueden contener contraseñas o claves en claro, mediante un archivo en /etc/security/limits.d/:

*    hard    core       0

La línea tiene cuatro campos. El primero, *, es el dominio al que se aplica el límite, en este caso todos los usuarios. El segundo, hard, es el tipo de límite, un límite rígido que impone el núcleo y que el usuario no puede superar, frente a soft, que el usuario sí puede elevar hasta el valor rígido. El tercero, core, es el recurso limitado, el tamaño de los archivos de volcado de memoria. El cuarto, 0, es el valor, que al ser cero desactiva por completo esos volcados.

Del mismo modo pueden limitarse el número máximo de procesos por usuario, con nproc, para mitigar las «bombas fork», o el número de sesiones simultáneas, con maxlogins.

Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 4. Mantenimiento y restricción de accesos.

6. Bastionado de la autenticación con PAM

6.1. Bloqueo por intentos fallidos con pam_faillock

pam_faillock mitiga los ataques de fuerza bruta y de diccionario contra contraseñas, recogidos en MITRE ATT&CK como Brute Force (T1110). Registra los fallos de cada usuario y bloquea la cuenta durante un tiempo cuando se alcanza un umbral.

Para conseguirlo, el módulo no se coloca en un único lugar, sino que tiene que aparecer en tres puntos de la configuración de PAM, cada uno con un cometido distinto. El primero, antes de pedir la contraseña, rechaza el acceso si la cuenta ya está bloqueada. El segundo, después de comprobar la contraseña, anota el intento como fallido si era incorrecta. El tercero, cuando el acceso ha sido correcto, pone a cero el contador de fallos. Los dos primeros se añaden a la pila auth y el tercero a la pila account.

El bloque siguiente es un ejemplo ilustrativo de cómo quedan esas dos pilas, es decir, los archivos /etc/pam.d/common-auth (las cinco primeras líneas) y /etc/pam.d/common-account (la última), una vez activado el módulo. No es un archivo que se escriba a mano. En Debian y Ubuntu lo genera pam-auth-update a partir de un perfil, como se explica al final de este apartado. Se reproduce aquí solo para ver qué hace cada línea:

auth     requisite      pam_faillock.so preauth     (1)
auth     [success=2 default=ignore]  pam_unix.so    (2)
auth     [default=die]  pam_faillock.so authfail    (3)
auth     requisite      pam_deny.so
auth     required       pam_permit.so
account  required       pam_faillock.so             (4)
1 Antes de pedir la contraseña, deniega el acceso si la cuenta ya está bloqueada.
2 Verificación de la contraseña. Si es correcta, salta las dos reglas siguientes y llega a pam_permit.
3 Solo se alcanza si la contraseña es incorrecta. Registra el fallo, bloquea la cuenta si se alcanza el umbral y termina la pila con error.
4 Tras una autenticación correcta, pone a cero el contador de fallos del usuario.

Conviene fijarse en el campo de control [success=2 default=ignore] de la regla de pam_unix, la que comprueba la contraseña. Como se vio en la sección 4, success=2 significa «si la contraseña es correcta, salta las dos reglas siguientes». Esas dos reglas son authfail y pam_deny, de forma que una contraseña válida las esquiva y llega directamente a pam_permit, que confirma el acceso. Ese número tiene que coincidir con cuántas reglas hay detrás de pam_unix. Si se pusiera success=1, una contraseña correcta saltaría solo una regla, caería en pam_deny (que siempre falla) y nadie podría entrar. Calcular bien ese salto es delicado, y es justo lo que hace pam-auth-update de forma automática, un motivo más para no editar las pilas comunes a mano.

En Debian y Ubuntu, estas reglas se añaden mediante perfiles de pam-auth-update, como se muestra en el Ejercicio 5, y los parámetros se definen en /etc/security/faillock.conf:

Parámetro Valor CIS Descripción

deny

5 o menos

Número de fallos consecutivos que provocan el bloqueo

fail_interval

900

Ventana en segundos en la que se cuentan los fallos

unlock_time

0 o 900 o más

Duración del bloqueo en segundos. Con 0, el desbloqueo es manual por un administrador

even_deny_root

Activado

Aplica también el bloqueo a root

root_unlock_time

60 o más

Duración específica del bloqueo de root

audit

Opcional

Registra el nombre de usuario de los intentos contra cuentas inexistentes

dir

/var/run/faillock

Directorio de los contadores

sudo faillock --user maria            # Consulta los fallos registrados
sudo faillock --user maria --reset    # Desbloquea la cuenta

Consideraciones operativas del bloqueo

  • Denegación de servicio: un atacante que conozca un nombre de usuario puede bloquear su cuenta a propósito. Por ello, unlock_time=0 solo es razonable con un procedimiento de desbloqueo ágil.

  • Bloqueo de root: con even_deny_root, mantenga siempre una vía de acceso alternativa, como la consola física o el modo de rescate.

  • Persistencia: /var/run es un sistema de archivos en memoria, de modo que los contadores se pierden al reiniciar.

  • Opción audit: los usuarios teclean con frecuencia su contraseña en el campo del nombre de usuario, y con audit activado esa contraseña queda escrita en los registros.

pam_faillock actúa sobre las cuentas, con independencia del origen de los intentos. Fail2ban, estudiado en otra guía del curso, actúa sobre las direcciones IP en el cortafuegos. Ambos son complementarios, ya que el primero protege también los accesos locales y el segundo frena a un atacante que prueba muchos nombres de usuario distintos sin llegar a bloquear ninguno.


6.2. Robustez de las contraseñas con pam_pwquality

Las contraseñas débiles son una de las vías de entrada más habituales, así que conviene rechazarlas en el momento en que se crean. De eso se ocupa pam_pwquality, que evalúa cada contraseña nueva en la pila password, la fase de PAM que se ejecuta al cambiar una contraseña, y comprueba que cumple una serie de reglas de robustez antes de que pam_unix la guarde. Si no las cumple, la rechaza y el usuario debe elegir otra.

Su configuración se define en /etc/security/pwquality.conf o, preferiblemente, en archivos de /etc/security/pwquality.conf.d/, que no se sobrescriben al actualizar el paquete. Los parámetros más importantes son los siguientes:

Parámetro Valor CIS Descripción

minlen

14 o más

Longitud mínima

minclass

4

Número de clases de caracteres exigidas: minúsculas, mayúsculas, dígitos y otros símbolos

difok

2 o más

Caracteres que deben cambiar respecto a la contraseña anterior

maxrepeat

3 o menos

Máximo de caracteres idénticos consecutivos

maxsequence

3 o menos

Máxima longitud de secuencias monótonas, como abcd o 4321

dictcheck

1

Rechaza contraseñas basadas en palabras de diccionario, con la biblioteca cracklib

usercheck

1

Rechaza contraseñas que contienen el nombre de usuario

enforcing

1

Rechaza la contraseña en lugar de limitarse a advertir

enforce_for_root

Activado

Aplica la política también cuando root cambia una contraseña

Sin enforce_for_root, cuando root ejecuta passwd usuario, pam_pwquality solo muestra una advertencia y acepta cualquier contraseña. Es un detalle que suele pasarse por alto al verificar la política.

La guía NIST SP 800-63B prioriza la longitud y la comprobación contra listas de contraseñas comprometidas frente a las reglas de composición, que llevan a los usuarios a patrones predecibles como Verano2026!. Los valores del CIS Benchmark combinan ambos enfoques. En cualquier caso, un minlen elevado aporta más seguridad que un minclass alto con una longitud corta.

Para que la política surta efecto hacen falta dos pasos. El primero es que el módulo pam_pwquality esté instalado y presente en la pila common-password. En Debian y Ubuntu se consigue instalando libpam-pwquality, que pam-auth-update añade a common-password de forma automática. En una instalación mínima el módulo no está, y passwd aceptará cualquier contraseña aunque exista el archivo de configuración.

# Actualiza el índice de paquetes
sudo apt update

# Instala el módulo y, de paso, pwscore (del paquete de herramientas)
sudo apt install -y libpam-pwquality libpwquality-tools

El segundo paso es crear el archivo de configuración en /etc/security/pwquality.conf.d/, que pam_pwquality lee al validar cada contraseña nueva, sin necesidad de reiniciar ningún servicio:

# /etc/security/pwquality.conf.d/99-bastionado.conf
minlen = 14
minclass = 4
difok = 2
maxrepeat = 3
maxsequence = 3
dictcheck = 1
usercheck = 1
enforcing = 1
enforce_for_root

Desde ese momento, cualquier contraseña nueva que no cumpla la política se rechaza cuando el usuario ejecuta passwd.

6.2.1. Comprobar la política con pwscore

pwscore, del paquete libpwquality-tools, evalúa una contraseña con la política vigente sin modificar ninguna cuenta. Lee la contraseña por la entrada estándar y devuelve una puntuación de 0 a 100 si la acepta, o el motivo del rechazo si no la cumple. Es la forma más rápida de probar la configuración antes de aplicarla a los usuarios.

# Un patrón predecible, rechazado por longitud
echo 'Verano2026!' | pwscore
Password quality check failed:
 The password is shorter than 14 characters
# Una contraseña que sí cumple la política
echo '7xQ!vp2L#mZ9fR' | pwscore
44

La puntuación no es una nota de corte, sino una orientación, y una contraseña puede cumplir la política y aun así obtener un valor modesto. El ejemplo ilustra también la tensión que señala la nota anterior. La frase de contraseña caballo-grapa-correcta-37, es decir, varias palabras unidas que forman una contraseña larga y fácil de recordar, es mucho más difícil de adivinar que Verano2026!. Aun así, minclass = 4 la rechaza por no reunir cuatro clases de caracteres, mientras que la cadena 7xQ!vp2L#mZ9fR, más corta pero con las cuatro clases, sí la supera. Por eso conviene primar la longitud sobre el número de clases.

pwscore valida el archivo de configuración, porque lee las mismas opciones, pero no depende de que el módulo esté activo en common-password. Para la comprobación de extremo a extremo, pida a una cuenta de prueba que cambie su contraseña con passwd. Si la contraseña no cumple la política, passwd la rechaza con un mensaje que empieza por BAD PASSWORD: seguido del motivo, como BAD PASSWORD: The password is shorter than 14 characters, y vuelve a pedirla.


6.3. Historial de contraseñas con pam_pwhistory

Exigir contraseñas robustas no sirve de mucho si, cuando caducan, el usuario vuelve a la de siempre o alterna entre dos o tres que conoce de memoria. pam_pwhistory lo impide, porque recuerda las últimas contraseñas de cada usuario y rechaza cualquier intento de reutilizar una de ellas.

Para recordarlas, guarda en el archivo /etc/security/opasswd el hash de cada contraseña anterior, es decir, una huella criptográfica irreversible de la que no puede recuperarse la contraseña en claro. El CIS Benchmark exige recordar al menos 24. Al igual que pam_pwquality, el módulo actúa en la pila password, y en Debian y Ubuntu su regla se añade mediante un perfil de pam-auth-update, no se escribe a mano en common-password. La regla resultante tiene este aspecto:

password  requisite  pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok

Cada argumento cumple una función:

  • remember=24 indica cuántas contraseñas anteriores se recuerdan y, por tanto, no pueden reutilizarse.

  • enforce_for_root aplica la comprobación también cuando root cambia una contraseña.

  • try_first_pass reutiliza la contraseña que el usuario ya ha tecleado antes de volver a pedírsela.

  • use_authtok obliga a usar la contraseña que pam_pwquality ya ha recibido y validado, en lugar de solicitarla otra vez. Es lo que encadena ambos módulos en la misma operación de cambio de contraseña.

El archivo /etc/security/opasswd debe tener permisos 600 y pertenecer a root:root, porque contiene hashes de contraseñas y ningún otro usuario debe poder leerlo.


6.4. Opciones seguras de pam_unix

pam_unix es el módulo clásico de autenticación local. Comprueba la contraseña contra /etc/shadow y, cuando se cambia, calcula y guarda su hash. Interviene en casi todas las pilas, así que conviene revisar con qué opciones se configura. El CIS Benchmark establece estos requisitos para sus reglas en las pilas comunes:

  • Sin nullok. La opción nullok permitiría entrar a una cuenta cuyo campo de contraseña está vacío. Debe retirarse de todas las pilas para que ninguna cuenta pueda autenticarse sin contraseña.

  • Sin remember en la pila password. Esta opción haría que el historial de contraseñas lo llevara pam_unix, pero de eso debe encargarse pam_pwhistory (sección 6.3), que lo hace mejor.

  • Algoritmo de hash robusto en la pila password. Es el algoritmo con el que se calcula el hash de la contraseña al guardarla. Cuanto más robusto, más costoso resulta descifrarla si alguien consigue una copia de /etc/shadow. Se recomienda yescrypt, el valor por defecto en Ubuntu 24.04, o sha512.

  • use_authtok en la pila password. Hace que pam_unix guarde la contraseña que ya han recibido y validado los módulos anteriores, como pam_pwquality y pam_pwhistory, en lugar de pedir una nueva por su cuenta.


6.5. Restricción de su con pam_wheel

La orden su, del inglés switch user, permite adoptar la identidad de otro usuario, normalmente root, si se conoce su contraseña. Esto plantea dos problemas de bastionado. El primero es que cualquiera que averigüe la contraseña de root se convierte en root. El segundo es que la acción no queda ligada a una persona concreta. sudo, en cambio, es nominal, es decir, cada acción queda atribuida al usuario que la ejecuta y registrada en el diario.

Por eso el CIS Benchmark exige restringir su a los miembros de un grupo y dejar ese grupo vacío, de modo que en la práctica nadie pueda usar su y la elevación de privilegios se canalice siempre por sudo. El módulo que aplica la restricción es pam_wheel.

Primero se crea el grupo, sin añadirle ningún usuario:

sudo groupadd sugroup

Después se añade la regla al archivo /etc/pam.d/su, que es el archivo de servicio PAM de la orden su y, a diferencia de los archivos common-*, se edita directamente:

# /etc/pam.d/su
auth  required  pam_wheel.so use_uid group=sugroup

La regla exige pertenecer al grupo sugroup para poder usar su, y como el grupo está vacío, nadie cumple la condición. La opción use_uid hace que pam_wheel compruebe el UID real de quien ejecuta su, en lugar de un nombre de inicio de sesión que podría falsearse.

En Ubuntu, la cuenta root viene de serie sin contraseña (bloqueada), de modo que su a root ya falla por sí solo si el usuario no recurre a sudo. Por eso, en Ubuntu el bastionado del acceso privilegiado gira sobre todo en torno al grupo sudo y al archivo /etc/sudoers, y la restricción de su con pam_wheel actúa como medida complementaria, no como barrera principal.


6.6. Autenticación multifactor con TOTP

La autenticación multifactor (MFA) refuerza el acceso al exigir, además de algo que el usuario sabe (la contraseña), algo que tiene (un código temporal generado en su móvil), de modo que una contraseña robada ya no basta para entrar. El módulo pam_google_authenticator, del paquete libpam-google-authenticator, añade ese segundo factor basado en el algoritmo TOTP (Time-based One-Time Password) definido en el RFC 6238. El usuario y el servidor comparten un secreto, y ambos calculan cada 30 segundos un código de seis dígitos a partir de ese secreto y de la hora actual. Lo implementan aplicaciones como Google Authenticator, FreeOTP o Aegis.

  1. Se instala el paquete que aporta el módulo y la herramienta google-authenticator con la que cada usuario genera su secreto:

    # Actualiza el índice de paquetes
    sudo apt update
    
    # Instala el módulo y la herramienta de generación del secreto
    sudo apt install -y libpam-google-authenticator
  2. Cada usuario genera su secreto, que se guarda en ~/.google_authenticator con permisos 400:

    google-authenticator -t -d -f -C -r 3 -R 30 -w 3

    Cada opción ajusta el comportamiento del segundo factor:

    Opción Función

    -t

    Usa códigos basados en el tiempo (TOTP)

    -d

    Impide reutilizar un código ya empleado

    -f

    Escribe el archivo de configuración sin pedir confirmación

    -C

    No solicita confirmar un código durante la instalación, pensado para configuraciones no interactivas

    -r 3 y -R 30

    Limitan los intentos a 3 cada 30 segundos, para frenar los ataques de fuerza bruta contra el código

    -w 3

    Acepta 3 códigos válidos a la vez, para tolerar un pequeño desfase entre el reloj del móvil y el del servidor

    La herramienta dibuja en la propia terminal un código QR con caracteres de bloque y, debajo, el secreto en Base32, un primer código de verificación y cinco códigos de emergencia:

    Warning: pasting the following URL into your browser exposes the OTP secret to Google:
      https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/...
    
    [aquí se dibuja el código QR]
    
    Your new secret key is: JV553MJBX2K5ADDWYPJ4W2PWOQ
    Your verification code for code 1 is 455530
    Your emergency scratch codes are:
      43679288
      50982468
      69373011
      66188301
      86617512
  3. Se registra el secreto en la aplicación del teléfono, por ejemplo Google Authenticator, FreeOTP o Aegis. En Google Authenticator se pulsa el botón +, se elige Escanear un código QR y se apunta la cámara al QR que ha quedado en la terminal. Si la cámara no consigue leerlo, se elige Introducir una clave de configuración, se escribe un nombre para la cuenta, se teclea el secreto de la línea Your new secret key is y se selecciona el tipo Basada en el tiempo. A partir de ese momento la aplicación muestra un código de seis dígitos que se renueva cada 30 segundos.

    No abra la dirección de google.com/chart que aparece en el aviso de la salida. El código QR de la terminal se genera en el propio servidor, mientras que esa URL enviaría el secreto a Google dentro de la propia petición, tal y como advierte el mensaje. Escanee siempre el QR de la terminal o teclee el secreto a mano.

    Algunos detalles que conviene tener en cuenta al registrar el secreto:

    • Escanee el QR antes de cerrar la terminal. La herramienta no vuelve a mostrarlo, y después el secreto solo está en la primera línea de ~/.google_authenticator, un archivo con permisos 400. Si se pierde, hay que generar un secreto nuevo y volver a registrarlo.

    • Guarde los cinco códigos de emergencia en un lugar seguro y fuera del servidor. Cada uno sirve una sola vez y son la única vía de acceso si se pierde el teléfono. Se almacenan en claro al final de ~/.google_authenticator, así que quien pueda leer ese archivo puede usarlos.

  4. Se añade el módulo a la pila auth de /etc/pam.d/sshd, un archivo que ya existe y que en este caso sí se edita, insertando la regla justo a continuación de la línea @include common-auth, de modo que se pidan la contraseña y el código:

    auth  required  pam_google_authenticator.so nullok

    nullok permite el acceso a los usuarios que aún no han configurado el segundo factor y debe eliminarse cuando todos lo hayan hecho.

  5. Se habilita en sshd el método keyboard-interactive, que es el que permite a PAM mantener un diálogo con varias preguntas. Para ello se crea un archivo nuevo en /etc/ssh/sshd_config.d/, en lugar de modificar /etc/ssh/sshd_config:

    # Crea el archivo de configuración propio de la MFA, que no existe de antemano
    sudo nano /etc/ssh/sshd_config.d/60-mfa.conf
    KbdInteractiveAuthentication yes
    UsePAM yes

    El directorio /etc/ssh/sshd_config.d/ está vacío en una instalación recién hecha, así que el archivo se crea desde cero. El nombre es libre siempre que termine en .conf, y el número inicial solo fija el orden de lectura cuando hay varios, igual que en /etc/security/pwquality.conf.d/. Se usa 60-mfa.conf por coherencia con el Ejercicio 8.

    Esta forma de configurar sshd funciona porque /etc/ssh/sshd_config incluye en su parte superior la directiva Include /etc/ssh/sshd_config.d/*.conf, antes de las demás opciones, y porque para casi todas las palabras clave sshd conserva el primer valor que encuentra. Lo que se escriba en el archivo propio prevalece, por tanto, sobre lo que aparezca más abajo en sshd_config. En Ubuntu 24.04 esto importa, ya que sshd_config trae KbdInteractiveAuthentication no de serie, y el archivo nuevo lo deja en yes sin tocar esa línea. La opción UsePAM yes ya viene activada en Ubuntu, y se incluye para dejar explícito que la MFA depende de ella.

  6. Se valida la configuración de sshd y se recarga el servicio:

    # Comprueba la sintaxis de la configuración antes de aplicarla
    sudo sshd -t
    
    # Relee la configuración sin cortar las sesiones SSH abiertas
    sudo systemctl reload ssh

    Este último paso solo hace falta por el cambio del paso 5. Los archivos de /etc/pam.d/ los lee PAM en cada autenticación, así que la regla añadida en el paso 4 surte efecto en el siguiente intento de acceso sin tocar el servicio. En cambio, sshd lee su propia configuración al arrancar, por lo que KbdInteractiveAuthentication no se aplica hasta que se recarga. Conviene usar reload en lugar de restart, porque reload relee la configuración sin cerrar las sesiones establecidas, que es la red de seguridad si la nueva configuración impide autenticarse.

Para exigir además la clave pública, se añade AuthenticationMethods publickey,keyboard-interactive al archivo del paso 5, lo que obliga a recargar de nuevo el servicio. En ese caso, como la clave pública ya sustituye a la contraseña, conviene comentar @include common-auth en /etc/pam.d/sshd para que PAM solo pida el código TOTP.

Mantenga abierta una sesión SSH mientras prueba el acceso desde otra terminal. Compruebe también que la hora del servidor está sincronizada, por ejemplo con timedatectl, ya que un desfase de más de unos segundos invalida los códigos.

Con los conceptos explicados en este apartado, ya puede realizar el Ejercicio 8. Segundo factor TOTP en SSH.

7. Pruebas, depuración y recuperación

7.1. Pruebas con pamtester

pamtester, del paquete del mismo nombre, ejecuta las operaciones de PAM sobre un servicio y un usuario sin necesidad de iniciar sesión, lo que permite comprobar un cambio antes de depender de él:

sudo pamtester login maria authenticate             # Pila auth del servicio login
sudo pamtester sshd maria acct_mgmt                  # Pila account del servicio sshd
sudo pamtester -v su maria authenticate open_session # Varias operaciones y modo detallado

7.2. Registros y depuración

Los módulos registran sus mensajes en la categoría authpriv de syslog, que en Debian y Ubuntu se escribe en /var/log/auth.log y en el diario de systemd:

sudo journalctl -f SYSLOG_FACILITY=10      # Mensajes de autenticación en tiempo real
sudo journalctl -g 'pam_(unix|faillock|pwquality)' --since '-1h'
sudo grep 'pam_unix' /var/log/auth.log

Muchos módulos admiten el argumento debug, que añade información sobre cada decisión. Debe retirarse al terminar, ya que aumenta el volumen de los registros y puede exponer información sensible.

7.3. Prevención y recuperación de bloqueos

Un error en PAM puede dejar el sistema inaccesible

Antes de modificar cualquier archivo de /etc/pam.d/:

  1. Haga una copia de seguridad de /etc/pam.d/ y de /etc/security/.

  2. Mantenga abierta una sesión de root, por ejemplo con sudo -i, que no se verá afectada por los cambios.

  3. Pruebe el cambio con pamtester y con un inicio de sesión nuevo desde otra terminal antes de cerrar la sesión de root.

Si se pierde el acceso, la recuperación exige arrancar en modo de rescate, o con el parámetro del núcleo init=/bin/bash, montar la raíz en modo lectura y escritura y restaurar la copia de los archivos.

8. Guía consolidada de bastionado y lista de verificación

8.1. Matriz de riesgos y medidas técnicas

Vector de amenaza Impacto potencial Medida de bastionado Verificación técnica

Fuerza bruta contra contraseñas

Acceso con credenciales adivinadas

pam_faillock con deny ≤ 5

grep -e deny -e unlock_time /etc/security/faillock.conf

Contraseñas débiles

Éxito de ataques de diccionario

pam_pwquality con valores CIS

grep -r -e minlen -e minclass /etc/security/pwquality.conf*

Reutilización de contraseñas

Uso de credenciales filtradas anteriormente

pam_pwhistory remember=24

grep pam_pwhistory /etc/pam.d/common-password

Cuentas sin contraseña

Acceso sin autenticación

Eliminar nullok

grep -n nullok /etc/pam.d/common-*

Uso de su con contraseñas compartidas

Elevación sin trazabilidad nominal

pam_wheel con grupo vacío

grep pam_wheel /etc/pam.d/su

Robo de la contraseña

Acceso remoto con una credencial válida

Segundo factor TOTP o claves con frase de paso

grep pam_google /etc/pam.d/sshd

Puerta trasera en /etc/pam.d/

Acceso sin contraseña persistente

Permisos 644 root:root y control de integridad

dpkg --verify libpam-modules libpam-runtime

Volcados de memoria con secretos

Fuga de contraseñas y claves

* hard core 0

grep -rs core /etc/security/limits.conf /etc/security/limits.d/


8.2. Lista de verificación CIS Benchmark para Ubuntu 24.04

La siguiente lista reúne los controles del CIS Benchmark para Ubuntu que afectan a PAM, cada uno con la orden que permite verificarlo.

  1. Paquetes actualizados: libpam-runtime, libpam-modules y libpam-pwquality instalados y en su última versión.

    apt list --installed 'libpam-runtime' 'libpam-modules' 'libpam-pwquality' 2>/dev/null
  2. Perfiles de pam-auth-update activados para pam_unix, pam_faillock, pam_pwquality y pam_pwhistory:

    grep -E 'pam_(unix|faillock|pwquality|pwhistory)' /etc/pam.d/common-{auth,account,password}
  3. Bloqueo por intentos fallidos con deny igual o inferior a 5, unlock_time igual a 0 o superior a 900, y even_deny_root o root_unlock_time de al menos 60:

    grep -Ev '^\s*(#|$)' /etc/security/faillock.conf
  4. Política de calidad con minlen de al menos 14, difok de al menos 2, complejidad exigida, maxrepeat y maxsequence de 3 o menos, y dictcheck, enforcing y enforce_for_root activos:

    grep -rEv '^\s*(#|$)' /etc/security/pwquality.conf /etc/security/pwquality.conf.d/
    grep pam_pwquality /etc/pam.d/common-password
  5. Historial de contraseñas con remember de al menos 24, enforce_for_root y use_authtok:

    grep pam_pwhistory /etc/pam.d/common-password
  6. pam_unix sin nullok ni remember, con yescrypt o sha512 y use_authtok en la pila password:

    grep -nE 'pam_unix.*(nullok|remember)' /etc/pam.d/common-* && echo "NO CONFORME"
    grep -E 'pam_unix.*(yescrypt|sha512).*use_authtok|pam_unix.*use_authtok.*(yescrypt|sha512)' /etc/pam.d/common-password
  7. Acceso a su restringido a un grupo vacío:

    grep -E '^\s*auth\s+required\s+pam_wheel\.so.*use_uid.*group=' /etc/pam.d/su
    getent group sugroup
  8. Volcados de memoria deshabilitados mediante pam_limits y fs.suid_dumpable = 0.

  9. Integridad de la configuración de PAM: sin cambios respecto a los archivos distribuidos por los paquetes, salvo los documentados.

    sudo dpkg --verify libpam-runtime libpam-modules libpam-modules-bin
    stat -c '%a %U:%G %n' /etc/pam.d/* | grep -v '^644 root:root'

Una vez estudiadas todas las medidas de esta guía, puede consolidar sus competencias completando el Ejercicio 9. Auditoría CIS de la configuración de PAM.

9. Ejercicios prácticos

Los ejercicios están diseñados para realizarse en orden sobre una máquina virtual Ubuntu 24.04 o Debian de pruebas, ya que algunos reutilizan usuarios, perfiles y archivos creados en ejercicios anteriores. El último ejercicio deja el sistema en su estado original.

No realice estos ejercicios en un servidor en producción. Durante toda la práctica, mantenga abierta en una terminal aparte una sesión de root obtenida con sudo -i: si un cambio en PAM le impide autenticarse, podrá revertirlo desde ella.

Nº Ejercicio Competencia que desarrolla Dificultad

1

Exploración de la configuración de PAM

Identificar servicios, pilas, módulos y perfiles

Baja

2

Un servicio PAM propio y el campo de control

Comprender la evaluación de las pilas con pamtester

Media

3

Autenticación PAM desde una aplicación Python

Integrar PAM en una aplicación y conocer sus límites

Alta

4

Mantenimiento y restricción de accesos

Aplicar pam_nologin y pam_access

Media

5

Bloqueo por intentos fallidos con pam_faillock

Configurar el bloqueo mediante perfiles de pam-auth-update

Media

6

Política de contraseñas con pam_pwquality y pam_pwhistory

Implantar los controles CIS sobre contraseñas

Media

7

Restricción de su con pam_wheel

Limitar la elevación de privilegios a sudo

Media

8

Segundo factor TOTP en SSH

Añadir autenticación multifactor a un servicio remoto

Alta

9

Auditoría CIS de la configuración de PAM

Verificar la conformidad de forma automatizada

Alta

10

Limpieza del entorno

Revertir los cambios realizados durante la práctica

Baja

9.1. Ejercicio 1. Exploración de la configuración de PAM

Realice los siguientes pasos:

  1. Haga una copia de seguridad de /etc/pam.d/, /etc/security/ y /etc/ssh/ en /root/pam_backup/, conservando permisos y propietarios. Instale las herramientas que se usarán en la práctica: pamtester, libpam-pwquality, libpwquality-tools y openssh-server.

    Solución

    # Crea el directorio donde se guardará la copia de seguridad
    sudo mkdir -p /root/pam_backup
    
    # Copia los archivos de configuración conservando permisos y propietarios (-a)
    sudo cp -a /etc/pam.d /etc/security /etc/ssh /root/pam_backup/
    
    # Actualiza el índice de paquetes
    sudo apt update
    
    # Instala las herramientas que se usan durante la práctica
    sudo apt install -y pamtester libpam-pwquality libpwquality-tools openssh-server
  2. Liste los servicios configurados en /etc/pam.d/ y compruebe sus permisos y propietario.

    Solución

    # Lista los archivos de servicio de /etc/pam.d/ con sus permisos y propietario
    ls -l /etc/pam.d/
  3. Muestre las reglas activas de /etc/pam.d/sshd y de common-auth. Identifique qué archivos incluye sshd y explique el patrón de salto de common-auth.

    Solución

    # Muestra solo las reglas activas de sshd, omitiendo comentarios y líneas en blanco
    grep -Ev '^\s*(#|$)' /etc/pam.d/sshd
    
    # Lo mismo para common-auth, la pila auth que incluyen casi todos los servicios
    grep -Ev '^\s*(#|$)' /etc/pam.d/common-auth

    En common-auth obtendrá un bloque parecido a este:

    auth  [success=1 default=ignore]  pam_unix.so nullok
    auth  requisite                   pam_deny.so
    auth  required                    pam_permit.so
    auth  optional                    pam_cap.so

    El patrón de salto está en el control [success=1 default=ignore] de pam_unix. Puede encontrar más información en la sintaxis avanzada de la sección 4.2. Si la contraseña es correcta, success=1 hace saltar una regla, la de pam_deny, y la ejecución cae en pam_permit, que confirma el acceso. Si la contraseña falla, default=ignore descarta ese resultado y continúa en pam_deny, que siempre falla y, al ser requisite, corta la pila. pam_permit es necesario porque el resultado de pam_unix se descartó con ignore, de modo que hace falta un módulo que fije un resultado positivo tras el salto.

    La última línea, pam_cap.so, está después de pam_permit, así que no forma parte del salto. Es un módulo optional que ajusta las capacidades del usuario y solo se alcanza cuando la autenticación ha tenido éxito. Al ser optional, no altera el resultado global aunque falle.

    El número del salto depende de cuántas reglas haya entre pam_unix y pam_permit. Aquí es success=1 porque en medio solo está pam_deny. Si se insertan más módulos ahí, pam-auth-update recalcula ese número de forma automática, como ocurre en el pam_faillock de la sección 6.1, donde pasa a ser success=2.

  4. Compruebe que sshd y sudo están enlazados con libpam y localice el directorio de módulos del sistema.

    Solución

    # Comprueba que el binario de sshd está enlazado con la biblioteca libpam
    ldd "$(command -v sshd)" | grep libpam
    
    # Hacemos lo mismo para sudo
    ldd "$(command -v sudo)" | grep libpam
    
    # Muestra la ruta del módulo pam_unix.so y, con ella, el directorio de módulos del sistema
    dpkg -L libpam-modules | grep 'pam_unix.so$'

    Las órdenes ldd y dpkg -L

    ldd lista las bibliotecas compartidas de las que depende un ejecutable, de modo que, si entre ellas figura libpam, el programa usa PAM para autenticar.

    dpkg -L muestra todos los archivos que instaló un paquete de Debian o Ubuntu, así que filtrar por pam_unix.so revela su ruta y, con ella, el directorio donde residen los módulos del sistema.

  5. Liste los perfiles disponibles para pam-auth-update y muestre el contenido del perfil unix.

    Solución

    # Lista los perfiles disponibles de pam-auth-update (un archivo por perfil)
    ls /usr/share/pam-configs/
    
    # Muestra el contenido del perfil unix, que define las reglas de pam_unix
    cat /usr/share/pam-configs/unix

9.2. Ejercicio 2. Un servicio PAM propio y el campo de control

Realice los siguientes pasos:

  1. Cree el usuario prueba_pam con una contraseña conocida y el grupo admins_lab, sin añadir el usuario a este grupo.

    Solución

    # Crea el usuario de pruebas con directorio personal (-m) y shell bash
    sudo useradd -m -s /bin/bash prueba_pam
    
    # Le asigna una contraseña conocida (la pedirá de forma interactiva)
    sudo passwd prueba_pam
    
    # Crea el grupo admins_lab, de momento sin ningún miembro
    sudo groupadd admins_lab

    El usuario se deja fuera de admins_lab a propósito, porque el paso 2 comprueba justo lo contrario, que la pila auth deniega el acceso a quien no pertenece al grupo. El usuario se añadirá al grupo más adelante, en el paso 4.

  2. Cree el servicio /etc/pam.d/laboratorio con una pila auth que solo permita autenticarse a los miembros de admins_lab y una pila account basada en common-account:

    auth     requisite  pam_succeed_if.so user ingroup admins_lab
    auth     required   pam_unix.so
    @include common-account

    Compruebe con pamtester que prueba_pam no puede autenticarse. Observe si llega a pedirse la contraseña y explique el motivo.

    Solución

    # Crea el servicio con sus tres reglas
    sudo nano /etc/pam.d/laboratorio
    
    # Comprueba el acceso. No se pide la contraseña, porque pam_succeed_if falla y, al ser requisite, termina la pila
    sudo pamtester laboratorio prueba_pam authenticate

    pam_succeed_if solo tiene éxito si se cumple su condición, que aquí es user ingroup admins_lab. Como prueba_pam todavía no pertenece a admins_lab (en el paso 1 se creó sin añadirlo al grupo), la condición es falsa y el módulo falla. Al ser requisite, la pila auth se corta en esa primera regla, de modo que pam_unix no llega a ejecutarse y no se pide la contraseña. El acceso se deniega antes incluso de solicitar credenciales.

  3. Cambie requisite por required en la primera regla y repita la prueba. Explique la diferencia de comportamiento y por qué required es preferible desde el punto de vista de la seguridad.

    Solución

    # Sustituye requisite por required en la primera regla del servicio
    sudo sed -i 's/^auth     requisite/auth     required /' /etc/pam.d/laboratorio
    
    # Repite la prueba. Ahora sí se pide la contraseña, aunque el resultado será un error, de modo que el atacante no sabe qué comprobación ha fallado
    sudo pamtester laboratorio prueba_pam authenticate

    Aunque ahora se pida la contraseña, la autenticación sigue fallando incluso con la contraseña correcta, porque prueba_pam todavía no pertenece a admins_lab y pam_succeed_if continúa fallando. La diferencia es que, con required, ese fallo no corta la pila, sino que marca el resultado global como error y deja continuar hasta pam_unix. Por eso pam_unix llega a pedir la contraseña y la valida, pero su éxito no rescata el fallo previo. La contraseña correcta solo satisface a pam_unix, no al requisito de pertenecer al grupo.

    Desde el punto de vista de la seguridad, required es preferible precisamente por eso, ya que al pedir siempre la contraseña no revela en qué comprobación se ha fallado. Con requisite, la ausencia de la pregunta delataría que el usuario no cumple una condición previa, una información útil para un atacante. El acceso solo tendrá éxito cuando prueba_pam esté en admins_lab, como se hace en el paso siguiente.

  4. Añada prueba_pam al grupo admins_lab y compruebe que la autenticación tiene éxito con la contraseña correcta y falla con una incorrecta.

    Solución

    # Añade prueba_pam al grupo admins_lab sin sacarlo de sus otros grupos (-aG)
    sudo usermod -aG admins_lab prueba_pam
    
    # Prueba las fases auth y account. Con la contraseña correcta ahora sí tiene éxito
    sudo pamtester laboratorio prueba_pam authenticate acct_mgmt

    Las dos palabras finales (authenticate y acct_mgmt) son operaciones que pamtester ejecuta en orden, cada una sobre una pila distinta del servicio laboratorio. authenticate ejecuta la fase auth (pam_succeed_if y pam_unix), que comprueba quién es el usuario y valida su contraseña. acct_mgmt ejecuta la fase account, que aquí es @include common-account, y comprueba si esa cuenta puede usarse una vez autenticada, es decir, que no esté caducada, expirada ni bloqueada. Pedir ambas reproduce lo que hace un acceso real, donde la aplicación llama primero a pam_authenticate() y después a pam_acct_mgmt().

  5. Compruebe qué ocurre al autenticar contra un servicio inexistente, como servicio_fantasma, y relacione el resultado con el contenido de /etc/pam.d/other.

    Solución

    # Intenta autenticar contra un servicio que no tiene archivo en /etc/pam.d/
    sudo pamtester servicio_fantasma prueba_pam authenticate

    Funciona con la contraseña correcta. Como no existe /etc/pam.d/servicio_fantasma, PAM aplica /etc/pam.d/other, que en Debian y Ubuntu incluye las pilas common-*, de modo que un servicio sin configuración propia autentica igual que cualquier otro. En la familia Red Hat, en cambio, other usa pam_deny y el acceso se denegaría.


9.3. Ejercicio 3. Autenticación PAM desde una aplicación Python

En este ejercicio se desarrolla una aplicación que delega la autenticación en PAM mediante la biblioteca python-pam, utilizando el servicio laboratorio creado en el ejercicio anterior.

Realice los siguientes pasos:

  1. Cree un entorno virtual de Python en ~/app_pam e instale en él la biblioteca python-pam.

    Solución

    # Actualiza el índice de paquetes
    sudo apt update
    
    # Instala el módulo de Python para crear entornos virtuales
    sudo apt install -y python3-venv
    
    # Crea un entorno virtual aislado en ~/app_pam/venv
    python3 -m venv ~/app_pam/venv
    
    # Instala python-pam dentro del entorno virtual, sin tocar el Python del sistema
    ~/app_pam/venv/bin/pip install python-pam
  2. Cree el archivo ~/app_pam/app.py con el siguiente contenido:

    #!/usr/bin/env python3
    import getpass
    import pam
    
    def autenticar():
        p = pam.pam()
        usuario = input("Usuario: ")
        password = getpass.getpass("Contraseña: ")
        if p.authenticate(usuario, password, service="laboratorio"):
            print(f"Autenticación correcta. Bienvenido, {usuario}.")
        else:
            print(f"Error de autenticación: {p.reason} (código {p.code}).")
    
    if __name__ == "__main__":
        autenticar()
  3. Ejecute la aplicación con privilegios de root y autentique a prueba_pam con la contraseña correcta y con una incorrecta.

    Solución

    # Ejecuta la aplicación como root, que sí puede leer /etc/shadow
    sudo ~/app_pam/venv/bin/python ~/app_pam/app.py
  4. Ejecute la aplicación con su usuario sin privilegios e intente autenticar a prueba_pam. Explique por qué falla aunque la contraseña sea correcta, teniendo en cuenta que un proceso sin privilegios no puede leer /etc/shadow y que pam_unix recurre entonces al programa auxiliar unix_chkpwd.

    Solución

    # Ejecuta la aplicación como tu usuario normal, sin sudo
    ~/app_pam/venv/bin/python ~/app_pam/app.py
    
    # El verificador auxiliar de contraseñas es SetGID shadow
    ls -l /usr/sbin/unix_chkpwd

    La autenticación falla aunque la contraseña sea correcta. El archivo /etc/shadow tiene modo 640 y pertenece a root:shadow, así que un proceso sin privilegios no puede leerlo. Cuando pam_unix no se ejecuta como root, delega la comprobación en el programa auxiliar unix_chkpwd, que es SetGID shadow y sí puede leer /etc/shadow. Pero unix_chkpwd solo acepta verificar la contraseña del propio usuario que lo invoca, no la de otra cuenta. Como tu usuario intenta autenticar a prueba_pam, que es distinto, unix_chkpwd se niega y la autenticación falla. Esta restricción impide que un proceso sin privilegios use unix_chkpwd como un oráculo de fuerza bruta contra las contraseñas de otros usuarios. Por eso el paso 3 sí funcionaba con sudo, ya que root puede leer /etc/shadow directamente.


9.4. Ejercicio 4. Mantenimiento y restricción de accesos

Realice los siguientes pasos:

  1. Cree el archivo /etc/nologin con un mensaje de mantenimiento y compruebe con pamtester que prueba_pam no puede autenticarse en el servicio login, mientras que root sí. Elimine después el archivo.

    Solución

    # Crea /etc/nologin con un mensaje; su mera presencia bloquea el acceso de los no-root
    echo "Mantenimiento programado." | sudo tee /etc/nologin > /dev/null
    
    # prueba_pam es rechazado por pam_nologin
    sudo pamtester login prueba_pam authenticate
    
    # root sí puede entrar, siempre que tenga contraseña; si está bloqueada, falla por ese motivo
    sudo pamtester login root authenticate
    
    # Elimina el archivo para restaurar el acceso normal
    sudo rm /etc/nologin

    pam_nologin actúa en la pila auth del servicio login. Si existe /etc/nologin, el módulo muestra su contenido y devuelve un fallo para cualquier cuenta que no sea root, de modo que prueba_pam queda rechazado sin llegar a comprobar su contraseña. La cuenta de root está exenta, por lo que el administrador conserva el acceso mientras el sistema permanece en mantenimiento.

  2. Active pam_access en la pila account del servicio laboratorio y configure /etc/security/access.conf para que solo los miembros de admins_lab puedan acceder, y únicamente en local. Haga antes una copia de access.conf.

  3. Compruebe con pamtester la operación acct_mgmt para prueba_pam, retírelo del grupo admins_lab y repita la prueba. Restaure después access.conf y vuelva a añadir el usuario al grupo.

  4. Cree el archivo /etc/security/limits.d/50-core.conf que deshabilita los volcados de memoria y compruebe el límite en una sesión nueva de prueba_pam.


9.5. Ejercicio 5. Bloqueo por intentos fallidos con pam_faillock

Realice los siguientes pasos:

  1. Cree los dos perfiles de pam-auth-update que utiliza el CIS Benchmark para activar pam_faillock.

    Solución

    # Crea el primer perfil, que añade la regla authfail a la pila auth
    sudo nano /usr/share/pam-configs/faillock
    Name: Enable pam_faillock to deny access      (1)
    Default: yes                                  (2)
    Priority: 0                                   (3)
    Auth-Type: Primary                            (4)
    Auth:
        [default=die] pam_faillock.so authfail    (5)
    1 Texto descriptivo del perfil, que aparece en el menú interactivo de pam-auth-update.
    2 Con yes, el perfil queda activado de serie.
    3 Prioridad 0, menor que la del perfil unix de pam_unix, que vale 256, así que esta regla se coloca después de pam_unix en la pila auth.
    4 Marca el módulo como método de autenticación principal de la pila auth.
    5 El argumento authfail anota el intento como fallido cuando la contraseña es incorrecta, y el control [default=die] corta la pila en ese momento.
    # Crea el segundo perfil, con la regla preauth (auth) y el reseteo del contador (account)
    sudo nano /usr/share/pam-configs/faillock_notify
    Name: Notify of failed login attempts and reset count upon success
    Default: yes
    Priority: 1024                                                      (1)
    Auth-Type: Primary
    Auth:
        requisite pam_faillock.so preauth                               (2)
    Account-Type: Primary                                               (3)
    Account:
        required pam_faillock.so                                        (4)
    1 Prioridad 1024, mayor que la de pam_unix, de modo que esta regla se sitúa antes de pam_unix en la pila auth.
    2 El argumento preauth comprueba antes de pedir la contraseña si la cuenta ya está bloqueada, y el control requisite interrumpe la autenticación cuando lo está.
    3 Marca el módulo como principal también en la pila account.
    4 Pone a cero el contador de fallos tras un acceso correcto.

    Se crean dos perfiles porque las reglas de pam_faillock deben quedar en posiciones distintas de la pila, y en los perfiles de pam-auth-update la posición la fija el campo Priority, donde a mayor prioridad, más arriba en la pila. Tomando como referencia el Priority: 256 del perfil unix, el perfil faillock queda por debajo de pam_unix y el perfil faillock_notify por encima.

    En conjunto, los dos perfiles dejan pam_faillock en los tres puntos descritos en la sección 6.1.

    Al copiar el contenido de los dos perfiles, asegúrese de que ninguna línea termina con espacios en blanco. pam-auth-update compara el valor de los campos Auth-Type, Account-Type y Password-Type con los textos exactos Primary y Additional, de modo que un espacio final convierte el valor en Primary y deja de coincidir con cualquiera de los dos. Las reglas de ese perfil se descartan en silencio al generar los archivos common-*, sin ningún mensaje de error, así que pam_faillock no llega a la pila y el bloqueo nunca se aplica, aunque el perfil se haya activado con --enable.

    Un espacio final en Default: yes produce un efecto parecido, ya que el perfil deja de contar como activable por omisión y no se selecciona cuando pam-auth-update se ejecuta sin indicar perfiles, que es lo que ocurre al instalarse o actualizarse un paquete. En los campos Name y Priority, y en las líneas de las reglas, los espacios finales son inofensivos, porque el analizador las recorta antes de usarlas. Ante la duda, elíminelos de todas las líneas y vuelva a ejecutar pam-auth-update.

  2. Active ambos perfiles y compruebe en common-auth y common-account la posición en la que se han insertado las reglas. Observe cómo ha cambiado el salto success= de pam_unix.

    Solución

    # Activa los dos perfiles de faillock
    sudo pam-auth-update --enable faillock faillock_notify
    
    # Muestra las reglas activas de ambas pilas (grep antepone el nombre de cada archivo)
    grep -Ev '^\s*(#|$)' /etc/pam.d/common-auth /etc/pam.d/common-account

    En la salida, pam_faillock aparece insertado en los tres puntos de la sección 6.1: como preauth antes de pam_unix en la pila auth, como authfail justo después, y como regla required en la pila account.

    Al añadirse pam_faillock.so authfail detrás de pam_unix, entre este y pam_permit pasa a haber dos reglas (authfail y pam_deny), por lo que pam-auth-update ha recalculado el salto de pam_unix de [success=1 …] a [success=2 …]. En common-account, en cambio, pam_unix conserva success=1, porque la regla de faillock va antes de él y no altera el salto.

  3. Configure en /etc/security/faillock.conf un umbral de 3 fallos, un bloqueo de 120 segundos y la opción audit. Estos valores son más laxos que los del CIS para agilizar la prueba.

    Solución

    # Abre el archivo de configuración de faillock
    sudo nano /etc/security/faillock.conf

    El archivo ya existe con todas sus opciones comentadas, así que basta con añadir estas tres líneas al final:

    deny = 3
    unlock_time = 120
    audit

    Las opciones se escriben en /etc/security/faillock.conf, que es la forma recomendada, en lugar de pasarlas como argumentos de las reglas, ya que así las tres reglas de los dos perfiles comparten una misma configuración. Con deny = 3 la cuenta se bloquea al cuarto intento fallido, con unlock_time = 120 el bloqueo se levanta solo a los dos minutos y audit registra en el diario el nombre del usuario de cada intento fallido, incluso cuando la cuenta no existe.

  4. Introduzca tres contraseñas incorrectas para prueba_pam con pamtester y compruebe que, en el cuarto intento, se rechaza incluso la contraseña correcta. Consulte el estado con faillock y los mensajes en el diario.

    Solución

    # Tres intentos con contraseña incorrecta para alcanzar el umbral de bloqueo
    for i in 1 2 3; do sudo pamtester login prueba_pam authenticate; done
    
    # Cuarto intento con la contraseña correcta: ahora se rechaza porque la cuenta ya está bloqueada
    sudo pamtester login prueba_pam authenticate
    
    # Consulta el estado del bloqueo y el número de fallos registrados de prueba_pam
    sudo faillock --user prueba_pam
    
    # Busca en el diario los mensajes de pam_faillock de los últimos 10 minutos
    sudo journalctl -g pam_faillock --since '-10min'

    Los tres primeros intentos los deniega pam_unix, porque la contraseña es incorrecta, y pam_faillock.so authfail anota cada fallo en /var/run/faillock/prueba_pam. En el cuarto intento ya no se llega a comprobar la contraseña, pues la regla pam_faillock.so preauth, situada antes de pam_unix, detecta que se ha alcanzado el umbral de deny = 3 y corta la pila auth. La salida de faillock muestra la marca de tiempo y el origen de cada fallo, y el diario recoge las líneas de pam_faillock gracias a la opción audit.

  5. Desbloquee la cuenta manualmente y compruebe que el acceso vuelve a ser posible.

    Solución

    # Pone a cero el contador de fallos de prueba_pam y desbloquea la cuenta
    sudo faillock --user prueba_pam --reset
    
    # Comprueba que, con la contraseña correcta, el acceso vuelve a funcionar
    sudo pamtester login prueba_pam authenticate

    La opción --reset borra los intentos fallidos registrados, de modo que preauth vuelve a dejar pasar la autenticación sin esperar los 120 segundos de unlock_time. El mismo efecto se consigue de forma automática al expirar ese plazo, y el contador se pone a cero también tras un acceso correcto, gracias a la regla required pam_faillock.so de la pila account.


9.6. Ejercicio 6. Política de contraseñas con pam_pwquality y pam_pwhistory

Realice los siguientes pasos:

  1. Cree el archivo /etc/security/pwquality.conf.d/50-cis.conf con los valores del CIS Benchmark de la sección 6.2.

    Solución

    # Crea el directorio de configuración si no existe
    sudo mkdir -p /etc/security/pwquality.conf.d
    
    # Crea el archivo con las opciones del CIS Benchmark
    sudo nano /etc/security/pwquality.conf.d/50-cis.conf
    minlen = 14
    minclass = 4
    difok = 2
    maxrepeat = 3
    maxsequence = 3
    dictcheck = 1
    usercheck = 1
    enforcing = 1
    enforce_for_root

    Los archivos de /etc/security/pwquality.conf.d/ se leen en orden alfabético después de /etc/security/pwquality.conf y sus valores prevalecen sobre los de este, así que la política queda en un archivo propio sin modificar el del sistema, que los paquetes pueden sobrescribir al actualizarse. De las opciones escritas, enforcing = 1 hace que pam_pwquality rechace las contraseñas que no cumplen la política, en lugar de limitarse a advertir, y enforce_for_root extiende la comprobación a los cambios de contraseña realizados por root, que de serie queda exento.

  2. Evalúe con pwscore las contraseñas Bastionado2026, aaaa1234ABCD!! y Tr3s-Gatos.Saltan_Muros e indique qué regla incumple cada una de las rechazadas.

    Solución

    # Rechazada, solo reúne tres clases de caracteres porque no incluye símbolos
    echo 'Bastionado2026' | pwscore
    
    # Rechazada, tiene cuatro caracteres repetidos y dos secuencias monótonas
    echo 'aaaa1234ABCD!!' | pwscore
    
    # Aceptada, cumple todas las reglas de la política
    echo 'Tr3s-Gatos.Saltan_Muros' | pwscore

    pwscore lee la contraseña de la entrada estándar y la evalúa con la misma configuración que usa pam_pwquality, de modo que permite probar la política sin cambiar ninguna contraseña. Si la acepta, devuelve una puntuación de 0 a 100, y si la rechaza, indica la regla incumplida.

    • Bastionado2026 incumple minclass = 4, ya que solo combina tres clases de caracteres, minúsculas, mayúsculas y dígitos, y no incluye ningún símbolo.

    • aaaa1234ABCD!! sí reúne las cuatro clases, pero incumple maxrepeat = 3 por los cuatro caracteres a consecutivos y maxsequence = 3 por las secuencias monótonas 1234 y ABCD.

    • Tr3s-Gatos.Saltan_Muros se acepta, porque supera los 14 caracteres de minlen, reúne las cuatro clases y no contiene repeticiones, secuencias ni palabras de diccionario.

  3. Cree el perfil pwhistory en /usr/share/pam-configs/ que añade pam_pwhistory con remember=24, enforce_for_root, try_first_pass y use_authtok, actívelo y compruebe el orden de las reglas en common-password.

    Solución

    # Crea el perfil pwhistory, que añade pam_pwhistory a la pila password
    sudo nano /usr/share/pam-configs/pwhistory
    Name: pwhistory password history checking
    Default: yes
    Priority: 1024
    Password-Type: Primary
    Password:
        requisite pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok
    # Activa el perfil para que pam-auth-update regenere common-password
    sudo pam-auth-update --enable pwhistory
    
    # Muestra las reglas activas de common-password para ver dónde se insertó pam_pwhistory
    grep -Ev '^\s*(#|$)' /etc/pam.d/common-password

    El perfil sigue la misma mecánica que los del ejercicio 5, pero sobre la pila password, con los campos Password-Type y Password: en lugar de los de auth. Su Priority: 1024 es mayor que el 256 del perfil unix, así que la regla de pam_pwhistory se inserta antes de pam_unix, que es el orden necesario para rechazar una contraseña repetida antes de que pam_unix guarde su hash en /etc/shadow.

    Las dos últimas opciones afectan a cómo obtiene el módulo la contraseña. Con use_authtok, pam_pwhistory no la pide de nuevo, sino que reutiliza la que ya ha recogido el módulo anterior de la pila, y try_first_pass le indica que pruebe primero esa contraseña. Así el usuario la escribe una sola vez aunque varios módulos la comprueben. Por su parte, remember=24 conserva los 24 hashes anteriores y enforce_for_root aplica también la comprobación a root.

  4. Como root, cambie la contraseña de prueba_pam por una que cumpla la política. Intente volver a asignarle la misma y después una débil, y compruebe que ambas se rechazan.

    Solución

    # Asigna una contraseña robusta a prueba_pam: aceptada por pam_pwquality
    sudo passwd prueba_pam
    
    # Intenta reasignar la misma contraseña: rechazada por pam_pwhistory
    sudo passwd prueba_pam
    
    # Intenta una contraseña débil: rechazada por pam_pwquality gracias a enforce_for_root
    sudo passwd prueba_pam
    
    # Muestra el historial de hashes anteriores almacenado por pam_pwhistory
    sudo cat /etc/security/opasswd

    Cada rechazo lo produce un módulo distinto de la pila password. La contraseña repetida la deniega pam_pwhistory, que la compara con los hashes guardados en /etc/security/opasswd y responde con el mensaje Password has been already used. La contraseña débil la deniega pam_pwquality, al no cumplir las reglas del apartado 1. Ambos rechazos se producen únicamente porque los dos módulos llevan enforce_for_root, ya que de serie root puede asignar cualquier contraseña a otra cuenta sin pasar por estas comprobaciones. El archivo /etc/security/opasswd solo es legible por root y guarda los hashes antiguos de cada usuario, que son los que sostienen el historial.


9.7. Ejercicio 7. Restricción de su con pam_wheel

Realice los siguientes pasos:

  1. Compruebe que prueba_pam puede usar su para adoptar la identidad de otra cuenta si conoce su contraseña. Para ello, cree el usuario destino_lab con una contraseña conocida.

  2. Cree el grupo vacío sugroup y añada a /etc/pam.d/su, antes de @include common-auth, la regla de pam_wheel de la sección 6.5.

  3. Repita la prueba del primer paso y compruebe que ahora se deniega aunque la contraseña sea correcta. Verifique que sudo sigue funcionando para su usuario.


9.8. Ejercicio 8. Segundo factor TOTP en SSH

Realice los siguientes pasos:

  1. Instale libpam-google-authenticator y oathtool, que se usará para calcular los códigos sin necesidad de un teléfono móvil.

  2. Genere el secreto TOTP de prueba_pam y obtenga el secreto en Base32, que es la primera línea del archivo ~/.google_authenticator.

  3. Añada el módulo a /etc/pam.d/sshd tras @include common-auth y cree el archivo /etc/ssh/sshd_config.d/60-mfa.conf que habilita KbdInteractiveAuthentication. Valide la configuración y recargue el servicio.

  4. Sin cerrar su sesión actual, conéctese con ssh prueba_pam@localhost. Cuando se solicite el código de verificación, calcúlelo en otra terminal con oathtool.

  5. Compruebe que un usuario sin archivo ~/.google_authenticator puede seguir accediendo gracias a nullok y razone qué riesgo supone mantener esta opción una vez completado el despliegue.


9.9. Ejercicio 9. Auditoría CIS de la configuración de PAM

Desarrolle el script /root/auditoria_pam.sh que compruebe los controles de la sección 8.2 y muestre CONFORME o NO CONFORME para cada uno. Ejecútelo y analice los controles que no se cumplen tras los ejercicios anteriores, teniendo en cuenta que algunos valores se relajaron deliberadamente para agilizar las pruebas.


9.10. Ejercicio 10. Limpieza del entorno

Para terminar, revierta los cambios realizados durante la práctica.

Realice los siguientes pasos:

  1. Desactive y elimine los perfiles de pam-auth-update creados durante la práctica.

  2. Restaure /etc/pam.d/, /etc/security/ y /etc/ssh/ desde la copia de seguridad del primer ejercicio, elimine el archivo /etc/pam.d/laboratorio, que no existía en la copia, y recargue el servicio SSH tras validar su configuración.

  3. Elimine los usuarios prueba_pam y destino_lab con sus directorios personales, los grupos admins_lab y sugroup, el directorio ~/app_pam y el script de auditoría.

  4. Compruebe con pamtester que su propio usuario puede autenticarse con normalidad en los servicios login y sudo antes de cerrar la sesión de root de seguridad. Elimine finalmente la copia /root/pam_backup.

Con la realización de estos ejercicios se consolidan los procedimientos esenciales de configuración, prueba y auditoría de PAM: la evaluación de las pilas, la gestión de perfiles con pam-auth-update, el bloqueo por intentos fallidos, la política de contraseñas, la restricción de su y la autenticación multifactor, conforme a las directrices del CIS Benchmark.

10. Recursos y referencias

11. Licencia

Licencia CC BY-NC-ND 4.0

Esta página forma parte del curso Bastionado de Redes y Sistemas © 2026 de José Juan Sánchez Hernández y su contenido se distribuye bajo una licencia Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International.

Esta licencia exige que quienes reutilicen el material otorguen el debido crédito al autor. Permite copiar y redistribuir el material en cualquier medio o formato, únicamente en su forma original, y solo para fines no comerciales.