Pages

Sunday, April 5, 2015

LUKS: Cifrado de disco en GNU/Linux

Una parte muy importante de la seguridad informática es el cifrado, el cual nos permite poder garantizar la confidencialidad de los datos mediante la codificación de la información de forma que únicamente las partes autorizadas puedan leerlo. El cifrado en si no previene la interceptación o robo de los datos, pero consigue que quién intercepte esos datos no pueda tener acceso a ellos en claro.

En esta entrada hablaré del cifrado de disco que, aunque parezca no estar relacionado directamente con la temática del blog, es algo que debe estar siempre presente el todos los ámbitos, incluyendo por supuesto la seguridad industrial, donde se pretenda, por ejemplo, impedir el acceso a datos confidenciales en caso de robo de servidores con datos de producción, portátiles de ingenieros de control, copias de seguridad en discos externos, etc.

Existen muchas opciones para cifrar un volumen de datos, un sistema de archivos, o parte de los ficheros contenidos en él, pero principalmente, existen dos formas:

Cifrado a nivel de archivos, donde por ejemplo tendríamos:
  • EncFS, utilidad de cifrado de archivos basada en FUSE, trabajando en espacio de usuario y que, por lo tanto, no requiere permisos de administrador.
  • eCryptfs, similar a EncFS, pero trabajando en espacio del kernel. Es usado para implementar el cifrado del directorio home en Ubuntu y en ChromeOS, entre otros.
Cifrado de un sistema de archivos completo:

LUKS


LUKS permite, de una forma fácil y cómoda, trabajar con cifrado a nivel de disco basado en el módulo del kernel Linux DMCrypt.




En las FAQ del proyecto podremos encontrar mucha información sobre cómo trabajar con volúmenes LUKS. Como introducción, para ayudar a entender el resto de la entrada del blog, indicaré los conceptos básicos.

Principales componentes:
  • dm-crypt: Subsistema de cifrado de disco del kernel Linux.
  • cryptsetup: Frontend de línea de comandos, de bajo nivel, para trabajar con dm-crypt. También implementa la especificación LUKS.
  • LUKS: LUKS es una especificación de cifrado de discos, implementada en la utilidad cryptsetup.
  • cryptmount: Frontend alternativo para trabajar con dm-crypt. Los volúmenes no son compatibles con LUKS por lo que no se tratará en esta entrada.

Principales tipos de volúmenes cifrados:
  • plain dm-crypt: Es un volumen cifrado simple, sin ningún tipo de metadata que contenga información sobre el cifrado. Para abrir un volumen de este tipo será necesario indicar todos los parámetros necesarios en la llamada a cryptsetup desde la línea de comandos. Normalmente no se recomienda utilizar este tipo, pero se puede consultar las FAQ para posibles casos de uso.
  • LUKS: Es el tipo que utilizaremos en la mayoría de los casos, por su facilidad de uso así como por características adicionales, muy interesantes, sobre el tipo plain dm-crypt:
    • Un formato en disco bien documentado, multiplataforma, que permite que un volumen pueda ser transportado y manejado por distintos programas. Existen utilidades para trabajar con volúmenes LUKS en Android y Windows, además de Linux.
    • Múltiples contraseñas, que es posible cambiar, empleadas para desbloquear una única clave maestra usada para descifrar el volumen.
    • Propiedades específicas para evitar técnicas forenses.
    • Mayor usabilidad, ya que el volumen contiene todo el metadata necesario y, por lo tanto, cryptsetup no necesita que le indiquemos los parámetros para trabajar con él.

Adicionalmente a la página del proyecto, para obtener más información, es muy recomendable visitar la wiki del proyecto Arch Linux, así como el manual de instalación de Ubuntu Server.

Trabajando con LUKS


Voy a describir la preparación de una máquina, que ejecutará Ubuntu 14.04.2 LTS Server (amd64), cuyo volumen principal estará cifrado, así como la partición de swap, y que además contará con otros dos discos adicionales para datos, también cifrados.

Como nota de interés de este sistema cabría resaltar:
  • Los volúmenes cifrados adicionales se desbloquearán automáticamente al desbloquear el de sistema, teniendo que introducir únicamente la contraseña del volumen de sistema.
  • Será posible introducir la contraseña para LUKS de forma remota, vía SSH, lo cual permitirá tener esta configuración en un equipo al que no tengamos acceso físico o que simplemente no tenga conectado un teclado y monitor.

