Creando una partición cifrada en un disco externo

Hace unos días, tuve un problema con un disco externo Western Digital Elements que compré en Amazon. El disco falló, daba como espacio total 0 Gb, incluso las utilidades de WD no eran capaces de comprobar el disco. Resulta que estos discos no son discos SATA de 2,5 dentro de una caja que los convierte a USB3.0, el conector que llevan soldado en placa es directamente un USB 3.0. No fui capaz de recuperar la información que contenía. Ante este problema, y estando el disco en garantía (18 meses de uso), tenía dos posibilidades: enviarlo a Amazon, o enviarlo a WD.

Si lo enviaba a Amazon, me devolvían el dinero pero NO me garantizaban que la información se eliminara y el disco se destruyera (la operadora de Amazon no pudo asegurármelo, WTF!). Western Digital por su parte me enviaba un disco remanufacturado (reparado), tras cobrarme los portes de enviarlo a su sede central en España (unos 7€, dependiendo de con que empresa de transportes lo enviase), respetándome con ese disco nuevo el tiempo de garantía que me quedase del viejo (en mi caso 6 meses). En mi conversación con WD me quedo completamente claro que para WD la privacidad de la información de los discos enviados a ellos es crítica, y se destruiría en el caso de ser accesible de algún modo.

Resumiendo:

  • Amazon: 100% del coste del disco (o cambio por uno igual nuevo), 0€ portes, destrucción de la información: NO.
  • WD: reemplazo por disco reparado (6 meses de garantía), 7€ portes, destrucción de la información: SÍ.

Tras pensármelo unos días, me decido por enviárselo a Amazon, y comprar otro disco nuevo WD Elements (sí, es verdad, no aprendo., se que me durará 18 meses…).

El problema de los discos WD Elements, es que internamente llevan un disco se la serie Green que no están pensados para un uso intensivo, 24/7 como el que yo le quiero dar, para un servidor de archivos de red. Para eso están los discos de la serie Red. Los Blue son para alto rendimiento de sobremesa, y los Black para sobremesa normales. No encontré discos de la serie Red de más de 1TB en formato 2,5.

¿Qué puedo hacer para que cuando me falle el disco no tener problemas al enviarlo a Amazon? Lo primero es cifrar la información sensible, y segundo tener una segunda copia de la información en otro disco… Lo de la segunda copia lo obviaremos de momento, y nos centraremos en el cifrado de la información sensible en esta entrada.

A la hora de cifrar la información tenemos dos posibilidades: cifrar la información a nivel de bloque, es decir, cifrar la partición que contiene el sistema de archivos, y cifrar la información a nivel de ficheros, es decir, podemos ver los archivos, su tamaño, fechas de modificación, etc, pero no tendremos acceso a su contenido al estar cifrado.

Es más segura la primera opción ya que si no tenemos la clave para descifrar la partición no podremos obtener absolutamente ninguna información de lo que contiene. Además es algo más rápida por lo que he podido leer. Vamos a ver entonces, como creamos nuestra partición cifrada. En mi caso no va a ser ninguna partición física, usaré un archivo de 256Gb como contenedor de la información, dentro de la única partición ext4 normal que tiene el disco externo. Como es posible que dentro de un tiempo esa partición se acabe llenando y necesite más espacio, vamos a usar también LVM para solucionar el problema de las posibles ampliaciones futuras (ya que lo hacemos, lo hacemos bien, jajaja). Una vez tengamos el volumen lógico creado, usaremos LUKS para cifrarlo y luego crearemos el sistema de archivos en él.

Pasos previos: LVM

Como había dicho, antes de nada hay que crear un volumen lógico con LVM que podamos usar. Resumiendo muy muy muy mucho, con LVM podemos agrupar distintos volúmenes físicos (/dev/sdaX, o ficheros como es nuestro caso a través de /dev/loop), en volúmenes lógicos que luego podremos formatear y usar.

420px-LVM-esquema_basico
Esquema básico de LVM (Wikipedia).

En nuestro caso, convertiremos ficheros en dispositivos de bloques a través de dispositivos de loopback, después los inicializaremos para que sean reconocidos por LVM como volúmenes físicos (Physical Volumes o PV), luego crearemos un volumen lógico (Logical Volumen o LV) con ellos y finalmente daremos formato al LV para poder usarlo. Básicamente el esquema de lo que haremos es el siguiente:

lvm2

Creando los archivos físicos

Suponiendo que tenemos el disco externo montado en /mnt/Externo, la forma más rápida de crear un archivo grande (sparse file), de 256Gb en nuestro caso, es mediante el siguiente comando:

