Blog

Instalación de Arch con cifrado por hardware: cryptsetup y OPAL

Instalación de Arch con cifrado por hardware: cryptsetup y OPAL

Physical security ID (PSID) en etiqueta de SSD

Tengo un SSD que además es self-encrypting drive (SED). Sigue las especificaciones de TCG/Opal. En este artículo instalo Arch con partición root cifrada, dejando /boot sin cifrar. Está basado en este enlace.

Lo ideal es que la placa soporte el cifrado hardware por firmware. Este método es transparente para el sistema operativo. Desafortunadamente, la mía no tiene ninguna opción «HDD Security» en el menú del Setup («BIOS»).

Anteriormente se utilizaban métodos como PBAs, pero es complejo y no soporta bien suspensión S3. Afortunadamente, ahora cryptsetup 2.7 soporta cifrado OPAL y es casi igual de sencillo que cifrado por software.

Ejecutamos timedatectl para actualizar la hora del sistema:

root@archiso ~ # timedatectl       
               Local time: Tue 2024-06-11 14:57:50 UTC
           Universal time: Tue 2024-06-11 14:57:50 UTC
                 RTC time: Tue 2024-06-11 14:57:50
                Time zone: UTC (UTC, +0000)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

Reseteamos el disco. Nos pedirá el PSID (Physical security ID) presente en la etiqueta del SSD (ver imagen arriba):

root@archiso ~ # cryptsetup luksErase --hw-opal-factory-reset /dev/nvme0n1
Enter OPAL PSID: 

WARNING!
========
WARNING: WHOLE disk will be factory reset and all data will be lost! Continue?

Are you sure? (Type 'yes' in capital letters): YES

Creamos particiones. En mi caso, serán 2, una EFI, que se montará en /boot, y otra raíz root:

root@archiso ~ # gdisk /dev/nvme0n1
GPT fdisk (gdisk) version 1.0.10

Partition table scan:
  MBR: not present
  BSD: not present
  APM: not present
  GPT: not present

Creating new GPT entries in memory.

Command (? for help): n
Partition number (1-128, default 1): 
First sector (34-3907029134, default = 2048) or {+-}size{KMGTP}: 
Last sector (2048-3907029134, default = 3907028991) or {+-}size{KMGTP}: +4GiB
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): ef00
Changed type of partition to 'EFI system partition'

Command (? for help): p
Disk /dev/nvme0n1: 3907029168 sectors, 1.8 TiB
Model: CT2000T500SSD8                          
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): E5CC199A-A957-4864-9632-D60A7592C037
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 3907029134
Partitions will be aligned on 2048-sector boundaries
Total free space is 3898640493 sectors (1.8 TiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         8390655   4.0 GiB     EF00  EFI system partition

Command (? for help): n
Partition number (2-128, default 2): 
First sector (34-3907029134, default = 8390656) or {+-}size{KMGTP}: 
Last sector (8390656-3907029134, default = 3907028991) or {+-}size{KMGTP}: +1280GiB
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8309
Changed type of partition to 'Linux LUKS'

Command (? for help): p
Disk /dev/nvme0n1: 3907029168 sectors, 1.8 TiB
Model: CT2000T500SSD8                          
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): E5CC199A-A957-4864-9632-D60A7592C037
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 3907029134
Partitions will be aligned on 2048-sector boundaries
Total free space is 1214285933 sectors (579.0 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         8390655   4.0 GiB     EF00  EFI system partition
   2         8390656      2692745215   1.3 TiB     8309  Linux LUKS

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): Y
OK; writing new GUID partition table (GPT) to /dev/nvme0n1.
The operation has completed successfully.

Formateamos la partición EFI

root@archiso ~ # mkfs.fat -F 32 /dev/nvme0n1p1
mkfs.fat 4.2 (2021-01-31)

Formateamos la partición cifrada con LUKS, sólo por hardware (--hw-opal-only). La contraseña de OPAL Admin y la de LUKS («passphrase») no tienen por qué ser las mismas:

root@archiso ~ # cryptsetup luksFormat /dev/nvme0n1p2 --type luks2 --hw-opal-only

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

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/nvme0n1p2: 
Verify passphrase: 
Enter OPAL Admin password: 
Verify passphrase: 
cryptsetup luksFormat /dev/nvme0n1p2 --type luks2 --hw-opal-only  15.93s user 0.30s system 49% cpu 32.563 total

Se creará un volumen lógico en /dev/mapper/cifrada. Comprobamos que se pueda abrir:

root@archiso ~ # cryptsetup open /dev/nvme0n1p2 cifrada
Enter passphrase for /dev/nvme0n1p2: 
cryptsetup open /dev/nvme0n1p2 cifrada  6.46s user 0.08s system 90% cpu 7.249 total
root@archiso ~ # ls -l /dev/ma
ls: cannot access '/dev/ma': No such file or directory
2 root@archiso ~ # ls -l /dev/mapper
total 0
lrwxrwxrwx 1 root root       7 Jun 11 15:01 cifrada -> ../dm-0
crw------- 1 root root 10, 236 Jun 11 13:42 control

Formateamos el volumen lógico LUKS utilizando el sistema de ficheros que deseemos. En mi caso, BTRFS:

root@archiso ~ # mkfs.btrfs /dev/mapper/cifrada
btrfs-progs v6.8.1
See https://btrfs.readthedocs.io for more information.

Performing full device TRIM /dev/mapper/cifrada (1.25TiB) ...
NOTE: several default settings have changed in version 5.15, please make sure
      this does not affect your deployments:
      - DUP for metadata (-m dup)
      - enabled no-holes (-O no-holes)
      - enabled free-space-tree (-R free-space-tree)