Nota: No describiré la propia instalación, así como la preparación del disco de sistema, ya que el instalador de Ubuntu es capaz de hacer gran parte del trabajo por nosotros, centrándome en los discos adicionales de datos. Simplemente decir que se utilizó un particionado automático, empleando para ello LVM cifrado, quedando el disco de sistema (/dev/sda) como sigue:
  • /dev/sda1 es /boot, sin cifrar, y /dev/sda5 es el volumen LUKS de sistema.
  • /dev/mapper/sda5_crypt es usado como volumen físico para LVM.
  • Dos volúmenes LVM lógicos, uno para root (/) y otro para swap.

Ver más detalles en el manual de instalación de Ubuntu Server.

Creación de los volúmenes LUKS


Particionado de los dos discos de datos, sin usar RAID ni LVM, mediante la utilidad parted:

// Creación de las particiones primarias (sdb1 and sdc1)
(parted) select /dev/sdb
(parted) mklabel gpt
(parted) mkpart primary 0% 100%
(parted) print
Model: ATA WDC WD20EARS-00M (scsi)
Disk /dev/sdb: 2000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name     Flags
 1      1049kB  2000GB  2000GB  ext4         primary

(parted) align-check optimal 1
1 aligned

... Lo mismo con el otro disco: /dev/sdc
 
Creación de los volúmenes LUKS de datos:

// Creación del volumen:
# cryptsetup --key-size=512 --verify-passphrase luksFormat /dev/sdb1

// Verificar la correcta creación:
# cryptsetup luksDump /dev/sdb1

Version:        1
Cipher name:    aes
Cipher mode:    xts-plain64
Hash spec:      sha1
Payload offset: 4096
MK bits:        512
...

... Lo mismo con el otro disco: /dev/sdc
 
Creación del sistema de archivos ext4 dentro de los volúmenes LUKS cifrados:

// Abrir el volumen LUKS y crear el sistema de archivos:
# cryptsetup open --type luks /dev/sdb1 data1
# mkfs -t ext4 -m 0 /dev/mapper/data1

// Montar el volumen:
# mkdir /media/data1
# mount -t ext4 /dev/mapper/data1 /media/data1

... Lo mismo con el otro disco: /dev/sdc para data2

Desbloqueo y montaje automático de los volúmenes de datos


Al crear los volúmenes de datos habremos utilizado una contraseña distinta para cada uno, así como para el volumen de sistema. Esto es lo correcto pero, a no ser que consideremos el sistema tan crítico como para tener que introducir cada contraseña individualmente, normalmente querremos desbloquear todos los volúmenes introduciendo una única contraseña. Es uno de esos compromisos, entre la seguridad y la usabilidad, que cada uno tendrá que considerar según cada caso.

La forma de implementar este sistema es desbloquear los volúmenes de datos a partir de una clave derivada del volumen LUKS de sistema, que será añadida a los volúmenes de datos como segunda clave alternativa. Posteriormente se configurará en el fichero /etc/crypttab el uso de la clave derivada del volumen de sistema para desbloquear los volúmenes de datos.

Se utilizará para ello una serie de scripts disponibles en distribuciones Debian y derivadas, como Ubuntu, situados en /lib/cryptsetup/scripts.

Desbloqueo y montaje automático de los volúmenes de datos al desbloquear el de sistema:

// Obtención del UUID de cada partición, necesario más adelante:
# blkid /dev/sdb1
/dev/sdb1: UUID="d38e4789-33e2-43dc-9991-f28221918390" TYPE="crypto_LUKS"

// Generar una clave derivada del volumen LUKS de sistema.
# cd /root
# /lib/cryptsetup/scripts/decrypt_derived sda5_crypt > sda5_derived_key_file

// Añadir esta clave como una contraseña adicional al volumen de datos:
# cryptsetup luksAddKey /dev/sdb1 sda5_derived_key_file

// Comprobación:
# cryptsetup luksDump /dev/sdb1
...
Key Slot 0: ENABLED      <---- Contraseña principal del volumen de datos
...
Key Slot 1: ENABLED      <---- sda5_derived_key_file
...
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

