Seguridad

Trabajando con unidades cifradas

Trabajando con unidades cifradas

A propósito de LUKS

El inquietante "Octopus Anti-monopoly", del artista americano George Luks, 1899

El inquietante «Octopus Anti-monopoly», del artista americano George Luks, 1899

LUKS, además de un peculiar artista norteamericano, son las siglas para «Linux Unified Key Setup», que vendría a ser un conjunto de especificaciones para el cifrado de discos duros. Aunque su nombre deja claro que fue creado inicialmente para trabajar en entornos Linux, la realidad es que su implementación ha originado un estándar que es en la práctica independiente de plataforma y que puede ser utilizado por diferentes herramientas en sistemas diferentes. Concretamente, LUKS trabaja sobre dispositivos de bloque, por lo que el contenido del mismo resulta arbitrario, pudiendo funcionar con cualquier sistema de archivos. Además, aunque el cifrado del volumen se realiza con una única clave maestra, LUKS proporciona un sistema para que múltiples usuarios con distintas claves puedan acceder a dicha clave maestra. Para ello, cada volumen LUKS dispone de una cabecera con un conjunto de ranuras (slots), cada una de las cuales puede almacenar una clave de cifrado junto con los parámetros necesarios para el manejo de la misma. El número de ranuras dependerá de la versión de implementación de LUKS, siendo 8 lo más habitual. Cada clave almacenada en cada ranura permitirá el acceso a la clave maestra y, por lo tanto, el descifrado del volumen, con la ventaja de que estas claves individuales pueden ser añadidas y eliminadas a discreción, lo que permite gestionar la estrategia de accesos al volumen sin que el cifrado maestro se vea afectado..

LUKS puede ser utilizado tanto para cifrar particiones individuales como sistemas de archivos completos, incluyendo la partición de arranque. Esto es especialmente útil en portátiles, teniendo en cuenta el riesgo que supone para nuestra privacidad cualquier percance que pudiera terminar con nuestros preciados datos en manos de desconocidos.

Para el cifrado del sistema de archivos completo los asistentes de instalación de la mayoría de las distribuciones de Linux suelen incluir una opción para realizarlo de manera transparente durante la instalación, por lo que no nos vamos a detener en este punto.

En nuestro caso nos centraremos en el cifrado de un disco completo al margen de nuestro disco de sistema, ya sea porque se trate de un disco externo con copias de seguridad que queremos llevar de aquí para allá, ya sea porque queremos que los datos del disco secundario de nuestro ordenador de escritorio estén a buen recaudo.

El cifrado

Para empezar, lo primero es asegurarnos de que tenemos instalado el paquete «cryptsetup»

sudo apt install cryptsetup

Y, a continuación, ciframos el disco completo:

sudo cryptsetup -v -y luksFormat /dev/sda

Tras lo cual nos pedirá una frase de contraseña

Observar que como estamos cifrando el disco duro completo el nombre de dispositivo corresponde a un volumen entero, no a una partición

Una vez cifrado el disco duro podremos darle el formato deseado (recordemos que, a diferencia de otros sistemas de cifrado de disco duro, LUKS es totalmente independiente del formato y compatible con cualquier formato de disco)

Montar

Supongamos que tenemos un disco duro externo para copias de seguridad que, al conectarlo, se muestra en /dev/sdb, y que queremos descifrarlo y montarlo en el directorio /mnt/external

Comenzamos utilizando el comando cryptsetup para abrir el volumen cifrado y añadirlo al mapeador de dispositivos (Device Mapper) con el nombre que elijamos, en este caso, «encrypted_volume».

sudo cryptsetup luksOpen /dev/sdb encrypted_volume

Si aún no hemos creado un punto de montaje, para el caso de nuestro disco duro externo…

sudo mkdir /mnt/external

… y montamos utilizando el nombre previamente asignado en el mapeador

sudo mount /dev/mapper/encrypted_volume /mnt/external

Desmontar

Lo mismo pero al revés. Desmontamos el volumen y cerramos el volumen cifrado.

sudo umount /mnt/external
sudo cryptsetup luksClose encrypted_volume

Montaje automático

