¿Por qué la gente generalmente encripta unidades de sus computadoras personales y, a veces, servidores? ¡Está claro que nadie ha robado fotos de sus gatos favoritos del disco! Eso es solo mala suerte: una unidad encriptada requiere que ingrese una frase clave del teclado en cada arranque, y es larga y aburrida. Eliminarlo para que al menos a veces no sea necesario reclutarlo. Sí, para que no se pierda el significado del cifrado.
Gato para llamar la atención Bueno, eliminarlo por completo no funcionará. En su lugar, puede crear un archivo de clave en una unidad flash USB, y también funcionará. Y sin una unidad flash (y sin una segunda computadora en la red) ¿es posible? Si tienes suerte con el BIOS, ¡casi puedes! Debajo del corte habrá una guía sobre cómo configurar el cifrado de disco a través de LUKS con las siguientes propiedades:
- La frase de contraseña o el archivo de clave no se almacenan en ningún lugar en forma abierta (o en la forma equivalente a abrir) cuando la computadora está apagada.
- La primera vez que enciende su computadora, debe ingresar una frase de contraseña.
- En reinicios posteriores (antes de cerrar), no se requiere una frase de contraseña.
Las instrucciones se han probado en CentOS 7.6, Ubuntu 19.04 y openSUSE Leap 15.1 en máquinas virtuales y en hardware real (computadora de escritorio, computadora portátil y dos servidores). Deberían trabajar en otras distribuciones que tengan una versión funcional de Dracut.
Y sí, en el buen sentido, esto debería haber terminado en el centro de "administración anormal del sistema", pero no existe dicho centro.
Sugiero usar una ranura separada en el contenedor LUKS y almacenar la clave ... ¡en la RAM!
¿Qué tipo de tragamonedas?El contenedor LUKS implementa el cifrado multinivel. Los datos útiles en el disco se cifran con un cifrado simétrico, generalmente aes-xts-plain64
. La clave de este cifrado simétrico (clave maestra) se genera en la etapa de creación del contenedor como una secuencia aleatoria de bytes. La clave maestra se almacena en forma cifrada, en el caso general, en varias copias (ranuras). Por defecto, solo uno de los ocho espacios está activo. Cada ranura activa tiene una frase clave separada (o un archivo de clave separado), con el que puede descifrar la clave maestra. Desde el punto de vista del usuario, resulta que puede desbloquear el disco usando cualquiera de las diferentes frases clave (o archivos clave). En nuestro caso, usando una frase clave (ranura 0) o usando una pieza de memoria utilizada como archivo clave (ranura 6).
El BIOS en la mayoría de las placas base no limpia la memoria durante el reinicio, o puede configurarlo para que no limpie (excepción conocida: "Intel Corporation S1200SP / S1200SP, BIOS S1200SP.86B.03.01.0042.013020190050 30/01/2019"). Por lo tanto, puede almacenar la clave allí. Cuando se apaga la alimentación, el contenido de la RAM se borra después de un tiempo, junto con una copia insegura de la clave.
Entonces vamos.
Paso uno: instale el sistema en un disco cifrado con LUKS
En este caso, la partición del disco (por ejemplo, /dev/sda1
) montada en /boot
debe permanecer sin cifrar, y la otra partición en la que todo lo demás (por ejemplo, /dev/sda2
) debe cifrarse. El sistema de archivos en la partición encriptada puede ser cualquiera, también puede usar LVM para que el sistema de archivos raíz, el volumen para el intercambio y todo lo demás excepto /boot
en el mismo contenedor. Esto corresponde a la partición de disco predeterminada en CentOS 7 y Debian al elegir la opción de cifrado. SUSE hace todo de manera diferente (encriptación /boot
) y, por lo tanto, requiere la partición manual del disco.
El resultado debería ser algo como lo siguiente:
$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 10G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 9G 0 part └─luks-d07a97d7-3258-408c-a17c-e2fb56701c69 253:0 0 9G 0 crypt ├─centos_centos--encrypt2-root 253:1 0 8G 0 lvm / └─centos_centos--encrypt2-swap 253:2 0 1G 0 lvm [SWAP]
En el caso de utilizar UEFI, también habrá una partición del sistema EFI.
Para usuarios de Debian y Ubuntu: reemplace el paquete initramfs-tools
con dracut
.
# apt install --no-install-recommends dracut
initramfs-tools
implementa una lógica incorrecta en nuestro caso, aplicada a secciones encriptadas con un archivo de clave. Dichas secciones se ignoran por completo o el contenido del archivo de clave se copia en initramfs (es decir, como resultado, en el disco) en claro, lo que no necesitamos.
Paso dos: cree un archivo de clave que se usará para desbloquear automáticamente la unidad después de un reinicio en caliente
128 bits aleatorios son suficientes para nosotros, es decir 16 bytes. El archivo se almacenará en un disco cifrado, por lo que nadie que no conozca la clave de cifrado y no tenga acceso de root al sistema cargado no lo leerá.
# touch -m 0600 /root/key # head -c16 /dev/urandom > /root/key
Hay suficientes bits verdaderamente aleatorios en el archivo de claves para que el algoritmo lento PBKDF, que hace una clave de cifrado difícil de elegir a partir de una frase clave potencialmente débil, no sea realmente necesaria. Por lo tanto, al agregar una clave, puede reducir el número de iteraciones:
# cryptsetup luksAddKey --key-slot=6 --iter-time=1 /dev/sda2 /root/key Enter any existing passphrase:
Como puede ver, el archivo de clave se almacena en un disco encriptado y, por lo tanto, no representa ningún riesgo de seguridad si la computadora está apagada.
Paso tres: asigne espacio en la memoria física para almacenar la clave
Linux tiene al menos tres controladores diferentes que le permiten acceder a la memoria física en una dirección conocida. Este es linux/drivers/char/mem.c
, que también es responsable del dispositivo /dev/mem
, así como de los módulos phram
(emula un chip MTD, proporciona el dispositivo /dev/mtd0
) y nd_e820
(utilizado cuando se trabaja con NVDIMM, proporciona /dev/pmem0
). Todos tienen sus características desagradables:
/dev/mem
no se puede escribir cuando se utiliza el Arranque seguro si la distribución usa el conjunto de parches LOCKDOWN de Matthew Garrett (y este conjunto de parches es necesario si la distribución va a admitir Arranque seguro con un gestor de arranque firmado por Microsoft);phram
no phram
disponible en CentOS y Fedora: el mantenedor simplemente no habilitó la opción correspondiente al construir el núcleo;nd_e820
necesita reservar al menos 128 megabytes de memoria: así es como funciona NVDIMM. Pero esta es la única opción que se ejecuta en CentOS con arranque seguro.
Como no hay una opción ideal, las tres se consideran a continuación.
Cuando se usa cualquiera de los métodos, se necesita un cuidado extremo para no afectar accidentalmente los dispositivos o rangos de memoria que no sean los necesarios. Esto es especialmente cierto para las computadoras que ya tienen chips MTD o módulos NVDIMM. Es decir, /dev/mtd0
o /dev/pmem0
pueden no ser el dispositivo que corresponde al área de memoria reservada para almacenar la clave. La numeración de los dispositivos existentes, en los que se basan los archivos de configuración y los scripts, también puede confundirse. En consecuencia, se recomienda que deshabilite temporalmente todos los servicios que dependen de los dispositivos existentes /dev/mtd*
y /dev/pmem*
.
Se realiza una memmap
memoria física de Linux pasando la opción memmap
al memmap
. Estamos interesados en dos tipos de esta opción:
memmap=4K$0x10000000
reservas (es decir, marcas reservadas para que el núcleo en sí no use) 4 kilobytes de memoria, comenzando con la dirección física 0x10000000;memmap=128M!0x10000000
marca 128 megabytes de memoria física, comenzando en la dirección 0x10000000, como NVDIMM (obviamente falso, pero será suficiente para nosotros).
La opción c $
es adecuada para usar con /dev/mem
y phram
, ¡la opción c !
- para nd_e820
. Cuando se usa $
dirección inicial de la memoria reservada debe ser un múltiplo de 0x1000
(es decir, 4 kilobytes), cuando se usa !
- un múltiplo de 0x8000000
(es decir, 128 megabytes).
Importante: el signo de dólar ( $
) en los archivos de configuración de GRUB es un carácter especial y debe ser escapado. Y dos veces: una vez, al generar grub.cfg
desde /etc/default/grub
, una segunda vez, al interpretar el archivo de configuración resultante en la etapa de arranque. Es decir en /etc/default/grub
, la siguiente línea debería aparecer finalmente:
GRUB_CMDLINE_LINUX="memmap=4K\\\$0x10000000 ... ..."
Sin doble escape del signo $
, el sistema simplemente no arrancará, ya que pensará que solo tiene 4 kilobytes de memoria. No existen tales dificultades con un signo de exclamación:
GRUB_CMDLINE_LINUX="memmap=128M!0x10000000 ... ..."
El root
puede acceder a la tarjeta de memoria física (y es necesaria para averiguar qué direcciones reservar) en el /proc/iomem
:
# cat /proc/iomem ... 000f0000-000fffff : reserved 000f0000-000fffff : System ROM 00100000-7ffddfff : System RAM 2b000000-350fffff : Crash kernel 73a00000-7417c25e : Kernel code 7417c25f-747661ff : Kernel data 74945000-74c50fff : Kernel bss 7ffde000-7fffffff : reserved 80000000-febfffff : PCI Bus 0000:00 fd000000-fdffffff : 0000:00:02.0 ...
La RAM está marcada como "RAM del sistema", es suficiente para nosotros reservar una de sus páginas para almacenar la clave. Adivinar qué parte de la memoria del BIOS no toca al reiniciar no funcionará de manera confiable por adelantado. A menos que haya otra computadora con exactamente la misma versión de BIOS y la misma configuración de memoria en la que ya se ha completado este manual. Por lo tanto, en el caso general, deberá actuar por prueba y error. Como regla general, cuando el BIOS se reinicia, cambia los datos solo al principio y al final de cada rango de memoria. Por lo general, es suficiente retirarse 128 megabytes ( 0x8000000
) de los bordes. Para máquinas virtuales KVM con 1 GB de memoria o más, las opciones propuestas ( memmap=4K$0x10000000
y memmap=128M!0x10000000
) funcionan.
Al usar el módulo phram
, necesitamos otro parámetro de línea de comando del núcleo, que, de hecho, le dice al módulo qué parte de la memoria física debe usar: la nuestra, reservada. El parámetro se llama phram.phram
y contiene tres partes: el nombre (arbitrario de hasta 63 caracteres, será visible en sysfs
), la dirección inicial y la longitud. La dirección de inicio y la longitud deben ser las mismas que en memmap
, pero los sufijos K
y M
no M
compatibles.
GRUB_CMDLINE_LINUX="memmap=4K\\\$0x10000000 phram.phram=savedkey,0x10000000,4096 ..."
Después de editar /etc/default/grub
necesita regenerar el archivo de configuración real que GRUB lee en el arranque. El comando correcto para esto depende de la distribución.
# grub2-mkconfig -o /boot/grub2/grub.cfg # CentOS (Legacy BIOS) # grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg # CentOS (UEFI) # update-grub # Debian, Ubuntu # update-bootloader --reinit # SUSE
Después de actualizar la configuración de GRUB, la computadora debe reiniciarse, pero lo haremos más adelante cuando actualicemos initramfs.
Cuarto paso: configurar LUKS para leer la clave de la memoria
/etc/crypttab
configuración de cifrado de la /etc/crypttab
se almacena en el /etc/crypttab
. Cada fila consta de cuatro campos:
- el dispositivo que se debe obtener al desbloquear,
- dispositivo encriptado
- dónde obtener el archivo de clave (
none
significa ingresar una frase clave desde el teclado), - campo opcional para opciones.
Si el archivo de clave existe, pero no cabe, entonces Dracut solicita la frase clave. Lo que, de hecho, será necesario en el primer arranque.
Un ejemplo del /etc/crypttab
de una distribución recién instalada:
# cat /etc/crypttab # luks-d07....69 UUID=d07....69 none
El archivo clave en nuestro caso será una pieza de memoria física. Es decir /dev/mem
, /dev/mtd0
o /dev/pmem0
, dependiendo de la tecnología de acceso a memoria seleccionada. Se necesitan opciones para indicar qué parte del archivo es la clave.
# cat /etc/crypttab # # /dev/mem: luks-d07....69 UUID=d07....69 /dev/mem keyfile-offset=0x10000000,keyfile-size=16 # phram: luks-d07....69 UUID=d07....69 /dev/mtd0 keyfile-size=16 # nd_e820: luks-d07....69 UUID=d07....69 /dev/pmem0 keyfile-size=16
Eso es solo que no funcionará así.
El punto es cómo systemd determina cuándo se puede desbloquear un dispositivo. Es decir, toma el dispositivo de la tercera columna y espera a que la unidad de dispositivo correspondiente se active. Parece lógico: no tiene sentido intentar desbloquear el contenedor LUKS hasta que aparezca un dispositivo con un archivo de clave. Pero la unidad del dispositivo no es lo mismo que el dispositivo en sí. Systemd por defecto crea unidades de dispositivo solo para dispositivos kernel relacionados con subsistemas de dispositivos de bloque e interfaces de red. Los dispositivos /dev/mem
y /dev/mtd0
son caracteres por carácter, por lo tanto, no se supervisan de forma predeterminada y nunca se reconocerán como listos.
Tendrá que decirle a systemd que debe rastrearlos creando reglas udev en el archivo /etc/udev/rules.d/99-mem.rules
:
# /dev/mem KERNEL=="mem", TAG+="systemd" # /dev/mtd* KERNEL=="mtd*", TAG+="systemd" # /dev/pmem*
Paso cinco: regenerar initramfs
Te recuerdo: este artículo solo trata las distribuciones que usan Dracut. Incluyendo aquellos donde no se usa por defecto, pero es accesible y eficiente.
Necesita regenerar initramfs para actualizar el /etc/crypttab
allí. Y también, para incluir módulos de kernel adicionales y reglas udev allí. De lo contrario, no se creará el dispositivo /dev/mtd0
o /dev/pmem0
. El parámetro de configuración Dracut force_drivers
es responsable de habilitar y cargar módulos de kernel adicionales, y install_items
es responsable de archivos adicionales. Creamos el archivo /etc/dracut.conf.d/mem.conf
con los siguientes contenidos (se necesita un espacio después de la comilla de apertura, este es un separador):
# /dev/mem: install_items+=" /etc/udev/rules.d/99-mem.rules" # phram: install_items+=" /etc/udev/rules.d/99-mem.rules" force_drivers+=" phram" # nd_e820: force_drivers+=" nd_e820 nd_pmem"
En realidad, la regeneración initramfs:
# dracut -f
Para los usuarios de Debian y Ubuntu, el responsable puso un rastrillo: el archivo resultante se llama incorrectamente. Debe cambiarle el nombre para que tenga el mismo nombre que se prescribe en la configuración de GRUB:
# mv /boot/initramfs-5.0.0-19-generic.img /boot/initrd.img-5.0.0-19-generic
Al instalar nuevos núcleos, la creación automática de initramfs a través de Dracut se lleva a cabo correctamente, el error afecta solo el inicio manual de dracut -f
.
Paso seis: reinicie su computadora
Es necesario reiniciar para que los cambios en la configuración de GRUB y Dracut surtan efecto.
# reboot
En esta etapa, no hay una clave en la memoria, por lo que deberá ingresar una frase de contraseña.
Después de reiniciar, debe verificar si la copia de seguridad de la memoria funcionó correctamente. Como mínimo, en el /proc/iomem
memoria requerida debe marcarse como "reservada" (cuando se usa /dev/mem
o phram
) o como "Memoria persistente (heredada)".
Incluso cuando use phram
o nd_e820
debe asegurarse de que el dispositivo /dev/mtd0
o /dev/pmem0
realmente se refiera al área de memoria previamente reservada, y no a otra cosa.
# cat /sys/class/mtd/mtd0/name # : "savedkey" # cat /sys/block/pmem0/device/resource #
Si esto no es así, entonces necesita encontrar cuál de los dispositivos /dev/mtd*
o /dev/pmem*
"nuestro", y luego arreglar / etc / crypttab, regenerar initramfs y volver a verificar el resultado después de otro reinicio.
Séptimo paso: configure la copia del archivo de clave a la memoria
El archivo de clave se copiará en la memoria antes de reiniciar. Una de las formas de ejecutar cualquier comando en la etapa de apagado del sistema es registrarlo en la directiva ExecStop
en el servicio systemd. Para que systemd entienda que esto no es un demonio y no jure por la falta de la directiva ExecStart
, debe especificar el tipo de servicio como oneshot
y también sugerir que se considera que el servicio se está ejecutando, incluso si no hay ningún proceso de trabajo asociado. Entonces, aquí está el archivo /etc/systemd/system/savekey.service
. Es necesario dejar solo una de las variantes dadas de la directiva ExecStop
.
[Unit] Description=Saving LUKS key into RAM Documentation=https://habr.com/ru/post/457396/ [Service] Type=oneshot RemainAfterExit=true # /dev/mem: ExecStop=/bin/sh -c 'dd if=/root/key of=/dev/mem bs=1 seek=$((0x10000000))' # /dev/mtd0: ExecStop=/bin/dd if=/root/key of=/dev/mtd0 # /dev/pmem0: ExecStop=/bin/dd if=/root/key of=/dev/pmem0 [Install] WantedBy=default.target
La construcción con /bin/sh
necesaria ya que dd
no entiende la notación hexadecimal.
Activamos el servicio, verificamos:
# systemctl enable savekey # systemctl start savekey # reboot
Durante el reinicio posterior, no es necesario que ingrese la frase de contraseña desde el disco. Y si es necesario, esto generalmente significa que la dirección de inicio del área de memoria reservada se selecciona incorrectamente. Está bien arreglar y regenerar varios archivos y reiniciar la computadora dos veces.
Cuando se utiliza phram
o nd_e820
solo se tendrá que editar la configuración de GRUB. Cuando se usa /dev/mem
dirección de inicio también se menciona en /etc/crypttab
(por lo tanto, initramfs tendrá que ser regenerado) y en el servicio systemd.
Pero eso no es todo.
Problemas de seguridad
Cualquier discusión sobre problemas de seguridad se basa en un modelo de amenaza. Es decir sobre los objetivos y los medios del atacante. Soy consciente de que algunos de los ejemplos a continuación son descabellados.
Las situaciones con acceso físico a una computadora apagada no son diferentes de las que no tienen un almacenamiento de claves configurado en la memoria. Existen los mismos tipos de ataques destinados a obtener frases clave, incluida Evil Maid , y las mismas características de seguridad . No nos detenemos en ellos, ya que no hay nada nuevo.
Las situaciones más interesantes son cuando la computadora está encendida.
Situación 1 . El atacante no tiene acceso físico a la computadora, no conoce la frase de contraseña, pero tiene acceso raíz a través de ssh. El objetivo es la clave para descifrar el disco. Por ejemplo, para acceder a copias de seguridad antiguas sector por sector de una imagen de disco de máquina virtual.
En realidad, la clave en el platillo está en el archivo /root/key
. La pregunta es cómo se relaciona esto con lo que sucedió antes de la implementación de esta instrucción. Respuesta: para luks1, la amenaza no es nueva. Hay una dmsetup table --target crypt --showkeys
que muestra la clave maestra, es decir También datos que permiten el acceso a copias de seguridad antiguas. Para luks2, la reducción de seguridad en este escenario realmente tiene lugar: las claves dm-crypt se almacenan en el llavero a nivel del núcleo, y es imposible mirarlas desde el espacio del usuario.
Situación 2 . El atacante puede usar el teclado y mirar la pantalla, pero no está listo para abrir la carcasa. Por ejemplo, usé la contraseña filtrada de IPMI o intercepté una sesión de noVNC en la nube. No conoce la frase clave, tampoco conoce otras contraseñas. El objetivo es el acceso de root.
Por favor: reinicie a través de Ctrl-Alt-Del
, agregando la opción de kernel init=/bin/sh
través de GRUB. La frase de contraseña no era necesaria, ya que la clave se leyó con éxito desde la memoria. Para protegerte de esto, deberías evitar que GRUB cargue lo que no está en el menú. Desafortunadamente, esta funcionalidad se implementa de manera diferente en diferentes distribuciones.
A partir de la versión 7.2, CentOS tiene el grub2-setpassword
, que en realidad protege a GRUB con una contraseña. Otras distribuciones pueden tener sus propias utilidades para la misma tarea. Si no lo están, puede editar directamente los archivos en el directorio /etc/grub.d
y regenerar grub.cfg
.
En el archivo /etc/grub.d/10_linux
, cambie la variable CLASS, agregue la opción --unrestricted
al final, si no estaba allí:
CLASS="--class gnu-linux --class gnu --class os --unrestricted"
En el archivo /etc/grub.d/40_custom
agregue las líneas que especifican el nombre de usuario y la contraseña que se necesitan para editar la línea de comando del núcleo:
set superusers="root" password_pbkdf2 root grub.pbkdf2....... # grub2-mkpasswd-pbkdf2
O, si es necesario deshabilitar dicha funcionalidad, aquí hay una línea como esta:
set superusers=""
Situación 3 . El atacante tiene acceso a la computadora incluida, lo que le permite iniciar desde medios no confiables. Esto puede ser acceso físico sin abrir el caso o acceso a través de IPMI. El objetivo es el acceso de root.
Puede cargar su GRUB desde una unidad flash USB o CD-ROM y agregar init=/bin/sh
a los parámetros de su núcleo, como en el ejemplo anterior. En consecuencia, el arranque desde cualquier medio horrible debería estar prohibido en el BIOS. Y también proteja el cambio de la configuración del BIOS con una contraseña.
Situación 4 . El atacante tiene acceso físico a la computadora incluida, incluida la capacidad de abrir el caso. El objetivo es encontrar la clave u obtener acceso de root.
En general, esta es una situación perdedora de todos modos. El ataque a los módulos de memoria enfriándolos ( ataque de arranque en frío ) no se ha cancelado. También teóricamente (no se verificó), puede aprovechar el hecho de que los discos SATA modernos admiten la reconexión en caliente. Cuando la computadora se reinicie, desconecte el disco, cambie grub.cfg
por init=/bin/sh
, vuelva a conectar, permita que el sistema se reinicie. Resulta (si entiendo correctamente) el acceso a la raíz.
Un empleado sin escrúpulos de un alojamiento en la nube puede hacer lo mismo haciendo una instantánea de una máquina virtual con su posterior modificación.
Otros asuntos
Mantener la clave en la memoria durante un reinicio es una burla. Use-after-free en su forma más pura. Una solución más limpia es usar kexec y agregar la clave a initramfs generados dinámicamente. También protege contra la sustitución de los parámetros del kernel . Sí, si kexec está funcionando. Las distribuciones modernas han hecho que la configuración de kexec sea demasiado complicada .
En los centros de datos, y más aún en la nube, la energía nunca desaparece. Resulta que la frase clave ya no es necesaria? De hecho, si está tan seguro de esto, puede eliminarlo. Resultará ser un servidor en funcionamiento, la clave del disco que nadie conoce¹ y, por lo tanto, no cederá, pero el sistema en el que se puede actualizar utilizando medios regulares. — sudo poweroff
.
¹ /root/key
— , cron.
? IPMI, . IPMI Java. .
? SSH . ! . , sudo reboot
, ?
, . SSH , . , , .