Saltar a contenido

Endurecer PAM

PAM es el sistema que decide quién entra y quién no en un servidor Linux. SSH, sudo, su y el login local no verifican credenciales por su cuenta: delegan en esta pila de módulos. Endurecer PAM cierra una vía de ataque que el resto de la guía no cubre — el momento en el que alguien ya tiene credenciales.

Qué es PAM

Cada servicio con autenticación tiene un fichero en /etc/pam.d/: sshd, sudo, su, login, passwd... Dentro se define la pila de módulos que se ejecutan en orden:

  • auth — comprobación de la identidad (contraseña, token, clave).
  • account — autorización: ¿puede este usuario entrar ahora y desde aquí?
  • password — reglas de cambio de contraseña.
  • session — acciones al iniciar/cerrar sesión (montajes, límites, logs).

Cada línea usa un flag de control que decide cómo afecta el resultado del módulo al resto de la pila:

Flag Qué hace
required Si falla, la autenticación falla, pero el resto de módulos se ejecutan.
requisite Si falla, la autenticación falla y se detiene la pila.
sufficient Si tiene éxito, la autenticación pasa sin mirar el resto de módulos.
optional Su resultado no decide nada; solo aporta información extra.

Quién gestiona estos ficheros

En Ubuntu los common-auth, common-password, common-account y common-session los regenera pam-auth-update. En Fedora/RHEL los ficheros password-auth y system-auth los gestiona authselect — no se editan a mano, se activan features. Las recetas de abajo respetan ambos.

Enfoque por niveles

Como en Proteger SSH, la guía sube de nivel añadiendo capas a la autenticación. Puedes parar donde quieras.

Nivel Qué añade ¿Aplicarlo?
1 Política de contraseñas (pam_pwquality) Recomendado
2 Bloqueo de intentos (pam_faillock) Recomendado
3 Acceso por origen y horario (pam_access/pam_time) Opcional, según uso
4 Segundo factor TOTP (pam_oath) Opcional, avanzado

Nivel 1 — Política de contraseñas

Recomendado. Fuerza una longitud mínima y composición obligatoria al definir o cambiar contraseña. No afecta a la de hosting actual, solo a las nuevas.

  1. Configurar la política en /etc/security/pwquality.conf (común a ambas):

    sudo vim /etc/security/pwquality.conf
    

    Contenido recomendado:

    minlen = 14
    retry = 3
    lcredit = -1
    ucredit = -1
    dcredit = -1
    ocredit = -1
    

     

    Con minlen = 14 y los cuatro *credit = -1, la contraseña debe tener al menos 14 caracteres e incluir letras, mayúsculas, dígitos y un símbolo. Un crédito (positivo) reduciría la longitud requerida; un valor negativo exige al menos uno de esa clase.

  2. Comprobar que el módulo pam_pwquality está en la pila de contraseñas:

    sudo vim /etc/pam.d/common-password
    

    Debe existir (antes de la línea de pam_unix):

    password requisite pam_pwquality.so retry=3
    

    Si no está, añadirla como primera línea de password.

    grep pam_pwquality /etc/pam.d/system-auth
    

    Debe salir una línea password requisite pam_pwquality.so retry=3. Si no aparece, activarla:

    sudo authselect enable-feature with-pwquality
    

Nivel 2 — Bloqueo de intentos