... Lo mismo con el otro disco: /dev/sdc

// Borrado seguro del fichero con la clave:
# shred -u /root/sda5_derived_key_file

// Configuración en /etc/crypttab:
sda5_crypt UUID=0bc29f0c-7892-f426-b9b8-9f7d82b10fbb none luks
data1 UUID=d38e4789-33e2-43dc-9991-f28221918390 sda5_crypt luks,keyscript=/lib/cryptsetup/scripts/decrypt_derived
data2 UUID=bb016fa9-0b6f-4c12-aef4-7dd6ae8fca15 sda5_crypt luks,keyscript=/lib/cryptsetup/scripts/decrypt_derived

// Configuración en /etc/fstab
/dev/mapper/data1  /media/data1  ext4  noatime,errors=remount-ro  0  0
/dev/mapper/data2  /media/data2  ext4  noatime,errors=remount-ro  0  0

// Actualizar /boot
# update-initramfs -u
update-initramfs: Generating /boot/initrd.img-3.13.0-43-generic
 
Como último paso, pero muy importante y altamente aconsejable, es realizar una copia de seguridad de la cabecera de los volúmenes LUKS y guardarla en un lugar seguro, por supuesto fuera de este equipo:

# cryptsetup luksHeaderBackup /dev/sda5 --header-backup-file /root/sda5-luks-header.img
# cryptsetup luksHeaderBackup /dev/sdb1 --header-backup-file /root/sdb1-luks-header.img
# cryptsetup luksHeaderBackup /dev/sdc1 --header-backup-file /root/sdc1-luks-header.img

Con estos pasos tendremos un sistema que al arranque nos solicitará la contraseña del volumen LUKS de sistema y, a continuación, se desbloquearán de forma automática los otros dos discos cifrados sin necesidad de introducir otras dos contraseñas.

Montando remotamente volúmenes LUKS


Ahora que ya hemos conseguido desbloquear los tres discos con una única contraseña, vamos a ver cómo configurar el sistema para poder realizar este desbloqueo de forma remota, utilizando para ello SSH.

Nota: Para las instrucciones que siguen a continuación, se parte del hecho que OpenSSH Server ya está instalado previamente.

Instalamos Dropbear, un servidor SSH especialmente diseñado para sistemas con recursos limitados, que será al que nos conectaremos para desbloquear los volúmenes LUKS.

Nota: También es necesario contar en el sistema con el paquete busybox que, al menos en una instalación estándar de Ubuntu, ya debería estar instalado.

# aptitude install dropbear
 
Esta instalación también realizará las siguientes acciones:
  • Convertirá las claves OpenSSH del host a formato Dropbear: /etc/dropbear/dropbear_rsa_host_key y /etc/dropbear/dropbear_dss_host_key. Para ello se emplea la utilidad /usr/lib/dropbear/dropbearconvert.
  • Desactivará el arranque de Dropbear, al ya estar instalado previamente OpenSSH: NO_START=1 en /etc/default/dropbear.
  • Se añadirá Dropbear a initramfs, lo cual permitirá acceder via SSH al equipo antes de que el kernel intente montar el sistema de archivos raíz.
  • Se generará un conjunto nuevo de claves de host para Dropbear, en /etc/initramfs-tools/etc/dropbear, y un par de claves RSA pública/privada para el usuario root de initramfs (el cual es distinto del usuario root del sistema ya que, cabe recordar, initramfs carga antes que el sistema de archivos raíz) en /etc/initramfs-tools/root/.ssh/id_rsa.

Ahora deberemos tomar la decisión de utilizar las claves generadas en la instalación de Dropbear para initramfs o bien, como es el caso descrito aquí, decidimos cambiar las claves por las que ya tenía el equipo y por nuestra propia clave de usuario.

Claves de host:

# cp /etc/dropbear/dropbear_* /etc/initramfs-tools/etc/dropbear/

Clave para el usuario root de initramfs:
 
// Añadimos nuestra propia clave pública al fichero `authorized_keys` y
// quitamos la generada durante la instalación.
// Hay muchas formas de realizar este paso y copiar nuestro propio
// fichero es una de ellas.
# cp ~/.ssh/authorized_keys /etc/initramfs-tools/root/.ssh/

