Tener una higiene básica de seguridad es esencial, aunque es un proceso interminable y la seguridad perfecta no existe.

Este tema es muy extenso y está en constante evolución. Por eso decidí dividirlo en múltiples partes. Esta parte cubre únicamente el cifrado completo de disco, el bloqueo de cuentas de usuario y la configuración de PAM para la autenticación de usuarios.

linux-hardening.webp



Cifrado Completo de Disco con LUKS

El cifrado de datos en reposo es imprescindible. Si alguien obtiene acceso físico a tu hardware, el cifrado completo de disco es lo único que protege tus datos de ser leídos directamente desde la unidad.

Linux utiliza LUKS (Linux Unified Key Setup) para el cifrado de disco.


¿Qué es LUKS?

LUKS añade una capa de cifrado entre el dispositivo de bloque físico (la representación del disco duro dentro de Linux) y el sistema de archivos (ext4, xfs, btrfs…).

Utiliza el mapeador de dispositivos del kernel para crear un mapeo entre la unidad física cifrada y un volumen lógico descifrado. Cuando estás usando tu computadora, las aplicaciones solo ven un dispositivo descifrado regular en /dev/mapper/. No son conscientes del cifrado.

Cuando cifras una unidad, LUKS añade un encabezado con metadatos sobre el cifrado (cifrador, tamaño de clave, función de derivación de claves) y hasta 32 ranuras de claves en LUKS2. Estas ranuras de claves pueden almacenar frases de contraseña o archivos de claves que desbloquean el mismo volumen cifrado, por lo que pueden ser utilizadas por múltiples usuarios para descifrar la unidad usando sus propias credenciales almacenadas en ranuras separadas.

Ten en cuenta que para acceder a un volumen cifrado con LUKS, necesitas descifrarlo primero. Por lo tanto, los datos solo están cifrados cuando el volumen está cerrado (bloqueado manualmente o cuando apagas la computadora). Mientras estás trabajando con el volumen, los datos están descifrados en memoria.


LUKS1 vs LUKS2

En sistemas modernos LUKS2 es el predeterminado, que proporciona más ranuras de claves y algunas mejoras.

  • Hasta 32 ranuras de claves (vs 8 en LUKS1)
  • Derivación de claves Argon2 (resistente a ataques basados en GPU)
  • Soporte para cifrado autenticado
  • Mejor resistencia del encabezado
  • Soporte para re-cifrado en línea (cifrar datos existentes sin reformatear)

Funciones de Derivación de Claves

La función de derivación de claves determina cómo tu frase de contraseña se convierte en una clave de cifrado:

  • PBKDF2 (predeterminado en LUKS1): Rápido pero vulnerable a ataques de fuerza bruta acelerados por GPU
  • Argon2i (predeterminado en LUKS2): Algoritmo con uso intensivo de memoria que resiste ataques de GPU y ataques de tiempo de caché
  • Argon2id: Enfoque híbrido con la mejor protección general contra ataques de GPU y ataques de canal lateral

Configuración del Cifrado LUKS

No puedes cifrar una unidad que esté actualmente en uso. Necesitas o una unidad nueva o si usas una existente, respaldar los datos, formatear la unidad, cifrarla y luego restaurar.

La mayoría de las distribuciones de Linux ofrecen configuración de LUKS durante la instalación (generalmente solo haciendo clic en una casilla de verificación). Solo te pedirán que borres la unidad e ingreses una frase de contraseña. Este es el enfoque más fácil.

luks-encrypt-at-install.webp

fuente: imagen encontrada en fedoramagazine



Cifrar una Unidad Después de la Instalación

# Verificar si un dispositivo ya está cifrado con LUKS
cryptsetup isLuks -v /dev/sdb1

# Formatear el dispositivo con cifrado LUKS (se te pedirá una frase de contraseña)
cryptsetup luksFormat /dev/sdb1

# Usar cifrador y tamaño de clave específicos para más control
cryptsetup luksFormat --cipher aes-xts-plain64 --key-size 512 /dev/sdb1

# Especificar función de derivación de claves
cryptsetup luksFormat --type luks2 --pbkdf argon2id --pbkdf-memory 1048576 /dev/sdb1


Crear Volumen LUKS con Encabezado Separado

Almacenar el encabezado LUKS por separado es más seguro. Sin acceso al archivo de encabezado externo, la unidad cifrada permanece inaccesible. Esto es algo extremo, pero puedes almacenar el encabezado en una unidad USB.