root@servidor:~# cd /mnt/Externo
root@servidor:~# mkdir /mnt/Externo/cifrado
root@servidor:~# dd if=/dev/zero of=/mnt/Externo/cifrado/Archivo1 bs=1 count=0 seek=256G
0+0 records in
0+0 records out
0 bytes (0 B) copied, 0.000831007 s, 0.0 kB/s
root@servidor:~# ls -ali /mnt/Externo/cifrado/Archivo1
62128131 -rw-r--r-- 1 root root 274877906944 Apr 5 20:23 Archivo1

Si la seguridad es primordial, sería conveniente crear el archivo leyendo de /dev/urandom o similar en lugar de /dev/zero, y por supuesto no usar seek=256G para que todo el disco se inicialice con valores aleatorios (tardará muuuucho, horas diría yo). Lo que me interesaba es la velocidad en la creación, ya que en el caso de que alguien intente acceder al disco, lo conecte a un ordenador y no vea nada, no se molestará mas que en formatear. En el remoto caso en el que el disco llegue a manos de alguien capaz de averiguar que los ficheros Archivo1…ArchivoN, pertenecen a un sistema de archivos cifrado y quiera descifrarlos sin la clave, no me importa demasiado, la verdad, ¡jajajaja buena suerte!. Para empezar, solo crearemos un archivo base y más adelante veremos como ampliar el espacio añadiendo más archivos.

Convirtiendo los archivos en ficheros de bloques

Para usar LVM necesitamos dispositivos de bloques, no archivos. Para convertir un fichero en un archivo de bloques podemos hacerlo a través del dispositivos de loopback:

root@servidor:~# losetup /dev/loop1 /mnt/Externo/cifrado/Archivo1

Tan fácil como eso. A partir de este momento cuando nos refiramos a /dev/loop1, estaremos usando el dispositivo de bloques asociado a Archivo1, o lo que es lo mismo: estaremos usando el Archivo1 como un disco duro. Ya tenemos nuestros dispositivos físicos (PV)

Inicializando los PV

Antes de que LVM sea capaz de usar los PV, es necesario inicializarlos (algo similar a lo que se hace con los discos RAID, antes de usarlos):

root@servidor:~# pvcreate /dev/loop1
Physical volume "/dev/loop1" successfully created
root@servidor:~# pvdisplay
"/dev/loop1" is a new physical volume of "256.00 GiB"
--- NEW Physical volume ---
PV Name               /dev/loop1
VG Name
PV Size               256.00 GiB
Allocatable           NO
PE Size               0
Total PE              0
Free PE               0
Allocated PE          0
PV UUID               0iC3tt-Bmvf-nssI-XYBT-n0RR-UAKW-lVh5i9

Hecho. Como queremos dejar el sistema preparado para futuras ampliaciones, vamos a crear un grupo de volúmenes (Volume Group o VG) al que posteriormente podemos añadir PVs, aunque de momento solo contendrá un PV.

Creando el VG

Para usar nuestros PVs, deben pertenecer a un VG. Vamos a ello:

root@servidor:~# vgcreate externo /dev/loop1
Volume group "cifrado" successfully created
root@servidor:~# vgdisplay
--- Volume group ---
VG Name               externo
System ID
Format                lvm2
Metadata Areas        1
Metadata Sequence No  1
VG Access             read/write
VG Status             resizable
MAX LV                0
Cur LV                0
Open LV               0
Max PV                0
Cur PV                1
Act PV                1
VG Size               256.00 GiB
PE Size               4.00 MiB
Total PE              65535
Alloc PE / Size       0 / 0
Free  PE / Size       65535 / 256.00 GiB
VG UUID               F3qaFs-Tf4f-e70u-H3XS-ior0-haVC-KVLXJc

En este ejemplo hemos creado el VG con solo un PV, pero se puede crear con varios, añadiéndolos al final de la orden vgcreate. Más adelante veremos como ampliarlo con vgextend.

Creando el LV (Volumen Lógico)

El LV, Volumen Lógico, es el producto final, algo equivalente a una partición tradicional, que vamos a poder usar, independientemente de lo que haya por debajo (ficheros, particiones, RAIDs, …). Como pasa en el particionado tradicional, podemos crear una partición que no ocupe el total del espacio que tenemos en el disco; con LVM2 pasa lo mismo, podemos crear uno o varios LVs dentro de un VG.

En nuestro caso queremos un solo LV que ocupe todo el VG, y más adelante ampliaremos el VG y el LV si es necesario. Creamos por tanto un LV del tamaño máximo posible, al que llamaremos ‘cifrado’ dentro del VG ‘externo’:

root@servidor:~# lvcreate -n cifrado -l 100%FREE externo
Logical volume "cifrado" created
root@servidor:~# lvdisplay
--- Logical volume ---
LV Path                /dev/externo/cifrado
LV Name                cifrado
VG Name                externo
LV UUID                Y1sC1Q-KcEA-A1hF-rJxY-tGXm-u73B-BkhUgH
LV Write Access        read/write
LV Creation host, time servidor, 2016-04-26 09:27:23 +0200
LV Status              available
# open                 0
LV Size                256.00 GiB
Current LE             65535
Segments               1
Allocation             inherit
Read ahead sectors     auto
- currently set to     256
Block device           253:0

A partir de este momento tendremos nuestro volumen lógico, accesible desde /dev/externo/cifrado, para usarlo como si se tratase de una partición normal.

Cifrando el LV

La segunda parte de esta entrada, consiste en cifrar la informacion de nuestro LV para que, en el caso de pérdida, del disco externo, no se pueda recuperar la información y durmamos tranquilos 🙂

Para el cifrado utilizaremos LUKS (Linux Unified Key Setup), un sistema de cifrado de discos para sistemas Linux, podéis leer mas sobre el en su web. Lo primero que debemos hacer es instalar el paquete correspondiente:

root@servidor:~# apt-get install cryptsetup
...

Acto seguido podemos dar el formato LUKS a nuestro LV con el siguiente comando:

root@servidor:~# cryptsetup --hash=sha256 --key-size=512 --verify-passphrase  luksFormat /dev/externo/cifrado

WARNING!
========
This will overwrite data on /dev/externo/cifrado irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase:
Verify passphrase:

root@servidor:~# cryptsetup luksDump /dev/externo/cifrado
LUKS header information for /dev/externo/cifrado

Version:           1
Cipher name:       aes
Cipher mode:       xts-plain64
Hash spec:         sha256
Payload offset:    4096
MK bits:           512
MK digest:         97 54 58 85 33 27 cc 6b d7 84 17 97 13 11 97 64 8a 7f e0 09
MK salt:           00 fe b4 87 2c ca 5b 2f 09 9e 53 29 b2 e6 2d 6d
80 9f 07 d9 b8 a5 5b 61 b3 84 1b c0 1e d1 0a 80
MK iterations:     5875
UUID:              f347737b-02f0-46ec-935b-9d085abb9654

Key Slot 0: ENABLED
Iterations:             23529
Salt:                   64 ef 02 ac a1 93 e1 28 17 a0 09 64 10 70 4a 52
66 a6 a1 f5 fb 5f 17 b2 b3 96 45 d3 b5 08 0c 8e
Key material offset:    8
AF stripes:                4000
Key Slot 1: DISABLED
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

Podemos tener hasta 8 claves de acceso al dispositivo cifrado (key slots), donde el primero corresponde a la clave introducida con el comando de creación. ¿Qué quiere decir esto? Pues que existen ocho claves para acceder al dispositivo, y una, que se genera automáticamente, para cifrarlo. Esto es muy útil porque podemos tener una clave manual para poder acceder al dispositivo, y otras basadas en ficheros que usaremos a modo de llave para que los scripts puedan montar y desmontar el dispositivo de forma automática (para hacer backups por ejemplo). Otra utilidad es que podemos revocar una clave de acceso en cualquier momento (si la perdermos, o la olvidamos).

Creación del sistema de archivos

Como último paso nos queda crear el sistema de archivos sobre el dispositivo cifrado. Para hacerlo primero tenemos que permitir el acceso al dispositivo, abrirlo, y después formatearlo en ext4 (por ejemplo) para poder almacenar archivos en él. Para abrirlo necesitaremos una de las 8 claves (key slots) que habremos definido antes. Una vez abierto, se creará un nuevo dispositivo, en /dev/mapper, descifrado, que formatearemos y montaremos en /mnt/cifrado para poder acceder a los datos:

root@servidor:~# cryptsetup luksOpen /dev/externo/cifrado externo_descifrado
Enter passphrase for /dev/externo/cifrado:
root@servidor:~# mkfs.ext4 -L DatosExterno /dev/mapper/externo_descifrado
mke2fs 1.42.9 (4-Feb-2014)
Filesystem label=DatosExterno
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
16777216 inodes, 67107328 blocks
3355366 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=0
2048 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872
Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done

Tarda un par de minutos en crear el sistema de archivos, no os desesperéis. Seguidamente lo montamos, y ya tendremos nuestros datos seguros en el disco externo, pero accesibles:

root@servidor:~#mkdir /mnt/datos_seguros
root@servidor:~#mount /dev/mapper/externo_descifrado /mnt/datos_seguros

Contraseñas en fichero

Como habíamos dicho, podemos tener ficheros que se usen a modo de contraseña, para no tener que estar tecleando la contraseña cada vez que queramos abrir el acceso al dispositivo cifrado, permitiendo así tareas automatizadas. La forma de crearlas es mediante los siguientes comandos:

root@servidor:~# mkdir /etc/luks
root@servidor:~# dd if=/dev/urandom bs=512 count=1 of=/etc/luks/clave1.key
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.00203702 s, 251 kB/s
root@servidor:~# chmod 400 /etc/luks/clave1.key
root@servidor:~# cryptsetup luksAddKey /dev/externo/cifrado /etc/luks/clave1.key
Enter any passphrase:
root@servidor:~# cryptsetup luksDump /dev/externo/cifrado
LUKS header information for /dev/externo/cifrado
Version:           1
Cipher name:       aes
Cipher mode:       xts-plain64
Hash spec:         sha256
Payload offset:    4096
MK bits:           512
MK digest:         97 54 58 85 33 27 cc 6b d7 84 17 97 13 11 97 64 8a 7f e0 09
MK salt:           00 fe b4 87 2c ca 5b 2f 09 9e 53 29 b2 e6 2d 6d
80 9f 07 d9 b8 a5 5b 61 b3 84 1b c0 1e d1 0a 80
MK iterations:     5875
UUID:              f347737b-02f0-46ec-935b-9d085abb9654

Key Slot 0: ENABLED
Iterations:             23529
Salt:                   64 ef 02 ac a1 93 e1 28 17 a0 09 64 10 70 4a 52
66 a6 a1 f5 fb 5f 17 b2 b3 96 45 d3 b5 08 0c 8e
Key material offset:    8
AF stripes:                4000
Key Slot 1: ENABLED
Iterations:             23529
Salt:                   db 37 70 89 75 54 3c 48 ee 18 68 cc 73 b6 6d b5
9f b0 b6 ce 63 fd a1 86 52 71 33 d2 ef cd c3 15
Key material offset:    512
AF stripes:                4000
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

Como veis, ya tenemos una segunda clave de acceso almacenada en el key slot 1. Para dar de alta una clave nueva, nos pide una de las ya existentes, y el archivo puede contener cualquier cosa, en mi caso he creado una clave aleatoria de 512 bytes, pero podéis poner perfectamente un poema o una imagen pequeña (un jpg…).

Podemos probar la nueva clave, pero primero debemos desmontar el sistema de archivos y cerrar el dispositivo LUKS:

root@servidor:~# umount /mnt/datos_seguros
root@servidor:~# cryptsetup luksClose externo_descifrado

Ahora ya podemos montarlo, usando el fichero como clave, y no nos pedirá la clave manual:

root@servidor:~# cryptsetup luksOpen --key-file /etc/luks/clave1.key /dev/externo/cifrado externo_descifrado
root@servidor:~# mount /dev/mapper/externo_descifrado /mnt/datos_seguros

Automontando el sistema de archivos al iniciar

Finalmente queremos que cuando se monte el disco externo, se monte también el sistema de archivos cifrado que acabamos de configurar. Para ello debemos modificar algunos ficheros:

/etc/init/loop.conf:

description "Setup loop devices"

start on mounted MOUNTPOINT=/mnt/Externo

task
script
    losetup /dev/loop1 /mnt/Externo/cifrado/Archivo1
end script

Añadimos la siguiente linea a /etc/fstab:

/dev/mapper/externo_descifrado  /mnt/datos_seguros ext4 defaults,nobootwait 0 2

y en /etc/rc.local añadimos el comando para abrir el dispositivo cifrado al arrancar:

cryptsetup luksOpen --key-file /etc/luks/clave1.key /dev/externo/cifrado externo_descifrado

De esta forma cada vez que arranquemos nuestro equipo con el disco conectado nos montará el dispositivo cifrado. Es posible ahorrarnos la ultima linea, y que el sistema abra el dispositivo cifrado automáticamente, cuando se conecte, pero esto supone modificar el archivo /etc/cryptab (no es difícil), pero al hacer uso de dispositivos de loopback para convertir archivos en dispositivos de bloques, hay que modificar las dependencias de algunos archivo en /etc/init (los relacionados con cryptdisks, udev, …) para que arranquen después de configurar el dispositivo loop y es un lío. Así funciona y es más mantenible.

