ukify Linux: armá Unified Kernel Images sin objcopy

En pocas palabras: ukify, la herramienta oficial de systemd, empaqueta kernel, initrd, cmdline y metadata en un único binario PE/COFF firmable (Unified Kernel Image), según la especificación UAPI.5. En Debian 13 y Ubuntu con systemd 257 se instala con apt install systemd-ukify.

Ponele que administrás un puñado de servidores Debian con Secure Boot activado y cada actualización de kernel te obliga a rearmar a mano el initrd, la línea de comandos y las firmas. ukify es la herramienta que trae systemd para automatizar exactamente eso: empaqueta kernel, initrd, cmdline y metadata en un solo binario PE/COFF firmable, la Unified Kernel Image (UKI). En Debian 13 y Ubuntu con paquetes systemd de la serie 257 se instala con apt install systemd-ukify, según describe la guía técnica publicada en dev.to.

Una Unified Kernel Image es, según la especificación UAPI.5, un archivo PE/COFF con el campo Subsystem seteado en IMAGE_SUBSYSTEM_EFI_APPLICATION que combina el stub de arranque UEFI, el kernel Linux en la sección .linux (la única obligatoria) y, de forma opcional, initrd, cmdline, splash, microcódigo y metadata de firma. El firmware o el boot loader tratan todo ese paquete como una sola unidad de confianza.

En 30 segundos

  • ukify es la herramienta oficial: systemd la recomienda para construir UKIs en Debian 13 y Ubuntu, vía el paquete systemd-ukify.
  • Solo una sección es obligatoria: .linux. Todo lo demás (.initrd, .cmdline, .osrel, .splash) es opcional.
  • Armar la UKI con objcopy a mano funciona, pero es frágil: alineación de secciones, merge de SBAT y firma Secure Boot se rompen fácil.
  • ukify genkey genera las claves de Secure Boot y PCR: el certificado dura 3650 días (10 años) por defecto.
  • systemd-measure calcula y firma los valores de TPM2 PCR 11, aunque el propio manual lo marca todavía como experimental.

¿Qué es una Unified Kernel Image (UKI) y qué secciones PE incluye?

Una UKI junta en un único ejecutable UEFI lo que normalmente son piezas sueltas del arranque: kernel, initrd, línea de comandos e información de versión. La especificación UAPI.5 define el orden canónico de las secciones, algo necesario porque las mediciones de TPM dependen de ese orden exacto. systemd-stub es la implementación de referencia: carga cada sección, la mide en PCR 11 y transiciona al kernel.

Acá el detalle importa. Que .osrel sea opcional no significa que convenga dejarla vacía: la fuente advierte que otras herramientas (bootctl, kernel-install) pueden dejar de reconocer el artefacto como UKI si falta.

Sección PEContenido¿Obligatoria?
.linuxImagen ELF del kernel
.osrelContenido de /etc/os-releaseNo
.cmdlineLínea de comandos del kernelNo
.initrdInitramfsNo
.ucodeInitrd de microcódigo (sin comprimir, va primero)No
.splashImagen BMP de arranqueNo
.dtb / .dtbautoDeviceTree fijo o auto-detectadoNo
.unameSalida de uname -rNo
.sbatMetadata de revocación SBAT (Shim)No
.pcrsigValores esperados de PCR 11, firmados (JSON)No
.pcrpkeyClave pública PEM asociada a .pcrsigNo
ukify linux diagrama explicativo

¿Por qué no conviene armar una UKI a mano con objcopy?

Porque objcopy te arma el PE, pero no te garantiza nada más. La propia especificación UAPI admite que “en principio” un objcopy bien invocado alcanza para producir el archivo, y sin embargo recomienda usar un helper como ukify. El motivo lo explica la guía técnica sin vueltas: alineación de secciones, merge del CSV de SBAT, firma Secure Boot del PE completo y cálculo de las mediciones de PCR 11 son pasos donde un error sutil rompe el modelo de confianza entero.

Pensalo en términos prácticos: subís el kernel a mano con objcopy, te olvidás de alinear una sección, el PE queda mal formado. El firmware capaz lo carga igual porque no siempre valida estrictamente esa alineación, pero cuando llega el momento de la firma Secure Boot o del cálculo de PCR 11 todo se desincroniza. Ahí terminás con un sistema que bootea sin problemas pero no logra desbloquear LUKS con la política TPM que esperabas, y el error no se ve hasta ese punto. Ya cubrimos un caso parecido de “funciona pero no del todo” en el soporte para el chip Apple T1.

¿Y si igual insistís con el objcopy “artesanal”? Podés, nadie te lo impide. Pero vas a estar depurando offsets de sección en vez de resolver el problema real que tenías: firmar y medir el arranque de forma confiable.