# Crear LUKS con encabezado separado
cryptsetup luksFormat /dev/sdb1 --header /root/luks-header.img

# Abrir con encabezado separado
cryptsetup luksOpen /dev/sdb1 encrypted-data --header /root/luks-header.img

Trabajando con LUKS

Ver Versión Actual de LUKS

# Verificar versión de LUKS
cryptsetup luksDump /dev/sdb1 | grep Version


Respaldar Encabezado LUKS

Si el encabezado LUKS se corrompe (sector defectuoso, corrupción del sistema de archivos) tus datos cifrados serán irrecuperables. Respalda tu encabezado LUKS inmediatamente después de cifrar cualquier unidad.

# Respaldar encabezado LUKS (hazlo inmediatamente después del cifrado)
cryptsetup luksHeaderBackup /dev/sdb1 --header-backup-file /root/luks-header-backup.img

# Almacenar respaldo en medios separados (unidad USB, gestor de contraseñas, almacenamiento en la nube)
# NO lo almacenes en la unidad cifrada misma

# Restaurar encabezado corrupto
cryptsetup luksHeaderRestore /dev/sdb1 --header-backup-file /root/luks-header-backup.img

# Verificar integridad del encabezado
cryptsetup luksDump /dev/sdb1


Abrir Volumen LUKS Manualmente

Una vez que la unidad está cifrada, necesitas abrir el volumen LUKS para acceder a su contenido.

# Abrir el volumen LUKS con un nombre de mapeador de dispositivos (crea /dev/mapper/encrypted-data)
cryptsetup luksOpen /dev/sdb1 encrypted-data

# Formatear el volumen descifrado con un sistema de archivos (aquí, xfs)
mkfs.xfs /dev/mapper/encrypted-data

# Montar el volumen como cualquier otro dispositivo
mount /dev/mapper/encrypted-data /mnt/secure


Cerrar Volumen LUKS Manualmente

Cierra el volumen LUKS abierto para restringir el acceso. Esto elimina la entrada del mapeador de dispositivos para asegurar que el contenido ya no sea accesible.

# Desmontar el sistema de archivos primero
umount /mnt/secure

# Cerrar el volumen LUKS
cryptsetup luksClose encrypted-data

# Sintaxis alternativa más corta
cryptsetup close encrypted-data


Trabajando con Ranuras de Claves

Puedes añadir claves a diferentes ranuras de claves para tener múltiples métodos de descifrado (esto es útil para sistemas multiusuario o recuperación).

Yo solo uso la ranura 0 para mi frase de contraseña principal y la ranura 1 para recuperación (guardada en el gestor de contraseñas).

# Ver ranuras de claves actuales y su estado
cryptsetup luksDump /dev/sdb1 | grep "Key Slot"

# Añadir una nueva frase de contraseña a una ranura disponible
cryptsetup luksAddKey /dev/sdb1

# Añadir un archivo de claves en lugar de una frase de contraseña
cryptsetup luksAddKey /dev/sdb1 /path/to/keyfile

# Añadir a un número de ranura específico
cryptsetup luksAddKey --key-slot 2 /dev/sdb1

# Cambiar una frase de contraseña existente
cryptsetup luksChangeKey /dev/sdb1

# Eliminar una ranura de claves
cryptsetup luksKillSlot /dev/sdb1 1


Montaje Automático con Archivos de Claves

Añade archivos de claves para montar automáticamente la unidad al arrancar (esto se configura automáticamente si añades cifrado LUKS en la instalación).

Crear archivo de claves.

# Generar un archivo de claves aleatorio de 4096 bytes
dd if=/dev/urandom of=/root/luks-keyfile bs=4096 count=1

# Restringir permisos (crítico—prevenir acceso no autorizado)
chmod 400 /root/luks-keyfile

# Añadirlo al volumen LUKS
cryptsetup luksAddKey /dev/sdb1 /root/luks-keyfile

Configurar descifrado automático en /etc/crypttab.

# Sintaxis: nombre dispositivo archivo_claves opciones
encrypted-data  /dev/sdb1  /root/luks-keyfile  luks

Añadir la entrada de montaje automático en /etc/fstab.

# Usar el nombre del mapeador de dispositivos en /etc/fstab
/dev/mapper/encrypted-data  /mnt/secure  xfs  defaults  0 0

El volumen LUKS ahora se descifra y monta automáticamente al arrancar. Los datos permanecen cifrados cuando el sistema está apagado o durante las etapas previas al arranque.