// Borramos las claves generadas durante la instalación ya que no van a
// ser usadas para nada.
# rm /etc/initramfs-tools/root/.ssh/id_rsa*

Importante: A tener en cuenta que Dropbear no soporta actualmente claves ECDSA, que son precisamente las que OpenSSH prefiere utilizar como primera opción. Por ello, si hemos conectado previamente con este equipo, deberemos eliminar su entrada ECDSA de nuestro fichero .ssh/known_hosts y conectar de nuevo con ssh -o HostKeyAlgorithms=ssh-rsa para forzar el uso de la clave RSA. Una vez que que la clave RSA haya sido registrada, ya no será necesario utilizar el parámetro HostKeyAlgorithm de nuevo. (Fuente)

A continuación configuraremos initramfs. Para ello editaremos el fichero

/etc/initramfs-tools/initramfs.conf:
  • Debe contener BUSYBOX=y
  • No debe contener DROPBEAR=n
  • Configuración IP (de nuevo debemos recordar que el sistema de archivos raíz todavía no está disponible):
    • Asignación por DHCP: Debe contener DEVICE=eth0 (o la interfaz que corresponda).
    • Dirección IP estática: Añadir encima de DEVICE= la línea: IP=192.168.10.100::192.168.10.1:255.255.255.0::eth0:off, donde:
      • Sintaxis: [host ip]::[gateway ip]:[netmask]:[hostname]:[device]:[autoconf]
      • Notar que en el ejemplo se ha omitido el hostname.
      • Notar que IP= está en mayúsculas, muy importante.

Ahora deberemos generar el script que nos permitirá desbloquear los volúmenes LUKS durante el arranque.

Para ello crearemos el script /etc/initramfs-tools/hooks/crypt_unlock.sh disponible en este gist y activaremos el bit de ejecución:
 
# chmod +x /etc/initramfs-tools/hooks/crypt_unlock.sh

Finalmente, ya sólo nos quedará actualizar initramfs y reiniciar el equipo.
 
// Actualizamos initramfs y reiniciamos:
# update-initramfs -u
# reboot

Al iniciar el equipo, nos conectaremos indicando el usuario root en nuestro cliente SSH. De nuevo, a tener en cuenta que este usuario no es el de sistema, sino el usuario root de initramfs.

Una vez conectados, el script que creamos anteriormente nos indicará que debemos ejecutar el comando unlock para desbloquear el volumen de sistema.
 
$ ssh root@server
To unlock root-partition run unlock

BusyBox v1.21.1 (Ubuntu 1:1.21.0-1ubuntu1) built-in shell (ash)
Enter 'help' for a list of built-in commands.

# unlock
Unlocking the disk /dev/disk/by-uuid/0bc29f0c-7892-f426-b9b8-9f7d82b10fbb (sda5_crypt)
Enter passphrase:

Reading all physical volumes.  This may take a while...
  Found volume group "server-vg" using metadata type lvm2
  2 logical volume(s) in volume group "server-vg" now active
cryptsetup: sda5_crypt set up successfully
Connection to localhost closed.

Solución de problemas


Estas instrucciones las he probado primero en una máquina virtual VirtualBox y, a continuación, en un HP ProLiant MicroServer N36L, ambos corriendo una Ubuntu 14.04.2 LTS Server (amd64). En ambos casos no he tenido ningún tipo de problema pero en este blog y en este otro, que me han servido muy bien como referencia, se puede encontrar soluciones a posibles problemas que nos podríamos encontrar.

Referencias y más información


[1] https://gitlab.com/cryptsetup/cryptsetup
[2] https://en.wikipedia.org/wiki/Comparison_of_disk_encryption_software
[3] https://wiki.archlinux.org/index.php/Dm-crypt
[4] http://stinkyparkia.wordpress.com/2014/10/14/remote-unlocking-luks-encrypted-lvm-using-dropbear-ssh-in-ubuntu-server-14-04-1-with-static-ipst/
[5] https://projectgus.com/2013/05/encrypted-rootfs-over-ssh-with-debian-wheezy/
[6] https://gist.github.com/mariodpros/3129b40038be5fbca0e4