¿Cómo instalo ukify en Debian 13 o Ubuntu?

Se instala con apt. En Debian 13 y Ubuntu con paquetes systemd de la serie 257, el comando es sudo apt install systemd-ukify systemd-boot-efi sbsigntool, según la guía citada. El paquete systemd-ukify trae como dependencia python3-pefile.

sudo apt update
sudo apt install systemd-ukify systemd-boot-efi sbsigntool
# Opcional pero útil
sudo apt install systemd-boot binutils

# Verificar la instalación
ukify --version
ukify --help | head

El stub EFI que ukify usa para armar el binario vive en /usr/lib/systemd/boot/efi/ (linuxx64.efi.stub, linuxia32.efi.stub, linuxaa64.efi.stub, según arquitectura). Tres verbos vas a usar todo el tiempo: ukify build para armar la UKI, ukify genkey para generar claves, y ukify inspect para revisar lo que construiste.

¿Cómo construyo una UKI mínima sin firmar con ukify build?

Con ukify build y cuatro flags básicos (–linux, –initrd, –cmdline, –output) armás una UKI funcional en un solo comando. La fuente propone un laboratorio concreto: detectar la versión del kernel corriendo, ubicar el vmlinuz y el initrd que ya generó tu gestor de paquetes, y pasarlos a ukify. Lo explicamos a fondo en las limitaciones de Omarchy en Mac.

KERNEL_VER="$(uname -r)"
LINUX="/boot/vmlinuz-${KERNEL_VER}"
INITRD="/boot/initrd.img-${KERNEL_VER}"

ukify build \
 --linux="$LINUX" \
 --initrd="$INITRD" \
 --cmdline='quiet rw' \
 --os-release=@"/etc/os-release" \
 --uname="$KERNEL_VER" \
 --output="minimal.unsigned.efi"

Si omitís --output=, ukify nombra el archivo con el sufijo .unsigned.efi o .signed.efi dependiendo de si corrió firma Secure Boot. Para hosts reales, la fuente recomienda mover la política a un archivo de config (/etc/kernel/uki.conf) en vez de pasar todo por línea de comandos, así kernel-install la reutiliza en cada actualización de paquete.

¿Cómo inspecciono una UKI ya construida?

ukify inspect te muestra qué secciones quedaron embebidas en el PE, sus tamaños y, si pedís JSON, un resumen estructurado. Es el primer paso antes de firmar cualquier cosa: si algo salió mal en el build, se ve acá.

# Ver todas las secciones
ukify inspect minimal.unsigned.efi

# Leer .cmdline como texto
ukify inspect minimal.unsigned.efi --section=.cmdline:text

# Resumen en JSON (ukify >= 255)
ukify inspect minimal.unsigned.efi --json=pretty | head -n 80

Ojo con esto: dejar .osrel vacía es válido para ukify, pero no recomendado, porque bootctl y kernel-install pueden dejar de tratar el archivo como UKI. Un detalle chico que te puede costar media tarde de debugging si no lo sabés de antemano.

¿Cómo firmo una UKI con Secure Boot usando ukify genkey?

ukify genkey genera el par de claves de Secure Boot (y, si los declarás, los pares de PCR) a partir de lo que definiste en el archivo de config. La certificación dura 3650 días (10 años) por defecto, salvo que la sobreescribas con SecureBootCertificateValidity=. En nuestra guía de fundamentos de Linux para DevOps profundizamos sobre esto.

# /etc/kernel/uki.conf
[UKI]
Cmdline=quiet rw
OSRelease=@/etc/os-release
SecureBootPrivateKey=/etc/kernel/secure-boot-key.pem
SecureBootCertificate=/etc/kernel/secure-boot-certificate.pem
SignKernel=yes

# Generar las claves una sola vez
sudo ukify genkey --config=/etc/kernel/uki.conf
sudo chmod 600 /etc/kernel/secure-boot-key.pem
sudo chmod 644 /etc/kernel/secure-boot-certificate.pem

Para firmar, ukify soporta tres herramientas: sbsign (la que usa por defecto), pesign (contra una base de certificados NSS) y systemd-sbsign (con soporte para providers de OpenSSL). El límite de responsabilidades queda claro en la propia fuente: sbctl maneja el enrolamiento de PK/KEK/db de la plataforma, mientras que ukify arma y firma la UKI en sí. Un dato que conviene tener presente si estás firmando kernels: con Secure Boot activo y una sección .cmdline presente, el firmware ignora cualquier intento de override en el momento del arranque, porque la línea de comandos firmada es parte de la imagen de confianza. Si necesitás overrides locales, la alternativa es omitir .cmdline del build principal y distribuir addons firmados aparte.