Si lo que queremos es que el montaje de la unidad cifrada se realice automáticamente al arrancar el sistema necesitamos realizar los dos pasos configurando los respectivos archivos crypttab y fstab. En este caso se tratará de un disco interno que se muestra en /dev/sda y que queremos montar en /mnt/datos

  1. En primer lugar, añadimos el dispositivo cifrado a la tabla del archivo /etc/crypttab, especificando el nombre que queramos y el archivo de dispositivo correcto.
    sudo nano /etc/crypttab
    # <target name> <source device> <key file> <options>
    encrypted_volume /dev/sda   none
    

    La opción «none» indica que no queremos especificar un archivo de clave.
    En este momento podemos reiniciar la máquina y ver qué pasa: Durante el arranque, el sistema intentará abrir el dispositivo cifrado y al no disponer de un archivo de clave nos la pedirá de manera interactiva. Una vez introducida el volumen quedará descifrado, mapeado y listo para ser montado.

  2. Sin embargo, como es lógico, querremos que el dispositivo se monte de manera automática en un directorio de nuestra elección. Esto lo especificamos, como siempre, en el archivo fstab de la manera habitual. Únicamente que, en lugar de referirnos al dispositivo físico, lo haremos al volumen previamente descifrado y disponible en el mapeador de dispositivos.
    sudo nano /etc/fstab
    # disco cifrado
    /dev/mapper/encrypted_volume /mnt/datos ext4 defaults 0 2

    Ahora podemos reiniciar de nuevo. El sistema nos mostrará un diálogo para pedirnos la contraseña y, al finalizar el arranque, el disco estará montado y disponible en nuestro sistema de archivos.

Montaje automático desatendido

Puede interesarnos que el montaje de la unidad cifrada se realice sin necesidad de introducir manualmente la contraseña. Esto es particularmente útil cuando queremos iniciar servidores de forma remota o automática. Existen varias maneras de implementar esta función, pero la más sencilla consiste en  utilizar un fichero de clave almacenado en el propio servidor.

Que quede claro: el fichero puede ser cualquiera y tener cualquier contenido. Es habitual crear largos archivos con una sucesión aleatoria de datos pero, sinceramente, si alguien se hace con el fichero, tanto da la complejidad del mismo. Para que el fichero pueda ser utilizado para descifrar el disco deberá ser añadido a una de las ranuras de la cabecera del volumen LUKS

Por convención, guardaremos el fichero en la siguiente ruta:

/root/luks_keyfile

A continuación añadimos el fichero a las claves de LUKS

sudo cryptsetup luksAddKey /dev/sda/ /root/luks_keyfile

Nos pedirá una contraseña válida para este volumen.

Ahora, para que el descifrado se realice con dicho fichero, sólo queda especificarlo en el archivo crypttab en lugar de «none»

sudo nano /etc/crypttab
# <target name> <source device> <key file> <options>
encrypted_volume /dev/sda /root/luks_keyfile

Y… ya está.

Nota: En tanto que cualquier fichero puede servir de clave y, en este caso, debe residir necesariamente en el servidor, es una buena idea disimular un poco de qué fichero se trata. En este ejemplo se lo hemos puesto a huevo a cualquiera que se quiera hacer con el contenido de nuestro disco duro (aunque si nos roban el servidor, teniendo en cuenta que se monta solo, tampoco harían falta muchas luces). En cualquier caso, si lo que queremos es ponerlo un poquito más difícil a un ladrón despistado, la posibilidad más obvia es utilizar un fichero arbitrario con un nombre arbitrario y un contenido arbitrario guardado en un lugar arbitrario. Ah! y no debemos tener miedo de perderlo, ya que la contraseña que hayamos utilizado para cifrar inicialmente el volumen siempre nos va a servir.

Publicado por David en Linux, Seguridad, 0 comentarios
Securizar un stake pool

Securizar un stake pool

Lo primero, el servicio SSH

Lo primero que tenemos que hacer para estar razonablemente seguros de que nuestro stake pool se encuentra seguro ante posibles intrusiones es ajustar el servicio SSH a nuestras necesidades reales. en nuestro caso, utilizamos una autenticación de dos factores mediante un par de claves pública y privada y el servicio de autenticación de Goggle. Tampoco estaría de más restringir las direcciones desde las cuales vamos a autorizar las conexiones al servicio SSH, siempre que esto sea posible. Lógicamente, si estamos en una red privada o una VPN en la que disponemos de direcciones privadas bien definidas, o nos conectamos desde accesos con IP’s públicas fijas, esta opción es inmejorable. Sin embargo, si necesitamos conectarnos desde lugares más o menos arbitrarios con IP’s públicas o privadas asignadas dinámicamente, esta estrategia no es posible.