Y si por cualquier motivo tenemos que enviar el disco de vuelva al vendedor, tendrá que entretenerse un rato en descifrar su contenido 🙂

Para nota: ampliar el tamaño

Si en un momento dado vemos que nos quedamos sin espacio libre podemos ampliarlo con los siguientes comandos:

root@servidor:~# dd if=/dev/zero of=/mnt/Externo/cifrado/Archivo2 bs=1 count=0 seek=256G
0+0 records in
0+0 records out
0 bytes (0 B) copied, 0.00106901 s, 0.0 kB/s

root@servidor:~# losetup /dev/loop2 /mnt/Externo/cifrado/Archivo2

root@servidor:~# pvcreate /dev/loop2
Physical volume "/dev/loop2" successfully created

root@servidor:~# vgextend externo /dev/loop2
Volume group "externo" successfully extended

root@servidor:~# vgdisplay
--- Volume group ---
VG Name               externo
System ID
Format                lvm2
Metadata Areas        2
Metadata Sequence No  3
VG Access             read/write
VG Status             resizable
MAX LV                0
Cur LV                1
Open LV               1
Max PV                0
Cur PV                2
Act PV                2
VG Size               511.99 GiB
PE Size               4.00 MiB
Total PE              131070
Alloc PE / Size       65535 / 256.00 GiB
Free  PE / Size       65535 / 256.00 GiB
VG UUID               F3qaFs-Tf4f-e70u-H3XS-ior0-haVC-KVLXJc

root@servidor:~# lvextend /dev/externo/cifrado -l +100%FREE 
Extending logical volume cifrado to 511.99 GiB 
 Logical volume cifrado successfully resized

Es muy importante antes de reiniciar, modificar el archivo /etc/init/loop.conf para añadir una línea para /dev/loop2 y Archivo2 (losetup /dev/loop2 /mnt/Externo/cifrado/Archivo2), si no, no funcionarán al reiniciar. Reiniciamos y solo nos queda extender el sistema de archivos que tenemos en el LV:

root@servidor:~# lvdisplay
--- Logical volume ---
LV Path                /dev/externo/cifrado
LV Name                cifrado
VG Name                externo
LV UUID                Y1sC1Q-KcEA-A1hF-rJxY-tGXm-u73B-BkhUgH
LV Write Access        read/write
LV Creation host, time servidor, 2016-04-26 09:27:23 +0200
LV Status              available
# open                 1
LV Size                511.99 GiB
Current LE             131070
Segments               2
Allocation             inherit
Read ahead sectors     auto
- currently set to     256
Block device           253:0

root@servidor:~# resize2fs /dev/mapper/externo_descifrado
resize2fs 1.42.9 (4-Feb-2014)
Filesystem at /dev/mapper/externo_descifrado is mounted on /mnt/datos_seguros; on-line resizing required
old_desc_blocks = 16, new_desc_blocks = 32
The filesystem on /dev/mapper/externo_descifrado is now 134215168 blocks long.

root@servidor:~# e2fsck -n -f /dev/mapper/externo_descifrado 
e2fsck 1.42.9 (4-Feb-2014)
Warning!  /dev/mapper/externo_descifrado is mounted.
Warning: skipping journal recovery because doing a read-only filesystem check.
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
DatosExterno: 407213/33554432 files (0.5% non-contiguous), 47953932/134215168 blocks

root@servidor:~# df -hT
Filesystem                     Type      Size  Used Avail Use% Mounted on
udev                           devtmpfs  414M   12K  414M   1% /dev
tmpfs                          tmpfs      84M  2.9M   81M   4% /run
/dev/mmcblk0p2                 ext4      5.8G  3.6G  2.1G  63% /
none                           tmpfs     4.0K     0  4.0K   0% /sys/fs/cgroup
tmpfs                          tmpfs     419M     0  419M   0% /tmp
none                           tmpfs     5.0M     0  5.0M   0% /run/lock
none                           tmpfs     419M     0  419M   0% /run/shm
none                           tmpfs     100M     0  100M   0% /run/user
/dev/mmcblk0p1                 vfat      129M  7.4M  121M   6% /media/boot
/dev/mmcblk0p3                 ext4      8.3G  3.0G  4.9G  38% /mnt/Data
/dev/sda1                      ext4      1.8T  341G  1.4T  20% /mnt/Externo
/dev/mapper/externo_descifrado ext4      504G  175G  307G  37% /mnt/datos_seguros

Como vemos, en la última línea, ya tenemos nuestro sistema de archivos cifrado y redimensionado tal y como queriamos.

Otro éxito 🙂

Añadir un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.