Recomendado. pam_faillock bloquea la cuenta durante un tiempo tras varios intentos fallidos. Complementa a Fail2ban: este actúa en la red bloqueando IPs, y pam_faillock frena localmente aunque el atacante cambie de IP.

  1. Activar el bloqueo:

    Editar /etc/pam.d/common-auth. Añadir esta línea al inicio:

    auth required pam_faillock.so preauth silent deny=5 unlock_time=900
    

    Y añadir estas dos entre la línea de pam_unix y la de pam_deny:

    auth [default=die] pam_faillock.so authfail silent deny=5 unlock_time=900
    auth sufficient pam_faillock.so authsucc silent deny=5 unlock_time=900
    

     

    El orden importa: el contador solo se resetea si authsucc se evalúa después de que pam_unix haya validado la contraseña. Si pones todas las líneas juntas al principio, authsucc (que es sufficient) aceptaría la sesión sin mirar la contraseña.

    Con estos ajustes, el bloque final debe leer así:

    auth required pam_faillock.so preauth silent deny=5 unlock_time=900
    auth [success=1 default=ignore] pam_unix.so nullok
    auth [default=die] pam_faillock.so authfail silent deny=5 unlock_time=900
    auth sufficient pam_faillock.so authsucc silent deny=5 unlock_time=900
    auth requisite pam_deny.so
    auth required pam_permit.so
    
    sudo authselect enable-feature with-faillock
    

    Ajustar en /etc/security/faillock.conf:

    deny = 5
    unlock_time = 900
    silent
    audit
    
  2. Validar que el fichero editado es válido cuando aplica:

    sudo pam-auth-update --package
    
    sudo authselect apply-changes
    

Con gestión desde la línea de comandos:

sudo faillock                 # estado de todas las cuentas
sudo faillock --user hosting  # estado de un usuario
sudo faillock --reset --user hosting  # desbloquear manualmente

 

Por defecto root queda fuera del conteo (even_deny_root apagado), lo que garantiza una vía de recuperación por consola si se bloquea una cuenta real.

Nivel 3 — Acceso por origen y horario

Opcional, según uso. Aplicar si quieres restringir desde dónde y cuándo se puede entrar por SSH — por ejemplo, solo desde tu IP/VPN y en horario laboral. Saltarlo si administras desde cualquier lugar o si prefieres no mantener una lista de IPs.

  1. Activar pam_access en SSH (ambas distros; el fichero sshd no lo gestiona authselect ni pam-auth-update):

    sudo vim /etc/pam.d/sshd
    

    Añadir al bloque account:

    account required pam_access.so
    
  2. Definir quién puede entrar desde dónde en /etc/security/access.conf:

    sudo vim /etc/security/access.conf
    

    Ejemplo — solo el grupo sshusers desde tu IP, tu VPN y la consola local:

    + : (sshusers) : 203.0.113.10 10.8.0.0/24 localhost
    - : ALL : ALL
    

    Reglas en orden

    La primera regla que coincide decide. + permite, - deniega. (grupo) referencia un grupo, y LOCAL hace match con terminales locales (no por red).

     

    Ten siempre una vía alternativa (consola física, o red de origen incluida) — si tu IP cambia y no la actualizas, te quedas fuera por SSH.

  3. (Opcional) Restringir por horario con pam_time. Añadir también en /etc/pam.d/sshd:

    account required pam_time.so
    

    Y definir la ventana en /etc/security/time.conf — el ejemplo deniega a hosting por SSH fuera de las 8:00–18:00:

    sshd ; * ; hosting ; !Al0800-1800
    

    Formato servicios ; terminales ; usuarios ; franjas; las franjas con ! se deniegan, las demás se permiten (Al = los días indicados, MoTuWeThFr = de lunes a viernes).

  4. Validar la configuración sin cerrar sesión:

    sudo pam-auth-update --package
    
    sudo authselect apply-changes
    

     

    Las reglas de access.conf y time.conf aplican en cuanto se guardan; no hay servicio que reiniciar. Prueba siempre en una sesión nueva antes de cerrar la actual.

Nivel 4 — Segundo factor TOTP

Opcional, avanzado. Añade un segundo factor a SSH: además de tu clave pública, necesitas un código que cambia cada 30 segundos (pam_oath). Su gracia se multiplica si ya aplicaste Proteger SSH: la pila queda clave + código, sin contraseña en juego.

Con pam_oath la tabla de secretos es central (/etc/users.oath) — limpio para un servidor con pocos administradores. Para varios usuarios con gestión individual, mira la alternativa pam_google_authenticator al final.

Riesgo de quedarte fuera