¿Para qué sirve la medición TPM2 PCR 11 en el arranque?

systemd-stub mide la mayoría de las secciones de la UKI en PCR 11 del TPM (la sección .pcrsig queda afuera de la medición para no ser circular). systemd-measure precalcula esos valores del mismo modo que lo hará el stub, opcionalmente los firma, y ukify los embebe en la sección .pcrsig.

El caso de uso típico: desbloquear LUKS solo para los kernels que vos firmaste, vía políticas TPM2 con systemd-cryptenroll, o liberar credenciales cifradas con systemd-creds únicamente para UKIs conocidas. La fuente detalla los phases por defecto que usa systemd-measure cuando no le pasás --phases=: enter-initrd, enter-initrd:leave-initrd, enter-initrd:leave-initrd:sysinit, y enter-initrd:leave-initrd:sysinit:ready. Podés separar claves distintas para secretos de initrd y secretos de runtime, cada una atada a su propio conjunto de phases.

¿Esto está listo para producción sin dudar? No del todo. El propio manual de systemd-measure lo marca todavía como experimental, así que la recomendación de la guía es pasar siempre por ukify en vez de armar el JSON de PCR a mano. Más contexto en la comparativa entre Apache y Nginx.

¿Cuándo conviene firmar y medir, y cuándo alcanza con lo básico? (criterios de decisión)

No todos los escenarios necesitan el combo completo de Secure Boot + PCR 11. Antes de meterte con genkey y systemd-measure, conviene distinguir qué problema estás resolviendo en cada máquina. Estos criterios salen directo de lo que definen las fuentes sobre para qué sirve cada mecanismo:

Situación¿Necesitás firmar con Secure Boot (ukify genkey)?¿Necesitás medición PCR 11 (systemd-measure)?
Laptop o VM personal, sin Secure Boot habilitado en firmwareNoNo
Servidor con Secure Boot activo, sin cifrado de disco atado al TPMNo — alcanza con firmar el PE completo
Servidor con LUKS + systemd-cryptenroll para desbloqueo automático solo con kernels de confianzaSí, necesitás .pcrsig firmado para que la política TPM tenga sentido
Kernel de test o debug que armás y tirás seguido, sin pasar por producciónNo, podés dejarlo sin firmarNo

Ejemplo hipotético (a modo ilustrativo, no un caso real reportado): imaginá que administrás tres servidores Debian 13 con Secure Boot habilitado en el firmware. En dos de ellos el disco raíz está cifrado con LUKS y se desbloquea automáticamente vía TPM con systemd-cryptenroll; el tercero no usa cifrado de disco. Siguiendo los criterios de la tabla, en los tres necesitás firmar la UKI con ukify genkey porque Secure Boot está activo. Pero solo en los dos servidores con LUKS+TPM tiene sentido el trabajo extra de declarar bloques [PCRSignature:initrd] y [PCRSignature:system] en el config, con sus phases separados. En el tercer servidor, alcanza con SecureBootPrivateKey= y SecureBootCertificate= en /etc/kernel/uki.conf, sin tocar nada de PCR. Meter systemd-measure ahí solo agregaría una pieza experimental sin ningún beneficio real.

Microcódigo, initrds múltiples y addons: lo que no siempre hace falta pero conviene conocer

Initrd= / --initrd= puede repetirse: ukify concatena los initrds en orden dentro de una única sección .initrd. El microcódigo, en cambio, tiene su propia sección dedicada (.ucode, vía Microcode= / --microcode=), que el stub entrega al kernel antes que el resto de los initrds y que debe estar sin comprimir.

ukify build \
 --linux="$LINUX" \
 --microcode=/boot/intel-ucode.img \
 --initrd="$INITRD" \
 --cmdline='quiet rw' \
 --output="with-ucode.efi"

Para casos donde necesitás una línea de comandos extra sin rearmar toda la UKI base, existen los PE addons: binarios *.addon.efi que systemd-stub carga desde ESP/.../foo.efi.extra.d/ o desde ESP/loader/addons/. No son UKIs completas —no pueden llevar sección .linux— pero sí pueden traer .cmdline, .initrd, .ucode o .dtb. Si Secure Boot está activo, firmalos con la misma clave que la UKI principal, porque el stub ignora companions sin firmar en plataformas con la verificación forzada.