Swap Cifrado

Si estás usando espacio swap, también debes cifrarlo. De lo contrario, los datos sensibles que se paginan al swap durante tu sesión permanecen allí sin cifrar en el disco.

# Clave aleatoria para swap (regenerada en cada arranque)
# Añadir a /etc/crypttab
swap    /dev/sda2    /dev/urandom    swap,cipher=aes-xts-plain64,size=512

# En /etc/fstab
/dev/mapper/swap    none    swap    defaults    0 0


Seguridad de Cuentas de Usuario

Deshabilitar el Login de Root

Ejecutar como root no es una buena idea. Deshabilita la cuenta root y solo usa un usuario regular con privilegios sudo en su lugar.

Esto previene que cualquiera inicie sesión como root, ya sea en la consola o vía SSH. Aún puedes obtener privilegios root a través de sudo o usando sudo -i para un shell root interactivo.

# Bloquear la cuenta root
passwd -l root

# Denegar login de root vía SSH /etc/ssh/sshd_config
vim /etc/ssh/sshd_config
PermitRootLogin no

Tiempos de Espera de Sesión

Cerrar sesión automáticamente de usuarios inactivos en /etc/profile.

# Añadir a /etc/profile
TMOUT=900  # Cerrar sesión automáticamente después de 15 minutos de inactividad
readonly TMOUT
export TMOUT

Historial de Contraseñas

Prevenir que los usuarios ciclen y reutilicen contraseñas recientes. La mayoría de las distribuciones recuerdan las últimas 10 contraseñas por defecto, así que puedes aumentar este número para prevenir que los usuarios reutilicen contraseñas antiguas. Un poco extremo para mí pero bueno saberlo.

Cambiar la configuración en /etc/security/pwhistory.conf.

# Aumentar a 24 (común en entornos Linux empresariales)
remember = 24
enforce_for_root

Restringir Permisos de Directorio Home

Los permisos predeterminados del directorio home en algunas distribuciones son demasiado permisivos, ya que los usuarios “otros” a menudo pueden leer archivos en tu directorio home.

Cambiar permisos existentes del directorio home.

# Eliminar acceso de lectura para otros
chmod o-r $HOME

# Más explícitamente, establecer acceso solo para el propietario
chmod 700 $HOME

Establecer valores predeterminados restrictivos para nuevos usuarios en /etc/login.defs.

# Establecer umask restrictivo para nuevos archivos
UMASK 077

# Establecer permisos del directorio home solo para el propietario
HOME_MODE 0700

Establecer umask restrictivo para nuevos archivos de usuarios existentes en ~/.bashrc (o mejor, en el directorio drop-in ~/.bashrc.d/).

# Establecer umask para nuevos archivos creados por este usuario
umask 027 

# O más restrictivo (acceso solo para el propietario)
umask 077

Configurar Acceso Sudo

sudo permite a usuarios configurados ejecutar comandos con privilegios elevados o como otro usuario sin conocer la contraseña del usuario objetivo. Asegúrate de que esto esté configurado correctamente.

Editar /etc/sudoers con visudo, que valida la sintaxis antes de guardar.

# Editar /etc/sudoers
visudo

# Mejor: crear un archivo de configuración personalizado en el directorio drop-in de sudoers
visudo -f /etc/sudoers.d/custom

Algunas buenas configuraciones de seguridad para sudo:

# Requerir contraseña para cada comando sudo
Defaults timestamp_timeout=0

# O establecer un tiempo de espera de 5 minutos para volver a pedir la contraseña al usuario si es muy molesto
Defaults timestamp_timeout=5

# Usar alias de sudo
# Sintaxis: User_Alias NOMBRE = usuario1, usuario2, %grupo1, ...
User_Alias ADMINS = alice, bob, %sysadmin

# Sintaxis: Host_Alias NOMBRE = host1, host2, ...
Host_Alias PRODUCTION = prod-web01, prod-db01, 10.0.1.0/24

# Sintaxis: Cmnd_Alias NOMBRE = /ruta/al/comando1, /ruta/al/comando2, ...
# ¡Muy importante! Cuando los comandos se listan con argumentos específicos, solo se permiten esas combinaciones exactas de argumentos. Además, no uses comodines ya que por ejemplo, un comando como `/usr/bin/systemctl * sshd` permite `systemctl stop sshd`, `systemctl restart sshd`, etc. ¡Peligroso!
Cmnd_Alias SERVICES = /usr/bin/systemctl, /usr/sbin/service
Cmnd_Alias SOFTWARE = /usr/bin/dnf, /usr/bin/yum, /usr/bin/rpm