Este nivel es la capa que más puede dejarte fuera: si pierdes el teléfono o el secreto, no podrás autenticarte. Guarda una copia del secreto en tu gestor de contraseñas y mantén una vía de rescate por consola. Actívalo solo sobre un usuario cuyo acceso por clave ya funcione (Nivel 2 de proteger-ssh).

  1. Instalar pam_oath:

    sudo apt install libpam-oath oath-toolkit
    
    sudo dnf install oath-toolkit
    
  2. Crear la tabla de secretos:

    sudo sh -c 'umask 077; echo "HOTP/T30/6 hosting $(openssl rand 20 | base32 | tr -d \"=\")" > /etc/users.oath'
    

    Guarda el secreto (tercer campo de la línea que acaba de escribir):

    sudo grep hosting /etc/users.oath
    

    Añádelo a tu app de TOTP (Aegis, Authenticator, 1Password...) y verifica el código actual:

    oathtool --totp --base32 "$(awk '$2=="hosting"{print $3}' /etc/users.oath)"
    

    Debe coincidir con el de tu app. Para un segundo operador, repite añadiendo otra línea con su nombre.

     

    Formato de línea: HOTP/T30/6 usuario SECRETO_BASE32. El fichero se crea con umask 077 (solo root). Si quieres crearlo a mano: sudo vim /etc/users.oath + sudo chmod 600 /etc/users.oath.

  3. Enchufar el módulo en la pila de sshd (ambas distros, antes de cualquier @include o substack):

    sudo vim /etc/pam.d/sshd
    

    Añadir al bloque auth:

    auth required pam_oath.so usersfile=/etc/users.oath window=30 digits=6
    
  4. Obligar a usar clave más código en SSH — amplía el fichero 00-hardening.conf de Proteger SSH:

    sudo vim /etc/ssh/sshd_config.d/00-hardening.conf
    

    Añadir:

    AuthenticationMethods publickey,keyboard-interactive
    KbdInteractiveAuthentication yes
    

     

    PasswordAuthentication no (de proteger-ssh) se mantiene; el canal keyboard-interactive queda reservado para el reto TOTP, no para contraseñas. El servidor tiene que poder consultar la hora (NTP) — un reloj mal sincronizado invalida los códigos.

  5. Validar y recargar sin cerrar la sesión actual:

    sudo sshd -t
    sudo service sshd reload
    
  6. En una terminal nueva, confirmar que entras con clave + código:

    ssh hosting@<ip-servidor>
    

Alternativa: pam_google_authenticator

Si prefieres secretos por usuario en vez de una tabla central: instala libpam-google-authenticator (Ubuntu) o google-authenticator (Fedora), ejecuta google-authenticator como cada usuario (genera un QR para importar en la app) y añade en /etc/pam.d/sshd:

auth required pam_google_authenticator.so

La gestión es individual (~/.google_authenticator), cómoda para muchos usuarios y dependiente de que cada uno configure su secreto — donde la tabla central de pam_oath es más controlable para un servidor propio.

Referencia

Ficheros que quedan tocados por nivel:

Nivel Ficheros
1 /etc/security/pwquality.conf, common-password / system-auth
2 common-auth (Ubuntu) o authselect + /etc/security/faillock.conf (Fedora)
3 /etc/pam.d/sshd, /etc/security/access.conf, /etc/security/time.conf
4 /etc/pam.d/sshd, /etc/users.oath, sshd_config.d/00-hardening.conf

Verificar

  • Nivel 1 — como usuario hosting, intenta poner una contraseña débil con passwd; debe rechazarla indicando la política.
  • Nivel 2 — falla 5 veces el login de hosting y comprueba que sudo faillock lo marca bloqueado; desbloquea con sudo faillock --reset --user hosting.
  • Nivel 3 — desde una IP no incluida, ssh hosting@<ip-servidor> debe denegar el acceso sin pedir clave; desde tu IP permitida, entra normal. Fuera de la ventana de time.conf, también se deniega.
  • Nivel 4ssh hosting@<ip-servidor> pide la clave y después el código; un código erróneo corta la conexión.

Y ahora qué

La autenticación ya tiene política, bloqueo y opcionalmente segundo factor. El siguiente paso es mantener el servidor al día con las Actualizaciones automáticas.