Errores comunes al trabajar con UKIs y ukify

  • Dejar .osrel vacía “porque total es opcional”: técnicamente ukify lo permite, pero bootctl y kernel-install pueden dejar de reconocer el archivo como UKI válida en el menú de arranque.
  • Mezclar objcopy manual con firma Secure Boot: si armaste la base con objcopy y después intentás firmarla o medirla con las herramientas de systemd, la alineación de secciones puede no coincidir con lo que el stub espera.
  • Esperar que un cmdline override funcione con Secure Boot activo: si la UKI tiene sección .cmdline y está firmada, el firmware ignora cualquier parámetro pasado por fuera. La solución es no incluir .cmdline en el build o usar addons firmados aparte.
  • Tratar los addons sin firmar como confiables: en plataformas con Secure Boot enforced, el stub ignora companions (*.addon.efi) que no estén firmados con la misma cadena de confianza.
  • Confiar ciegamente en systemd-measure como si fuera estable: el manual lo marca experimental. Conviene pasar siempre por ukify en vez de generar el JSON de PCR a mano.
  • Configurar PCRSignature sin necesitarlo: si no vas a atar ningún secreto (LUKS, systemd-creds) a la identidad del kernel, agregar bloques PCR solo suma superficie experimental sin beneficio, según el criterio de decisión de más arriba.

Todo esto aplica igual en una laptop con Secure Boot que en un servidor bare-metal o un VPS Linux con firmware UEFI. Si estás por levantar infraestructura Linux en Argentina y necesitás controlar vos mismo estas configuraciones de arranque, en donweb.com podés conseguir servidores con acceso completo para este tipo de trabajo.

Preguntas Frecuentes

¿Qué es una Unified Kernel Image (UKI) en Linux?

Una UKI es un archivo PE/COFF que combina el kernel Linux, el initrd, la línea de comandos y otra metadata en un solo binario UEFI firmable, según define la especificación UAPI.5. Solo la sección .linux es obligatoria; el resto (cmdline, initrd, splash, etc.) es opcional según qué necesites embeber.

¿Cómo instalo ukify en Debian o Ubuntu?

Con sudo apt install systemd-ukify systemd-boot-efi sbsigntool en Debian 13 o Ubuntu con systemd de la serie 257. Después de instalarlo, confirmá que quedó operativo con ukify --version.

¿Cómo firmo una UKI con Secure Boot?

Generás el par de claves con ukify genkey --config=/etc/kernel/uki.conf, declarando SecureBootPrivateKey y SecureBootCertificate en ese archivo. Después corrés ukify build --config=/etc/kernel/uki.conf ... y ukify firma el PE completo usando sbsign, pesign o systemd-sbsign según lo que configures.

¿Para qué sirve la medición TPM2 PCR 11 en el arranque?

PCR 11 guarda un hash acumulado de las secciones de la UKI que systemd-stub mide durante el arranque, lo que permite atar secretos (como el desbloqueo de LUKS con systemd-cryptenroll) a una imagen de kernel específica. systemd-measure calcula y firma esos valores esperados antes de que ukify los embeba en la sección .pcrsig.

¿Cuándo tiene sentido usar Secure Boot sin PCR 11, o al revés?

Si tu único objetivo es que el firmware verifique que la UKI no fue alterada, alcanza con firmarla vía Secure Boot (ukify genkey + build). PCR 11 solo aporta valor adicional cuando además querés atar secretos —como el desbloqueo de LUKS o credenciales de systemd-creds— a que se haya booteado exactamente esa UKI y no otra. Sin ese caso de uso concreto, activar PCR 11 solo suma una pieza que el propio manual de systemd-measure marca como experimental.

¿Cuál es la diferencia entre ukify y armar la UKI a mano con objcopy?

objcopy puede producir un PE técnicamente válido, pero no maneja de forma segura la alineación de secciones, el merge de metadata SBAT, la firma Secure Boot ni el cálculo de mediciones PCR 11. ukify automatiza esos cuatro pasos y es la herramienta que recomienda la propia especificación UAPI.5.

Conclusión

ukify no inventa nada nuevo sobre el papel: el formato UKI existe hace rato y siempre se pudo armar con objcopy. Lo que cambia es que ahora tenés una herramienta que hace bien la parte tediosa (alineación, SBAT, firma, PCR) y que además se integra con kernel-install, así que cada actualización de kernel produce automáticamente una UKI consistente en vez de depender de un script casero que alguien escribió hace tres años y nadie mantiene.

Si administrás sistemas con Secure Boot o pensás atar secretos al arranque vía TPM, vale la pena migrar el flujo a ukify antes que seguir parcheando un objcopy manual. Pero no hace falta ir directo al combo completo: empezá por el laboratorio sin firmar, inspeccioná el resultado con ukify inspect, sumá Secure Boot solo si tu firmware lo exige, y dejá PCR 11 para cuando tengas un secreto concreto (LUKS, systemd-creds) que quieras atar a esa imagen específica. Usar los tres criterios de la tabla de arriba antes de tocar systemd-measure te ahorra configurar una capa experimental que no ibas a necesitar.

Fuentes

Te puede interesar...