# Todo junto, regla sudo usando alias
ADMINS PRODUCTION=(ALL) SOFTWARE, SERVICES

Verificar los privilegios sudo de tu usuario.

# Listar tus propios privilegios sudo
sudo -l

Política de Antigüedad de Contraseñas

Establecer antigüedad de contraseñas para nuevos usuarios en /etc/login.defs.

# Antigüedad máxima de contraseña en días
PASS_MAX_DAYS   90

# Días mínimos entre cambios de contraseña (previene el ciclo inmediato de vuelta a contraseñas antiguas)
PASS_MIN_DAYS   1

# Días antes de la expiración para advertir al usuario
PASS_WARN_AGE   7

# Longitud mínima de contraseña (configuración heredada, usar pwquality en su lugar)
PASS_MIN_LEN    8

Configurar antigüedad de contraseñas para usuarios existentes:

# Ver configuración actual de antigüedad de contraseñas
chage -l username

# Establecer que la contraseña expire en 90 días
chage -M 90 username

# Establecer días mínimos entre cambios de contraseña
chage -m 7 username

# Forzar cambio de contraseña en el próximo login
chage -d 0 username

# Establecer fecha de expiración de cuenta
chage -E 2025-12-31 username

# Modo de configuración interactivo
chage username

Entendiendo PAM

Entender (al menos a alto nivel) PAM es necesario.

PAM es un marco de autenticación centralizado que muchas aplicaciones usan para verificar credenciales de usuario. Sin PAM, cada aplicación implementaría su propia lógica de autenticación.


El proceso de autenticación PAM funciona así:

  1. La aplicación solicita autenticación a través de la API de PAM
  2. PAM lee la configuración de /etc/pam.d/ para ese servicio específico
  3. PAM carga y ejecuta los módulos configurados en orden
  4. Cada módulo realiza su verificación de autenticación específica
  5. PAM combina los resultados y le dice a la aplicación éxito o fallo

Los módulos PAM se dividen en cuatro tipos:

  • auth - Verificar credenciales (contraseñas, tokens, biometría, YubiKeys)
  • account - Verificar estado de cuenta (expiración, horarios de acceso, bloqueos)
  • password - Manejar cambios de contraseña y aplicar reglas de complejidad
  • session - Configurar y desmantelar sesiones de usuario (montar directorios home, establecer límites)

Las banderas de control PAM determinan qué sucede cuando un módulo tiene éxito o falla:

  • requisite - Debe tener éxito o la autenticación falla inmediatamente, no se verifican más módulos
  • sufficient - Si esto tiene éxito, se omiten los módulos restantes de este tipo
  • required - Debe tener éxito, pero PAM continúa verificando otros módulos independientemente
  • optional - El resultado del módulo solo importa si es el único módulo en la pila
  • include - Incluir todas las líneas del tipo dado desde otro archivo de configuración

Aplicar Complejidad de Contraseñas

Complejidad de Contraseñas con pwquality

El módulo PAM pam_pwquality aplica requisitos de complejidad de contraseñas a través de PAM. Utiliza un sistema de “créditos” para definir la fortaleza de la contraseña (tendrás que especificar el número requerido de dígitos, letras mayúsculas, letras minúsculas y caracteres especiales).

Cómo funciona es algo confuso pero, por ejemplo, para minlen = 12 y estableces dcredit = 1, una contraseña con un dígito obtiene 1 crédito hacia la longitud mínima, por lo que necesita 11 caracteres regulares más el dígito. Pero si ucredit = -1, la mayúscula es requerida pero no reduce el requisito de longitud, aún necesitas 12 caracteres en total incluyendo la mayúscula.


Configurarlo en /etc/security/pwquality.conf.

# Aclaración del sistema de créditos:
# Valor positivo (dcredit = 1): "Si la contraseña tiene 1+ dígitos, reducir minlen en 1"
# Valor negativo (ucredit = -1): "La contraseña DEBE tener 1+ mayúscula, sin crédito de longitud"

# Ejemplo con minlen = 12:
# dcredit = 1  → Contraseña con dígito puede ser de 11 caracteres en total
# ucredit = -1 → Contraseña debe tener mayúscula Y aún ser de 12 caracteres

