Cifrado de bloques ================== Mediante esta técnica el software hace de intermediario entre los bloques físicos y los bloques de un dispositivo virtual cifrando en las escrituras y descifrando en las lecturas. .. image:: files/dm-crypt.png En consecuencia: - Ciframos un dispositivo de bloques entero. - Como el cifrado es independiente del sistema de archivos, se puede utilizar cualquier sistema de archivos. - Permite no sólo el cifrado de datos, sino el cifrado del sistema operativo de arranque, preparando convenientemente el sistema (caso que no abordaremos aquí, pero que puede consultarse, por ejemplo, en un `artículo de la wiki de Archlinux `_). Utilizaremos |LUKS| y abordaremos el caso más sencillo de querer cifrar una partición física, para lo cual debemos primero instalar el *software* de cifrado:: # apt install cryptsetup .. rubric:: Operativa manual Lo primero es mapear una partición física\ [#]_ (p.e. :file:`/dev/sda6`) sobre un dispositivo virtual:: # cryptsetup -y -v luksFormat /dev/sda6 # Requerirá una contraseña # cryptsetup open /dev/sda6 cifrado # Debemos proporcionar la contraseña Esto generará el dispositivo virtual de bloques :file:`/dev/mapper/cifrado`, sobre el cual podemos actuar como si se tratara de un dispositivo físico, o sea:: # mkfs.ext4 -L DATOSECRETOS /dev/mapper/cifrado # mount /dev/mapper/cifrado /mnt Si en algún momento quisiéramos desmontar todo:: # umount /mnt # cryptsetup close cifrado .. rubric:: Operativa automatizada Que el administrador deba llevar a cabo estas operaciones cada vez que se arranca el sistema, no es algo operativo. Para semiautomatizar el montaje durante el arranque podemos añadir la asociación entre el dispositivo físico y el virtual en :file:`/etc/crypttab`:: # echo "cifrado /dev/sda6 none" >> /etc/crypttab y la asociación entre el dispositivo virtual y el punto de montaje en :file:`/etc/fstab`:: # echo "/dev/mapper/cifrado /mnt ext4 defaults 0 0" >> /etc/fstab El montaje será semiautomático, porque durante el proceso de arranque deberemos digitalizar la contraseña. Es posible, también, en vez de que la clave sea interactiva, guardarla en un archivo. Es más, |LUKS| dispone de ocho slots para almacenar claves alternativas. Ahora mismo sólo habría una: .. code-block:: console :emphasize-lines: 19 # cryptsetup luksDump /dev/sda6 LUKS header information Version: 2 Epoch: 8 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: e26d3cf8-20a7-422f-ac8f-83340e63725f Label: (no label) Subsystem: (no subsystem) Flags: (no flags) Data segments: 0: crypt offset: 16777216 [bytes] length: (whole device) cipher: aes-xts-plain64 sector: 512 [bytes] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2i Time cost: 4 Memory: 98948 Threads: 1 Salt: a0 a1 57 4c 30 6a af e5 de 76 d5 d8 a9 f0 11 b7 ac b5 c6 90 d0 1d 4e 92 4d 1c 4b b5 4c 07 97 70 AF stripes: 4000 AF hash: sha256 Area offset:32768 [bytes] Area length:58048 [bytes] Digest ID: 0 Tokens: Digests: 0: pbkdf2 Hash: sha256 Iterations: 39337 Salt: 2b c9 51 10 c7 29 4b 63 35 a4 83 63 bc 36 46 2f 49 92 af dd 32 a8 7c 9d 19 08 51 80 1b 58 6f 56 Digest: 0c 52 b0 1d 8c 80 2e 6b 45 0a c8 ac 4a b2 e9 a2 f4 bf 81 e6 5a 00 c4 42 af 10 21 9c 3a 92 fe 6c con lo que podemos añadir al mismo sistema otra clave que esté en un archivo. Para ello, vamos primero a generar esa clave, constituida por 512 *bytes* totalmente aleatorios:: # dd < /dev/urandom > /root/luks.key bs=512 count=1 que, podemos consultar en formato hexadecimal, así:: # od -v -An -tx1 /root/luks.key # Consultamos la clave dc 12 ae d8 2c b5 4e 12 56 a9 35 b4 5f a6 29 b9 [...] Con la clave ya en el archivo :file:`/root/luks.key`, podemos añadirla a un *slot*:: # cryptsetup luksAddKey /dev/sda6 /root/luks.key # cryptsetup luksDump /dev/sda6 [...] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2i Time cost: 4 Memory: 98948 Threads: 1 Salt: a0 a1 57 4c 30 6a af e5 de 76 d5 d8 a9 f0 11 b7 ac b5 c6 90 d0 1d 4e 92 4d 1c 4b b5 4c 07 97 70 AF stripes: 4000 AF hash: sha256 Area offset:32768 [bytes] Area length:258048 [bytes] Digest ID: 0 1: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2i Time cost: 4 Memory: 100952 Threads: 1 Salt: b1 63 a9 24 aa cc f5 9c b4 6c 8a 8b 27 7a cb 2c 72 cd f8 d9 68 b9 1b f4 43 c7 d6 b5 20 81 47 c5 AF stripes: 4000 AF hash: sha256 Area offset:290816 [bytes] Area length:258048 [bytes] Digest ID: 0 [...] Por último, si en :file:`/etc/crypttab` modificamos la línea para que se use el archivo:: cifrado /dev/sda6 /root/luks.key durante el arranque no se pedirá ninguna clave y el sistema se encontrará montado al acabar la secuencia. .. warning:: Ahora bien, ¿para qué ciframos una partición si dejamos la clave para su descifrado en un archivo de otra partición sin cifrar? Lo interesante de lo anterior es, simplemente, comprobar que se puede guardar la clave en un archivo y usarlo para no tener que escribirla interactivamente. Y ello es útil, si almacenamos el archivo en un dispositivo externo como un pincho |USB| que procuremos retirar y llevarnos lejos de la máquina cuando no la usemos. Además, es conveniente ocultar ese archivo para que pase desapercibido si alguien se hace con nuestro pincho. A este respecto, lo más juicioso es guardar los 512 *bytes* de la clave en algún espacio libre del pincho |USB| y ajeno a los sistemas de archivos que pueda haber en él: - Si el particionado es |DOS|, podemos utilizar los últimos 512 bytes del espacio entre el |MBR| y la primera partición, ya que al principio de ese espacio puede haber código de un gestor de arranque como |GRUB|. - Si el particionado es |GPT|, podemos utilizar los últimos 512 bytes del espacio que se reserva para definir particiones, ya que es bastante improbable que en el pincho hayamos creado más de 124 particiones. Pongamos este segundo caso de ejemplo. En un disco |GPT|: * El primer sector es un |MBR| ficticio (512B) * El segundo sector es la cabecera |GPT| (512B) * A continuación hay espacio para 128 definiciones de particiones cada una de las cuales ocupa 128 bytes (16KiB). En consecuencia el comienzo del disco ocupa 17KiB o lo que es lo mismo 34 sectores, así que podemos ocupar el sector **34** para almacenar nuestra clave, con el único costo de que \"sólo\" podremos definir 124 particiones, lo cual, ciertamente, no parece ningún problema. Supongamos que el pincho se encuentra en :file:`/dev/sdb`\ [#]_:: # gdisk -l /dev/sdb [...] Number Start (sector) End (sector) Size Code Name 1 416 103003 50.1 MiB EF00 EFI System Partition 2 103008 30719966 14.6 GiB 0700 Microsoft basic data Vamos a crear una clave aleatoria de 512 bytes directamente sobre su sector **34**:: # dd < /dev/urandom > /dev/sdb bs=512 count=1 seek=33 y, creada, la añadimos a un *slot*:: # { echo "secreto" ; dd < /dev/sdb bs=512 count=1 skip=33; } | cryptsetup luksAddKey /dev/sda6 - donde "secreto" es la contraseña que introdujimos al crear el dispositivo cifrado y que nos servía para hacer el montaje interactivo. Añadida esta clave, podemos probar si funciona del siguiente modo:: # dd < /dev/sdb bs=512 count=1 skip=33 | cryptsetup open /dev/sda6 cifrado --key-file=- que debe generar el dispositivo virtual y, si continua la línea en :file:`/etc/fstab`, montarnos directamente la partición sobre :file:`/srv`. Ya tenemos la mitad del trabajo hecho, ya que aún falta que al arrancar el sistema busque el dispositivo, lo monte y lleve a cabo justamente esta operación. Para ello, debemos crear una regla para :program:`udev`, que al detectar el dispositivo |USB| lance un script:: # cat > /etc/udev/rules.d/70-usb.rules SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="abcd", ATTRS{idProduct}=="1234", \ KERNEL=="sd?", SYMLINK+="usbkey", RUN+="/usr/local/bin/unlock.sh" La regla identifica el dispositivo en el que hemos guardado la clave a través de su *idVendor* e *idProduct* que se pueden consultar fácilmente al hacer:: $ lsusb [...] Bus 002 Device 002: ID abcd:1234 Unknown [...] Además, aprovechamos la regla para añadir un enlace simbólico :file:`/dev/usbkey` que apunte al dispositivo. Con este nombre podremos referirnos al dispositivo dentro del *script*:: #!/bin/sh RT="/dev/sda6" DEVICE="/dev/usbkey" ENCVOL="cifrado" MOUNTP="/srv" { until [ -b "$PART" ]; do sleep .5; done dd < "$DEVICE" bs=512 count=1 skip=33 | \ cryptsetup open "$PART" "$ENCVOL" --key-file=- } & Por último, en :file:`/etc/crypttab` no debe existir referencia alguna, ya que es el *script* el que realiza la operación de crear el dispositivo cifrado. En :file:`/etc/fstab`, sí podemos dejar la línea, pero añadiendo la opción *nofail*, para que no falle el montaje y pare el arranque en caso de que no se encuentre el pincho:: /dev/mapper/cifrado /srv ext4 defaults,nofail 0 0 .. note:: Esta estrategia está tomada de `esta entrada de /dev/blog `_ y sólo es válida si se cifra una partición de datos y no la partición del sistema. Si se lleva a cabo el cifrado del sistema, es necesario recurrir a otra estrategia totalmente distinta basada en manipular la imagen `initramfs `_. .. rubric:: Notas al pie .. [#] También puede ser un volumen lógico de |LVM|. .. [#] Si se observa con atención, la primera partición no empieza en 34. Sino más adelante. Es posible, puesto que la parte destinada a la definición de particiones puede ser mayor. Sin embargo, ese |USB| procede de una imagen híbrida y es probable que empiece después, porque antes se ha situado el código de un gestor de arranque. En cualquier, como |GPT| obliga a que como mínimo se puedan definir 128 particiones, si escribimos en el sector 34, no nos cargaremos nada. .. |LVM| replace:: :abbr:`LVM (Logical Volume Management)` .. |LUKS| replace:: :abbr:`LUKS (Linux Unified Key Setup)` .. |GPT| replace:: :abbr:`GPT (GUID Partition Table)` .. |GRUB| replace:: :abbr:`GRUB (GRand Unified Bootloader)` .. |DOS| replace:: :abbr:`DOS (Disk Operating System)` .. |USB| replace:: :abbr:`USB (Universal Serial Bus)` .. |MBR| replace:: :abbr:`MBR (Master Boot Record)`