Iniciar Arch Linux con UEFI Secure Boot




El Secure Boot (arranque seguro), es un modo de la uefi de nuestra placa base que impide iniciar cualquier software a menos que esté firmado y certificado. Este modo no trae ningún problema al instalar versiones de Windows superiores a la 8, ya que estas disponen de certificación para poder iniciar el sistema operativo sin ningún problema.
En los ultimos años en algunas distribuciones de linux como Ubuntu o Fedora han implementado un software que está diseñado para funcionar como un cargador de arranque de primera etapa en sistemas UEFI, llamado shim. Desafortunadamente, shim no viene instalado ni configurado en la distro de Arch Linux, es por eso que en este post mostraré como generar nuestros propios certificados e implementarlos en nuestra placa base para poder ejecutar nuestro Arch en modo Secure Boot sin utilizar Shim.
Antes de comenzar en la NVRam de la placa base tenemos cuatro variables llamadas: PK KEK DB DBX,

1.DB (aka, 'signature database'): contains the trusted keys used for authenticating any applications or drivers executed in the UEFI environment
2.DBX (aka, 'forbidden signature database' or 'signature database blacklist'): contains a set of explicitly untrusted keys and binary hashes. Any application or driver signed by these keys or matching these hashes will be blocked from execution.
3.KEK (key exchange keys database): contains the set of keys trusted for updating DB and DBX
4.PK (platform key - while PK is often referred to simply as a single public key, it could be implemented as a database). Only updates signed with PK can update the KEK database.

El modo secure boot de la uefi tiene dos modos: usuario y setup mode -En el modo usuario (PK creada): las variables no pueden ser modificadas sin llaves ni firmas propias

-En el setup mode (PK no creada): se pueden establecer las variables que queramos sin ninguna llave ni firma

Después de entender para qué sirven estas 4 variables, vamos a crear nuestras propias variables firmadas por nosotros mismos manteniendo las que ya están en la nvram(certificados de Microsoft).


Proceso a seguir:

1.Tener el secure boot desactivado y iniciar el linux
2.Leer y hacer un backup de las variables existentes (Microsoft)
3.Generar nuevas llaves
4.Crear variables compuestas de nuestras llaves propias y las llaves existentes que tenemos en el backup creado anteriorimente
5.Reiniciar el ordenador y en la uefi poner el setup mode del secure boot
6.Iniciar nuevamente linux y cargar las variables compuestas creadas anteriormente, esto hará que después de cargar las llaves, la uefi se ponga en modo usuario


Comandos Utilizados:

Antes de comenzar deberemos tener las siguientes aplicaciones instaladas: efitools sbsigntools mokutil openssl

Al tener un Linux iniciado con el secure boot desactivado:

mkdir -p /etc/efikeys
cd /etc/efikeys
chmod 700 /etc/efikeys

Leeremos las variables ya creadas y haremos backup:

efi-readvar -v PK -o old_PK.esl
efi-readvar -v KEK -o old_KEK.esl
efi-readvar -v db -o old_db.esl
efi-readvar -v dbx -o old_dbx.esl

Crearemos tres llaves con openssl, una para nuestra PK, otra será KEK y otra para db y cambiaremos el permiso de las llaves para que nadie pueda leerlas.

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=My Platform Key/" -keyout PK.key -out PK.crt -days 3650 -nodes -sha256

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=Key exchange key/" -keyout KEK.key -out KEK.crt -days 3650 -nodes -sha256

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=Kernel signing key/" -keyout db.key -out db.crt -days 3650 -nodes -sha256

chmod 400 *.key

Cambiaremos los certificados a firmas.

UUID=$(uuidgen)
cert-to-efi-sig-list -g $UUID PK.crt PK.esl
sign-efi-sig-list -k PK.key -c PK.crt PK PK.esl PK.auth

cert-to-efi-sig-list -g $UUID KEK.crt KEK.esl
sign-efi-sig-list -a -k PK.key -c PK.crt KEK KEK.esl KEK.auth

cert-to-efi-sig-list -g $UUID db.crt db.esl
sign-efi-sig-list -a -k KEK.key -c KEK.crt db db.esl db.auth

sign-efi-sig-list -k KEK.key -c KEK.crt dbx old_dbx.esl old_dbx.auth

Crearemos certificados de nuestras llaves en formato DER

openssl x509 -outform DER -in PK.crt -out PK.cer
openssl x509 -outform DER -in KEK.crt -out KEK.cer
openssl x509 -outform DER -in db.crt -out db.cer

Combinaremos las llaves ya existentes (Microsoft) con las llaves generadas por nosotros.

cat old_KEK.esl KEK.esl > compound_KEK.esl
cat old_db.esl db.esl > compound_db.esl
sign-efi-sig-list -k PK.key -c PK.crt KEK compound_KEK.esl compound_KEK.auth
sign-efi-sig-list -k KEK.key -c KEK.crt db compound_db.esl compound_db.auth