dcredit = -1   # Requerir al menos 1 dígito
ucredit = -1   # Requerir al menos 1 mayúscula
lcredit = -1   # Requerir al menos 1 minúscula
ocredit = -1   # Requerir al menos 1 carácter especial

# Longitud mínima de contraseña (considerando créditos)
minlen = 12 

# Máximo de caracteres idénticos consecutivos
maxrepeat = 2

# Máximo de caracteres consecutivos de la misma clase de caracteres
maxclassrepeat = 3

# Número de intentos de entrada de contraseña antes de rendirse
retry = 3

# Mínimo de caracteres diferentes de la contraseña antigua
difok = 5

# Verificar contra palabras del diccionario
dictcheck = 1

# Rechazar contraseñas que contengan el nombre de usuario
usercheck = 1

# Aplicar reglas para usuario root (0=sí, 1=no)
enforce_for_root = 0


Integrar pwquality con PAM

Asegurar que PAM use el módulo pwquality verificando /etc/pam.d/passwd y /etc/pam.d/system-auth.

# En /etc/pam.d/passwd
password    requisite     pam_pwquality.so retry=3
password    sufficient    pam_unix.so sha512 shadow try_first_pass use_authtok
password    required      pam_deny.so

# En /etc/pam.d/system-auth
password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3
password    sufficient    pam_unix.so sha512 shadow try_first_pass use_authtok
password    required      pam_deny.so

Prevenir Ataques de Fuerza Bruta

El módulo PAM pam_faillock protege contra ataques de contraseña de fuerza bruta bloqueando cuentas después de intentos de autenticación fallidos consecutivos. Esto es importante para cualquier sistema, incluso si no está expuesto a la red. Ten en cuenta que esto solo protege para logins basados en PAM (por ejemplo, no aplica para modo de recuperación).


Configurar el comportamiento de faillock en /etc/security/faillock.conf.

# Directorio para registros de intentos fallidos
dir = /var/run/faillock

# Número de intentos fallidos antes del bloqueo
deny = 4

# Duración del bloqueo en segundos (1200 = 20 minutos)
unlock_time = 1200

# Aplicar bloqueo a la cuenta root
even_deny_root

# Duración del bloqueo específica para root (si even_deny_root está establecido)
root_unlock_time = 600

# Habilitar registro de auditoría
audit

# Bloquear cuentas si faillock no puede escribir en el directorio de registro
silent

Probar la configuración.

# Intentar iniciar sesión con contraseña incorrecta múltiples veces
ssh username@localhost

# Verificar si la cuenta está bloqueada
faillock --user username

# Verificar que aún puedes desbloquear
sudo faillock --user username --reset


Habilitar Faillock con Authselect

En RHEL y Fedora, usa perfiles de authselect en lugar de editar manualmente archivos PAM. Esto previene conflictos de configuración y asegura consistencia entre archivos de servicio PAM.

# Mostrar perfil authselect actual
authselect current

# Listar perfiles disponibles
authselect list

# Listar características de un perfil
authselect list-features sssd

# Habilitar característica faillock
authselect enable-feature with-faillock

# Aplicar cambios
authselect apply-changes

Authselect modifica /etc/pam.d/system-auth y /etc/pam.d/password-auth para incluir verificaciones faillock. La configuración se ve así.

# Sección Auth - verificar bloqueo antes de la autenticación
auth        required      pam_faillock.so preauth silent
auth        sufficient    pam_unix.so nullok try_first_pass
auth        [default=die] pam_faillock.so authfail
auth        required      pam_deny.so

# Sección Account - aplicar bloqueo
account     required      pam_faillock.so
account     required      pam_unix.so


Ver Intentos de Login Fallidos

# Mostrar todos los intentos fallidos en todo el sistema
faillock

# Mostrar intentos fallidos para usuario específico
faillock --user username

# Información más detallada
faillock --user username --verbose

Login con YubiKey

He añadido reglas PAM para iniciar sesión vía YubiKey para más seguridad.

Más detalles aquí: Using YubiKey for Local Linux Authentication



¿Qué Sigue?

Al final, el endurecimiento de seguridad y la privacidad no son perfectos. Hay medidas extremas que te hacen muy seguro, pero hacen que usar el sistema sea doloroso. Este es un buen equilibrio entre usabilidad y seguridad.

Las próximas partes cubrirán SSH, redes, kernel, LSM (específicamente SELinux), systemd, configuración de firewall…