Como siempre, no existe un plan de seguridad estándar perfecto. El mejor plan de seguridad es el que se adapta de manera adecuada a la estructura de nuestro sistema y a las circunstancias personales de cada cual.

Después, el firewall

Lo siguiente es restringir el tráfico a nuestros ordenadores mediante el firewall. Como la mayoría de operadores de Stake Pools, he elegido la opción de ufw (Uncomplicated FireWall) frente a la configuración de IPtables. Sé que los más avezados expertos en Linux no tardarán en advertir que si IPtables existe es por algo, y que su mayor complejidad ofrece la contrapartida de un control mucho más preciso y robusto de las conexiones del PC. Y tendrán toda la razón. En mi descargo diré que también es bastante más lioso de configurar y, por lo tanto, mucho más probable que en manos de un zarpas como yo se deslicen gazapos que puedan convertirse en brechas de seguridad.

Es importante entender que la seguridad real implica un compromiso entre la robustez de la herramienta y la habilidad para manejarla de manera correcta. No hay nada más peligroso que un AK-47 en manos de un chimpancé.

Y, la verdad, es que ufw cumple con este cometido, al menos para mí. Si un día me decido a estudiar IPtables a fondo, os prometo que seréis los primeros en saberlo.

Dicho esto, lo primero será asegurarnos de que tenemos ufw instalado en nuestro equipo, lo cual no es difícil porque en la mayoría de distribuciones suele venir por defecto. En cualquier caso, basta con teclear:

sudo apt install ufw

Reading package lists... Done
Building dependency tree       
Reading state information... Done
ufw is already the newest version (0.36-6).
ufw set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Lo cual nos demuestra que, efectivamente, ya está instalado. Cuando hacemos esto, nos encontramos con el mensaje de que el paquete en cuestión ha sido marcado como instalado manualmente, lo cual a mí, que soy un maniático, me ralla un poco. Si quieres saber más, mira esta otra entrada.

Volviendo al firewall, lo primero que comprobamos es su estado

sudo ufw status
Status: inactive

Es decir, que viene deshabilitado por defecto. Lo cual es conveniente para evitar cortar servicios que pudieran estar en ejecución antes de poder configurarlo. Sin ir más lejos, si estamos conectados a la máquina por SSH y habilitamos el firewall ya podéis imaginar lo que iba a pasar (spoiler: la conexión se corta sin posibilidad de volver a conectar hasta poder acceder físicamente)

Así que lo primero de todo, habilitaremos la conexión al servicio ssh

sudo ufw allow ssh
Rules updated
Rules updated (v6)

Como vemos, esta es una manera rápida de habilitar conexiones al ordenador, basándonos en servicios, en lugar de puertos.

Nota: En muchos manuales veremos que es interesante como medida adicional de protección modificar el puerto por defecto utilizado por el servicio ssh (que pertenece a ese grupo de puertos denominados como «bien conocidos» y que corresponde siempre al 22). En mi opinión, se trata de una medida más incómoda que eficaz. Cualquier atacante puede imaginar que el servicio ssh se puede estar ejecutando en cualquier otro puerto y el primer paso antes de probar nada suele ser un escaneo de puertos para averiguar qué tenemos por ahí. Y lo mismo vale para cualquier servicio ejecutado en uno de los susodichos «well known ports»

Ahora sí, habilitamos el firewall

sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

Y como era de esperar en un programa bien implementado y dirigido a dummies como yo, nos advierte de los peligros antes de hacernos caso.

Comprobemos las reglas que tenemos hasta ahora:

sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere 
22/tcp (v6)                ALLOW       Anywhere (v6)       

Efectivamente, el bien conocido puerto 22 accesible para el protocolo tcp desde cualquier lugar. Eso es lo que ha mantenido nuestra conexión activa. Nuevamente, si sabemos desde qué direcciones nos vamos a conectar, éste sería otro buen lugar para restringir el acceso desde esas direcciones, además de la configuración del propio servicio ssh. Cuantas más puertas, mejor.

Bien, si ya teníamos nuestro stake pool en funcionamiento con un núcleo y un relé hablando entre sí, será interesante lanzar la aplicación gLiveView para ver lo que ha pasado. Y es que, si todo va bien, las dos máquinas ya no podrán hablar entre sí. Al menos, el relé ya no podrá acceder al núcleo, ya que éste está restringiendo todas las conexiones entrantes. Lógicamente, nos interesa añadir una regla que permita esta comunicación. Será la siguiente:

sudo ufw allow proto tcp from <IP_RELAY> to any port <PORT_KERNEL>

En cristiano, permitir toda conexión entrante en protocolo tcp desde la IP del relé al puerto en el cual se ejecuta el nodo del núcleo. Sencillo, ¿no?

Comprobemos…

sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere                  
<PORT_KERNEL>/tcp          ALLOW       <IP_RELAY>             
22/tcp (v6)                ALLOW       Anywhere (v6) 

Y para evitar paranoias, nada como un túnel SSH

Cualquier manual básico de Cardano nos explica que la necesidad de disponer un cierto número de relés además del nodo productor de bloques (o núcleo) tiene que ver con una cuestión básica de seguridad: el núcleo jamás debería estar directamente expuesto a la red. De este modo, los únicos nodos que pueden ser comprometidos son los relés, y si disponemos de más de uno, bastaría con apagarlo y usar los otros mientras arreglamos el desaguisado. Pero claro, la primera pregunta que surge es… y si el núcleo no está abierto a la Internet… ¿cómo diablos me conecto a él para configurarlo? Si te has hecho esta pregunta, felicidades: vas por el buen camino (y estás jodido).

Y ahora, veamos las distintas respuestas:

1.- ¡Qué le vamos a hacer!

Dejaremos el puerto ssh abierto con un porrón de medidas de seguridad, especiamente, autenticación multifactorial (MFA) y cruzaremos los dedos.

Bien, no está mal. Conozco a unos cuantos que han seguido esta política y aún no les ha pasado nada. Afortunadamente, un servicio ssh bien configurado es muy seguro.

2.- Restringimos también el acceso ssh a través del relé.

Es decir, nos conectaremos mediante ssh al relé, y de allí al núcleo. Es una medida muy sensata. Y lo único que requiere es modificar un poquito el firewall para que el acceso al núcleo quede restringido únicamente al relé.

Añadimos una regla por aquí… (la que nos permitirá el acceso al núcleo desde el relé)

sudo ufw allow proto tcp from <IP_RELAY> to any port 22

Con lo cual:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere                  
6000/tcp                   ALLOW       <IP_RELAY>             
22/tcp                     ALLOW       <IP_RELAY>             
22/tcp (v6)                ALLOW       Anywhere (v6)   

Y eliminamos otra por allá… (la que permitía el acceso ssh indiscriminado)

sudo ufw delete 1
Deleting:
 allow 22/tcp
Proceed with operation (y|n)? y
Rule deleted

Y ya estaría. Sin embargo, esta estrategia plantea un nuevo problemilla nada trivial. Como comentábamos en el apartado relativo a la conexión segura con SSH, si ya era importante no dejar la llave privada en cualquier máquina local, aún más lo será no dejarla permanentemente en el relé para cuando necesitemos realizar la conexión al núcleo desde allí. La solución de copiarla mediante scp y borrarla cada vez puede ser una opción, pero desde luego resulta lo bastante incómoda como para caer en la muy humana tentación de dejarla almacenada en el relé y confiar en el sistema 2FA. Aunque, si todo este guirigay de relés tienen que ver con considerar la posibilidad de que se estos puedan verse comprometidos, ya no parece tan buena opción, ¿verdad?. Afortunadamente, aún existe una tercera opción (y más que habrá, pero hasta aquí llega mi cacumen por hoy).

3. Crear un túnel ssh desde la máquina local al núcleo a través del relé

Y una vez establecido dicho túnel, conectarnos directamente desde la máquina local al núcleo. Tenemos como prerequisito que la máquina núcleo sea accesible únicamente desde la máquina relé, lo cual ya ha quedado descrito en la opción número 2. La magia vienen a continuación:

En primer lugar, creamos un túnel a través del relé que redirija un puerto de nuestra máquina local a otro puerto de la máquina núcleo.

ssh -L 8022:<IP_CORE>:22 <user_relay>@<IP_RELAY> -i /route-to-key/private-key