Reiniciaremos el pc y entraremos a la uefi, en la opción del secure boot lo pondremos en setup mode Volvemos a iniciar Linux

Si hemos establecido el setup mode correctamente al ejecutar el comando efi-readvar nos ha tenido que salir:

Variable PK has no entries
Variable KEK has no entries
Variable db has no entries
Variable dbx has no entries
Variable MokList has no entries

cd /etc/efikeys actualizaremos las variables con las llaves compuestas que hemos generado nosotros

efi-updatevar -e -f compound_db.esl db
efi-updatevar -e -f compound_KEK.esl KEK
efi-updatevar -f PK.auth PK

Si utilizamos efi-readvar otra vez volveremos a ver las llaves de Microsoft y las llaves generadas por nosotros (OPCIONAL) Podemos crear una copia de las llaves compuestas que hemos generado nosotros

efi-readvar -v PK -o new_PK.esl
efi-readvar -v KEK -o new_KEK.esl
efi-readvar -v db -o new_db.esl
efi-readvar -v dbx -o new_dbx.esl

Una vez tenemos nuestras propias llaves creadas y válidas en nuestra nvram, tendremos que firmar el kernel de Linux con el que queremos iniciar en secure boot y el bootloader, en este caso grub.

Primero firmaremos el kernel, en mi caso vmlinuz Le llamaremos vmlinuz-zen-unsigned para diferenciarlo del que vamos a firmar,lo firmamos y para verificar que ha sido firmado correctamente podemos utilizar el comando sbverify

mv vmlinuz-linux-zen vmlinuz-linux-zen-unsigned 
sbsign --key /etc/efikeys/db.key --cert /etc/efikeys/db.crt vmlinuz-linux-zen-unsigned --output vmlinuz-linux-zen
sbverify --list vmlinuz-linux-zen

Desde la versión 2:2.06.r334 necesitamos especificar el parámetro disable-shim-lock, a demás de eso también necesitaremos cargar los módulos de grub en el propio archivo grubx64.efi, ya que tampoco está permitida la carga de los módulos fuera del archivo durante el arranque, es por eso que tendremos que especificar que módulos queremos cargar en el propio archivo cuando instalemos grub, pondremos los módulos en una variable para que el comando de la instalación de grub sea más legible.

GRUB_MODULES="acpi all_video boot bufio cat chain crypto datetime disk echo efi_gop efi_uga efifwsetup efinet ext2 extcmd fat font 
fshelp gcry_crc gettext gfxterm gzio halt help linux loadenv ls mmap net normal part_gpt part_msdos priority_queue reboot relocator search 
search_fs_file search_fs_uuid search_label terminal tpm video video_bochs video_cirrus video_colors video_fb zstd gfxmenu jpeg png"

grub-install --target=x86_64-efi --efi-directory=/boot --modules=${GRUB_MODULES} --bootloader-id=Grub --sbat /usr/share/grub/sbat.csv --disable-shim-lock

Finalmente, firmaremos grub, iremos a la partición efi, en mi caso /boot y en el archivo grubx64.efi realizaremos lo siguiente.

Lo cambiaremos de nombre para diferenciar que archivo ha sido firmado

mv grubx64.efi grubx64-unsigned.efi
#Firmamos el archivo con nuestras llaves
sbsign --key /etc/efikeys/db.key --cert /etc/efikeys/db.crt grubx64-unsigned.efi --output grubx64.efi
#Comprobamos que ha sido firmado
sbverify --list grubx64.efi

Una vez realizado todos estos pasos podremos reiniciar el pc, activar el secure boot y al iniciar Linux podremos comprobar si el secure boot está activado con el siguiente comando: mokutil -sb

Después de haber firmado el kernel y el grub tendremos que firmar el kernel cada vez que actualicemos el sistema En mi caso (Arch Linux) crearé un hook para que pacman firme el kernel cada vez que se actualiza el sistema

Copiaremos los siguientes archivos:

cp /usr/share/libalpm/hooks/90-mkinitcpio-install.hook /etc/pacman.d/hooks/90-mkinitcpio-install.hook
cp /usr/share/libalpm/scripts/mkinitcpio-install /usr/local/share/libalpm/scripts/

En el archivo /etc/pacman.d/hooks/90-mkinitcpio-install.hook cambiaremos:

Exec = /usr/share/libalpm/scripts/mkinitcpio-install

Por la siguiente linea

Exec = /usr/local/share/libalpm/scripts/mkinitcpio-install

Y en el archivo mkinitcpio-install ubicado en /usr/local/share/libalpm/scripts/ reemplazaremos esta linia de código

install -Dm644 "${line}" "/boot/vmlinuz-${pkgbase}"

Por la siguiente linea:

sbsign --key /path/to/db.key --cert /path/to/db.crt --output "/boot/vmlinuz-${pkgbase}" "${line}"

Helios Project 2023
Navegación
Contacto
  • Github
  • Linkedin