Label:              (null)
UUID:               1a86a74a-a3d0-402d-8b96-33af86bff39d
Node size:          16384
Sector size:        4096	(CPU page size: 4096)
Filesystem size:    1.25TiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         DUP               1.00GiB
  System:           DUP               8.00MiB
SSD detected:       yes
Zoned device:       no
Features:           extref, skinny-metadata, no-holes, free-space-tree
Checksum:           crc32c
Number of devices:  1
Devices:
   ID        SIZE  PATH               
    1     1.25TiB  /dev/mapper/cifrada

Probamos a montar el volumen lógico formateado

root@archiso ~ # mount /dev/mapper/cifrada /mnt

Si estamos empleando BTRFS, puede ser interesante crear un subvolumen raíz @ y otro @snapshots:

root@archiso ~ # btrfs subvolume create /mnt/@ 
Create subvolume '/mnt/@'
root@archiso ~ # btrfs subvolume create /mnt/@snapshots
Create subvolume '/mnt/@snapshots'
root@archiso ~ # ls -l /mnt
total 0
drwxr-xr-x 1 root root 0 Jun 11 15:02 @
drwxr-xr-x 1 root root 0 Jun 11 15:02 @snapshots

# Ahora desmonto y monto sólo el subvolumen raíz:

root@archiso ~ # umount /mnt
root@archiso ~ # mount -o subvol=@ /dev/mapper/cifrada /mnt
root@archiso ~ # ls -l /mnt
total 0
root@archiso ~ # mount --mkdir /dev/nvme0n1p1 /mnt/boot
root@archiso ~ # ls -l /mnt/boot
total 0

Instalamos paquetes con pacstrap. Aquí he incluído como extra amd-ucode porque tengo CPU AMD, btrfs-progs para utilidades BTRFS, así como vim y openssh.

root@archiso ~ # pacstrap -K /mnt base linux linux-firmware amd-ucode btrfs-progs grub vim openssh

Seguimos la instalación típica de arch:

# fstab
genfstab -U /mnt >> /mnt/etc/fstab

# chroot
arch-chroot /mnt

# zona horaria y sincronizar hora al hardware
ln -sf /usr/share/zoneinfo/Europe/Madrid /etc/localtime
hwclock --systohc

# locales
vim /etc/locale.gen
locale-gen

# hostname
vim /etc/hostname


Tal y como dice aquí, tenemos que editar la configuración de mkinitcpio. En mi caso estoy utilizando el initramfs por defecto, de tipo busybox. Ver enlace para initramfs de tipo systemd.

[root@archiso /]# cd /etc
# Hago backup de la configuración por si acaso:
[root@archiso etc]# cp -rp mkinitcpio.conf mkinitcpio.conf.bak
[root@archiso etc]# vim mkinitcpio.conf
    # La línea HOOKS tiene que quedar así. En mi caso, sólo tuve que añadir "encrypt":
    HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block encrypt filesystems fsck)

Regeneramos initramfs:

[root@archiso etc]# mkinitcpio -P
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'default'
==> Using default configuration file: '/etc/mkinitcpio.conf'
  -> -k /boot/vmlinuz-linux -g /boot/initramfs-linux.img
==> Starting build: '6.9.3-arch1-1'
[...]

Seguimos con instalación típica de Arch. Contraseña:

[root@archiso etc]# passwd
New password:
Retype new password:
passwd: password updated successfully

Instalamos paquetes grub y efibootmgr:

[root@archiso ~]# pacman -S grub efibootmgr

Instalamos GRUB en /boot (esto es para UEFI, si el lector utiliza la clásica BIOS consulte el comando adecuado):

[root@archiso ~]# grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=mi_arch

Tenemos que configurar GRUB. Obtenemos el UUID de la partición LUKS (/dev/nvme0n1p2, no del volumen lógico /dev/mapper/cifrada):

[root@archiso default]# blkid
[...]
/dev/nvme0n1p2: UUID="<UUID_DE_PARTICION_LUKS>" SUBSYSTEM="HW-OPAL" TYPE="crypto_LUKS" PARTLABEL="Linux LUKS" PARTUUID="<PART_UUID_DE_PARTICION_LUKS>"
[...]
/dev/mapper/cifrada: UUID="<UUID_DEL_VOLUMEN_LOGICO>" UUID_SUB="<UUID_SUB_DEL_VOLUMEN_LOGICO>" BLOCK_SIZE="4096" TYPE="btrfs"
[...]

Configuramos GRUB en /etc/default/grub. Buscamos esta línea

GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"

Añadimos lo siguiente:

GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet cryptdevice=UUID=<UUID_DE_PARTICION_LUKS>:cifrada root=/dev/mapper/cifrada"

También es posible utilizar la ruta de la partición en lugar del id:

cryptdevice=/dev/nvme0n1p2:cifrada root=/dev/mapper/cifrada"

…pero prefiero utilizar el UUID, por si acaso cambiase por algún motivo el orden de las particiones o cómo denomina Linux al disco…

Regeneramos GRUB:

[root@archiso etc]# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-linux
Found initrd image: /boot/amd-ucode.img /boot/initramfs-linux.img
Found fallback initrd image(s) in /boot:  amd-ucode.img initramfs-linux-fallback.img
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...
done

Ya podemos reiniciar con el comando «reboot». Debería aparecer el menú de selección de GRUB. Al iniciar, se mostrará este mensaje:

A password is required to access the cifrada volume:
Enter passphrase for /dev/nvme0n1p2:

Introducimos la contraseña de LUKS y deberíamos arrancar al login de texto de Arch.