Este comando crea un túnel desde el puerto 8022 de mi máquina local al puerto 22 de la máquina core. Utilizamos el puerto local 8022 ya que el puerto 22 suele estar ocupado por el servidor ssh local y es preferible utilizar uno libre para evitar conflictos. Los datos de conexión son los mismos que usamos para conectar al relé: el nombre de usuario y su IP. Y, por supuesto, la clave pública que guardamos en nuestro pendrive, y que se especifica con el parámetro -i (identidad). Si todo va bien, el único dato que nos pedirá será el código del Google Authenticator y el túnel quedará establecido.

Desde ahora, cualquier petición al puerto 8022 a la máquina local será como si la realizáramos directamente al puerto 22 del nucleo, por lo que podremos conectar simplemente así:

ssh  -p 8022 <user>@127.0.0.1 -i /route-to-key/private-key 

Es decir, que en realidad nos estamos conectando a nuestra propia máquina en el puerto 8022. Y, por supuesto, el archivo de clave privada permanece a buen recaudo en nuestro preciado pendrive. ¿A que mola?

Publicado por David en Cardano, Linux, Seguridad, SSH, 0 comentarios
Acceso remoto SSH con clave pública y Google 2FA

Acceso remoto SSH con clave pública y Google 2FA

Las dos maneras más populares de asegurar una conexión SSH son el acceso mediante clave pública y la autenticación en dos pasos (2FA). La primera es la que más tradición tiene y la que llevo años utilizando en mis máquinas domésticas. La segunda es algo más reciente y nunca la había utilizado antes, así que me apeteció probarla para aumentar la seguridad. Hay decenas de tutoriales que explican el procedimiento, bastante sencillo, por cierto. Pero como el objeto de este blog es facilitarme las cosas a mí mismo y evitarme el ponerme a rebuscar, aquí dejo el enlace a la guía oficial de Ubuntu sobre el tema: Configure SSH to use 2FA

La verdad es que el acceso mediante 2FA es una chulada. Metes la contraseña y te pide un código del Google Authenticator. Sin embargo, una vez disipada la ilusión inicial pensé que no estaría de más sustituir el paso de introducir la contraseña por una autenticación mediante clave pública. Mucho más cómodo y, sobre todo, mucho más seguro. Así que configuré un par de claves RSA para entrar al servidor (proceso que aunque he realizado decenas de veces siempre se me olvida, así que también lo he dejado descrito por aquí).

La sorpresa vino cuando intenté la conexión. El hecho de haber configurado el acceso mediante clave hizo que la entrada al servidor fuera inmediata, sin solicitar el código de validación. Investigando un poco me entero de que, al parecer, el acceso mediante clave anula por defecto cualquier otro método de autenticación. Toda la configuración para el Google 2FA a hacer puñetas. Menudo chasco!

Así que vuelta a investigar la manera de poder mantener ambos modos de autenticación aunque, eso sí, sin necesidad de introducir la contraseña. De momento dos factores ya me parecen suficientes. Mejor dejar la autenticación multifactor (MFA) para más adelante.

Partimos de la configuración previa del archivo del servidor SSH, que coloco aquí mientras le busco un sitio mejor

sudo nano /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication yes
PermitRootLogin prohibit-password

La cuestión es que si deseamos mantener ambos métodos de autenticación, de manera que ya no nos pida la contraseña (gracias a la clave RSA) pero nos continúe pidiendo el código de verificación, deberemos indicárselo explícitamente. Para ello, añadiremos una línea especificando dos posibles vías de autorización: O proporcionamos una clave pública y una contraseña, o bien una clave pública y un código de verificación.Las dos posibilidades están descritas en forma de dos listas de métodos. Las listas están separadas por un espacio. Y en cada lista, los métodos requeridos están separados por comas. Si los requisitos de una lista no se dan, entonces se pasa a la siguiente.
Como la configuración de este parámetro parece algo liosa, dejo aquí este enlace en el que lo explican todo muy bien y de manera muy detallada.

AuthenticationMethods publickey,password publickey,keyboard-interactive

Luego debemos modificar el archivo de configuración de PAM para que no nos pida la contraseña

sudo nano /etc/pam.d/sshd

Y comentamos la siguiente línea

#@include common-auth

Y ya está. Si os estáis preguntando por qué es necesario decirle a PAM que no pida contraseña en lugar de quitarla directamente de la lista de los métodos de autorización, os diré que yo me pregunto lo mismo. Lo único que sé es que hice la prueba y no funcionó, pero con el método descrito va como la seda.

Publicado por David en Blog, Seguridad, SSH, 0 comentarios