Всем привет! На связи Алексей Гаврилов, DevOps-инженер компании «Флант». Эта статья предназначена для довольно искушённых пользователей Linux. Я покажу, как устанавливать Debian или его аналоги стандартным установщиком в Secure boot. Эту установку я проверил на AWS ARM64 и в Selectel Cloud. Также конечные скрипты работают на служебном Lenovo ThinkPad T14 и личном L380 Yoga.

Чего в итоге мы добьёмся:

  1. Включённый Secure boot с личными ключами для него. Так мы получим возможность загружать только EFI-файлы, подписанные нашим ключом. Это исключит возможность запуска сторонних EFI-файлов, подписанных другими ключами, например, Microsoft или производителем железа.

  2. Файл ядра и initramfs, которые подписаны нашим ключом. Это возможно благодаря использованию UKI. Так мы получаем EFI-файл, содержимое которого подписано. Это позволяет нам исключить из последовательности загрузки grub или systemd-boot. Исключение загрузчиков нужно для уменьшения возможного вектора взлома ноутбука.

  3. Зашифрованные разделы, кроме EFI boot. Так мы получим возможность исключить утечку данных в случае кражи ноутбука, а также усложним жизнь потенциальному взломщику тем, что в его распоряжении будут только подписанные EFI-файлы.

Ручной вариант установки

Прежде чем перейти к установке, убедимся, что Secure Boot находится в режиме настройки. При этом настройки UEFI BIOS должны быть защищены паролем, чтобы Secure boot нельзя было отключить.

Дальше работаем по следующему алгоритму:

1. Нужно загрузиться с установщика и выбрать экспертный режим. Это даёт дополнительные вопросы при установке, но также и возможности в более тонкой настройке новой системы.  

2. С помощью программы установки разобьём диск на:

  • системный раздел EFI (он должен быть как минимум 512 МБ, иначе все EFI-файлы могут не поместиться);

  • зашифрованный корневой раздел. Здесь вы вправе сделать разбивку настолько сложной, насколько хотите. Главное — не забыть, чтобы разделы оказались в LUKS-разделе либо его аналоге.

[12:18:23][ssh][root@aws2] ~ $ cat /etc/crypttab 
nvme0n1p2_crypt UUID=2de785af-2b9e-432a-bb31-a8f1fe36fa31 none luks,discard
[12:18:29][ssh][root@aws2] ~ $ cat /etc/fstab 
/dev/mapper/aws-root /               ext4    errors=remount-ro 0       1
# /boot/efi was on /dev/nvme0n1p1 during installation
UUID=F751-88E0  /boot/efi       vfat    umask=0077      0       1
/swapfile swap swap defaults 0 0
[12:18:35][ssh][root@aws2] ~ $ lsblk 
NAME                MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
zram0               253:0    0  466M  0 disk  [SWAP]
nvme0n1             259:0    0   25G  0 disk  
├─nvme0n1p1         259:1    0  487M  0 part  /boot/efi
└─nvme0n1p2         259:2    0 24.5G  0 part  
  └─nvme0n1p2_crypt 254:0    0 24.5G  0 crypt 
    └─aws-root      254:1    0 24.5G  0 lvm   /

3. На этапе установки системы выберем backports в качестве пакета для bookworm (stable), так как в bookworm нет systemd-ukify.

4. На этапе выбора загрузчика выберем вариант без загрузчика. При этом нам нужно пропустить пункт с установкой grub.

5. Завершим установку. При этом нужно остановиться перед перезагрузкой, так как в этот момент система не сможет запуститься корректно.

6. Переключимся в Shell-режим для модификации новой системы, которая монтируется в /target в процессе установки.

7. Поменяем приоритет bookworm-backports на приоритет как для bookworm. Это нужно для установки systemd-ukify.

TARGET=/target

# ukify install
cat > $TARGET/etc/apt/preferences.d/bookworm-backports <<EOF
Package: *
Pin: release bookworm-backports
Pin-Priority: 500
EOF

8. Обновим пакеты в новой системе, чтобы поставить версии из bookworm-backports:

in-target sh -c "export DEBIAN_FRONTEND=noninteractive && apt update && apt full-upgrade -y"

9. Установим необходимые пакеты:

apt-install systemd-boot-efi efibootmgr sbsigntool python3-pefile

10. Настроим dropbear для возможности удалённо ввести пароль от зашифрованного диска по SSH. В данном случае мы перенастраиваем dropbear на 1022-й порт при запуске и делаем жёсткую ссылку на /root/.ssh/authorized_keys. Это позволит с тем же ключом зайти и при запуске сервера, а также при изменении содержимого получить новый список ключей после применения update-initramfs:

apt-install dropbear-initramfs
echo 'DROPBEAR_OPTIONS="-I 600 -j -k -p 1022 -s -c cryptroot-unlock"' >> $TARGET/etc/dropbear/initramfs/dropbear.conf
ln $TARGET/root/.ssh/authorized_keys $TARGET/etc/dropbear/initramfs/authorized_keys

11. Запомним параметры запуска ядра:

root_fs=$(cat $TARGET/etc/fstab | grep errors=remount-ro | grep " / " | awk '{print "root=" $1}')
echo "$root_fs ro" > $TARGET/etc/kernel/cmdline

12. Сделаем дополнительную настройку для kernel-install из состава systemd. Данные скрипты добавляют собранные EFI-файлы ядер в загрузочное меню. Также добавляется настройка для сборки ядер в EFI-файлы:

cat > $TARGET/etc/kernel/install.d/99-efiboot.install <<EOF
#!/bin/sh
set -e
[ "\$KERNEL_INSTALL_LAYOUT" = "uki" ] || exit 0
COMMAND="\${1:?}"
KERNEL_VERSION="\${2:?}"
boot=\$(mount | grep "\$KERNEL_INSTALL_BOOT_ROOT" | awk '{print \$1}')

case "\$COMMAND" in
  remove)
    if [ "\$(efibootmgr | grep \$KERNEL_VERSION | wc -l)" -eq "1" ] ; then
      efibootmgr -B -b "\$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8)"
    fi
    ;;
  add)
    if [ "\$(efibootmgr | grep \$KERNEL_VERSION | wc -l)" -eq "0" ] ; then
      prefix='\EFI\Linux\'
      efibootmgr -d \$boot -C -L "\${KERNEL_VERSION}" -l "\$prefix\${KERNEL_INSTALL_ENTRY_TOKEN}-\${KERNEL_VERSION}.efi"
    fi
    if [ -f "/boot/initrd.img" ] ; then
      if [ "\$(ls -la /boot/initrd.img | grep \$KERNEL_VERSION | wc -l)" -eq "1" ] ; then
        efibootmgr -o "\$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8)"
      fi
    else
      if [ "\$(efibootmgr | grep BootOrder: | grep \$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8) | wc -l)" -eq "0" ] ; then
        list=""
        if [ "\$(efibootmgr | grep BootOrder: | awk '{print \$2}' | wc -l)" -eq "1" ] ; then
          list=",\$(efibootmgr | grep BootOrder: | awk '{print \$2}')"
        fi
        efibootmgr -o "\$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8)\$list"
      fi
    fi
    ;;
  *)
    exit 0
    ;;
esac
EOF
chmod +x $TARGET/etc/kernel/install.d/99-efiboot.install

cat > $TARGET/etc/kernel/install.conf <<EOF
layout=uki
uki_generator=ukify
EOF

cat > $TARGET/etc/kernel/postrm.d/zz-kernel-install <<EOF
#!/bin/bash
if [[ -z \${1:-} ]]; then
  echo "E: initramfs-tools: \${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number" >&2
  exit 1
else
  version="\$1"
fi
kernel-install remove "\${version}" "/boot/vmlinuz-\${version}" "/boot/initrd.img-\${version}"
EOF
chmod +x $TARGET/etc/kernel/postrm.d/zz-kernel-install

cat > $TARGET/etc/kernel/postinst.d/zz-kernel-install <<EOF
#!/bin/bash
if [[ -z \${1:-} ]]; then
  echo "E: initramfs-tools: \${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number" >&2
  exit 1
else
  version="\$1"
fi
kernel-install add "\${version}" "/boot/vmlinuz-\${version}" "/boot/initrd.img-\${version}"
EOF
mkdir -p $TARGET/etc/initramfs/post-update.d/
ln $TARGET/etc/kernel/postinst.d/zz-kernel-install $TARGET/etc/initramfs/post-update.d/zz-kernel-install
chmod +x $TARGET/etc/kernel/postinst.d/zz-kernel-install

13. Используем sbctl, чтобы упростить создание сертификатов для Secure boot и их применение к UEFI. 

sbctl на данный момент нет в Debian, поэтому приходится ставить с GitHub. Ключ --yes-this-might-brick-my-machine нужен только для AWS, так как в VM используется довольно старая версия EFI BIOS. После исполнения данной части Secure boot перейдёт в режим пользователя, так как sbctl загрузит в него созданные ключи:

ARCH=$(chroot /target/ dpkg --print-architecture)
wget --quiet https://github.com/Foxboron/sbctl/releases/download/0.14/sbctl-0.14-linux-$ARCH.tar.gz -O - | tar -xz sbctl/sbctl -O > $TARGET/usr/local/bin/sbctl
chmod +x $TARGET/usr/local/bin/sbctl
  1. Также настроим /etc/kernel/uki.conf, чтобы при создании EFI-файлы подписывались указанными ключами:

cat > $TARGET/etc/kernel/uki.conf <<EOF
[UKI]
SecureBootSigningTool=sbsign
SecureBootPrivateKey=/usr/share/secureboot/keys/db/db.key
SecureBootCertificate=/usr/share/secureboot/keys/db/db.pem
EOF

15. Создадим ключи для EFI, загрузим их в UEFI и обновим initramfs и EFI-файлы:

in-target sh -c "mount -o rw -t efivarfs efivarfs /sys/firmware/efi/efivars; \
    sbctl create-keys; \
    sbctl enroll-keys --yes-this-might-brick-my-machine; \
    update-initramfs -u -k all; \
    umount /sys/firmware/efi/efivars;"

Автоматизированный вариант установки

Это практически автоматизированная версия установки. На момент написания статьи требуется разметка диска. Для такого варианта нужен работающий nginx с файлами, доступный с сервера в процессе установки. Ссылки в файлах нужно менять на свои.

1. Соберём EFI-файл установщика с параметрами для загрузки файла ответов для автоматической установки auto=true url=https://le9i0nx.gitlab.io/autoinstall/aws-crypt.cfg interface=auto netcfg/dhcp_timeout=60 keyboard-configuration/xkb-keymap=en priority=critical, а также настройки взаимодействия через ttyS0 (это нужно для работы в AWS) console=tty0 console=ttyS0. После выполнения мы получаем /boot/efi/EFI/BOOT/BOOTAA64.EFI, с которого запустим установщик:

#!/bin/sh
set -x
wget "https://ftp.debian.org/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz"
wget "https://ftp.debian.org/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux"
apt update
apt install systemd-boot-efi binutils python3-pefile -y
wget https://raw.githubusercontent.com/systemd/systemd/v255/src/ukify/ukify.py -v -O /usr/local/bin/ukify
chmod +x /usr/local/bin/ukify

mkdir -p /boot/efi/EFI/BOOT/
ukify build \
	--linux=./linux \
	--initrd=./initrd.gz \
	--cmdline="auto=true url=https://le9i0nx.gitlab.io/autoinstall/aws-crypt.cfg interface=auto netcfg/dhcp_timeout=60 keyboard-configuration/xkb-keymap=en priority=critical theme=dark gfxpayload=800x600x16,800x600 console=tty0 console=ttyS0" \
	--uname="Debian install" \
	--output "/boot/efi/EFI/BOOT/BOOTAA64.EFI"

Скрипт для установки Debian

2. Файл ответов для почти автоматической установки. Здесь важная часть находится в команде d-i preseed/late_command string. В ней исполняются произвольные команды после установки Debian. В нашем случае — добавление SSH-ключей пользователю root и настройка Secure boot:

d-i preseed/late_command string \
  wget --quiet -O - https://le9i0nx.gitlab.io/autoinstall/ssh.sh > /ssh.sh; \
  sh /ssh.sh /target; \
  wget --quiet -O - https://le9i0nx.gitlab.io/autoinstall/secure-boot/kernel-install.sh > /install.sh; \
  sh /install.sh /target

Так как установка предполагается по сети, используется d-i network-console/authorized_keys_url string. Он указывает на файл с ключами, которыми можно зайти для продолжения установки:

d-i keyboard-configuration/xkb-keymap select us
d-i netcfg/get_hostname string unassigned-hostname
d-i netcfg/get_domain string unassigned-domain
d-i hw-detect/load_firmware boolean true
d-i anna/choose_modules string network-console
d-i preseed/early_command string anna-install network-console
d-i network-console/password password root42
d-i network-console/password-again password root42
d-i network-console/authorized_keys_url string https://le9i0nx.gitlab.io/autoinstall/authorized_keys

d-i debian-installer/language string en
d-i debian-installer/country string US
d-i debian-installer/locale string en_US.UTF-8
d-i localechooser/supported-locales multiselect en_US.UTF8, ru_RU.UTF8
d-i console-keymaps-at/keymap select us
d-i keyboard-configuration/variant select American English
d-i netcfg/choose_interface select auto
d-i mirror/http/proxy string
tzsetup-udeb time/zone select UTC
d-i clock-setup/utc boolean true
d-i clock-setup/ntp boolean true
clock-setup clock-setup/ntp-server string pool.ntp.org
d-i apt-setup/contrib boolean true
d-i apt-setup/non-free boolean true
d-i apt-setup/non-free-firmware boolean true
apt-setup-udeb apt-setup/enable-source-repositories boolean false
popularity-contest popularity-contest/participate boolean true
tasksel tasksel/first multiselect ssh-server, standard
d-i pkgsel/include string openssh-server wget ca-certificates vim
pkgsel pkgsel/update-policy select unattended-upgrades
pkgsel pkgsel/upgrade select full-upgrade
openssh-server openssh-server/password-authentication boolean false
d-i base-installer/install-recommends boolean false
bootstrap-base base-installer/initramfs-tools/driver-policy select most

d-i passwd/root-login boolean true
d-i passwd/root-password-crypted password $6$0YbiHUntJTa$k7geJE7k0.GJqG8XZKpIHrM0frlVX5ZsxFFsZ6D7SGb2IZ08La6bV8KPYkkPNEK6NJzmOggU/T3JRE0lRwE6w1
d-i passwd/make-user boolean false
d-i preseed/late_command string \
  wget --quiet -O - https://le9i0nx.gitlab.io/autoinstall/ssh.sh > /ssh.sh; \
  sh /ssh.sh /target; \
  wget --quiet -O - https://le9i0nx.gitlab.io/autoinstall/secure-boot/kernel-install.sh > /install.sh; \
  sh /install.sh /target
d-i apt-setup/services-select multiselect security, updates, backports
#bootstrap-base base-installer/kernel/image select linux-image-cloud-arm64
#d-i debian-installer/add-kernel-opts string console=tty0 console=ttyS0

Сам файл ответов.

3. Загрузим список ключей для доступа по SSH:

#!/bin/sh
TARGET=$1
mkdir -p $TARGET/root/.ssh
chmod 0700 $TARGET/root/.ssh
wget --quiet -O - https://le9i0nx.gitlab.io/autoinstall/authorized_keys >> $TARGET/root/.ssh/authorized_keys
chmod 0600 $TARGET/root/.ssh/authorized_keys

Скрипт.

4. Модификация системы для загрузки в Secure boot:

#!/bin/sh
set -x

TARGET=$1

# ukify install
cat > $TARGET/etc/apt/preferences.d/bookworm-backports <<EOF
Package: *
Pin: release bookworm-backports
Pin-Priority: 500
EOF

# ukify install python3-pefile -> systemd-ukify
PACKAGE="systemd-boot-efi efibootmgr sbsigntool python3-pefile dropbear-initramfs"
if [ "$TARGET" = "" ]; then
  apt update
  apt full-upgrade -y
  apt install -y $PACKAGE
else
  in-target sh -c "export DEBIAN_FRONTEND=noninteractive && apt update && apt full-upgrade -y"
  apt-install $PACKAGE
fi

# dropbear
echo 'DROPBEAR_OPTIONS="-I 600 -j -k -p 1022 -s -c cryptroot-unlock"' >> $TARGET/etc/dropbear/initramfs/dropbear.conf
ln $TARGET/root/.ssh/authorized_keys $TARGET/etc/dropbear/initramfs/authorized_keys

#kernel-install fixed
root_fs=$(cat $TARGET/etc/fstab | grep errors=remount-ro | grep " / " | awk '{print "root=" $1}')
#echo "$root_fs ro console=tty0 console=ttyS0" > $TARGET/etc/kernel/cmdline
echo "$root_fs ro" > $TARGET/etc/kernel/cmdline

cat > $TARGET/etc/kernel/install.d/99-efiboot.install <<EOF
#!/bin/sh
set -e
[ "\$KERNEL_INSTALL_LAYOUT" = "uki" ] || exit 0
COMMAND="\${1:?}"
KERNEL_VERSION="\${2:?}"
boot=\$(mount | grep "\$KERNEL_INSTALL_BOOT_ROOT" | awk '{print \$1}')

case "\$COMMAND" in
  remove)
    if [ "\$(efibootmgr | grep \$KERNEL_VERSION | wc -l)" -eq "1" ] ; then
      efibootmgr -B -b "\$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8)"
    fi
    ;;
  add)
    if [ "\$(efibootmgr | grep \$KERNEL_VERSION | wc -l)" -eq "0" ] ; then
      prefix='\EFI\Linux\'
      efibootmgr -d \$boot -C -L "\${KERNEL_VERSION}" -l "\$prefix\${KERNEL_INSTALL_ENTRY_TOKEN}-\${KERNEL_VERSION}.efi"
    fi
    if [ -f "/boot/initrd.img" ] ; then
      if [ "\$(ls -la /boot/initrd.img | grep \$KERNEL_VERSION | wc -l)" -eq "1" ] ; then
        efibootmgr -o "\$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8)"
      fi
    else
      if [ "\$(efibootmgr | grep BootOrder: | grep \$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8) | wc -l)" -eq "0" ] ; then
        list=""
        if [ "\$(efibootmgr | grep BootOrder: | awk '{print \$2}' | wc -l)" -eq "1" ] ; then
          list=",\$(efibootmgr | grep BootOrder: | awk '{print \$2}')"
        fi
        efibootmgr -o "\$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8)\$list"
      fi
    fi
    ;;
  *)
    exit 0
    ;;
esac
EOF
chmod +x $TARGET/etc/kernel/install.d/99-efiboot.install

cat > $TARGET/etc/kernel/install.conf <<EOF
layout=uki
uki_generator=ukify
EOF

cat > $TARGET/etc/kernel/postrm.d/zz-kernel-install <<EOF
#!/bin/bash
if [[ -z \${1:-} ]]; then
  echo "E: initramfs-tools: \${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number" >&2
  exit 1
else
  version="\$1"
fi
kernel-install remove "\${version}" "/boot/vmlinuz-\${version}" "/boot/initrd.img-\${version}"
EOF
chmod +x $TARGET/etc/kernel/postrm.d/zz-kernel-install

cat > $TARGET/etc/kernel/postinst.d/zz-kernel-install <<EOF
#!/bin/bash
if [[ -z \${1:-} ]]; then
  echo "E: initramfs-tools: \${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number" >&2
  exit 1
else
  version="\$1"
fi
kernel-install add "\${version}" "/boot/vmlinuz-\${version}" "/boot/initrd.img-\${version}"
EOF

mkdir -p $TARGET/etc/initramfs/post-update.d/
ln $TARGET/etc/kernel/postinst.d/zz-kernel-install $TARGET/etc/initramfs/post-update.d/zz-kernel-install
chmod +x $TARGET/etc/kernel/postinst.d/zz-kernel-install

# sbctl
if [ "$TARGET" = "" ]; then
  ARCH=$(dpkg --print-architecture)
else
  ARCH=$(chroot /target/ dpkg --print-architecture)
fi
wget --quiet https://github.com/Foxboron/sbctl/releases/download/0.14/sbctl-0.14-linux-$ARCH.tar.gz -O - | tar -xz sbctl/sbctl -O > $TARGET/usr/local/bin/sbctl
chmod +x $TARGET/usr/local/bin/sbctl

cat > $TARGET/etc/kernel/uki.conf <<EOF
[UKI]
SecureBootSigningTool=sbsign
SecureBootPrivateKey=/usr/share/secureboot/keys/db/db.key
SecureBootCertificate=/usr/share/secureboot/keys/db/db.pem
EOF

if [ "$TARGET" = "" ]; then
  sbctl create-keys
  sbctl enroll-keys --yes-this-might-brick-my-machine
  update-initramfs -u -k all
else
  in-target sh -c "mount -o rw -t efivarfs efivarfs /sys/firmware/efi/efivars; \
    sbctl create-keys; \
    sbctl enroll-keys --yes-this-might-brick-my-machine; \
    update-initramfs -u -k all; \
    umount /sys/firmware/efi/efivars;"
fi

Скрипт

Как перейти на вариант загрузки с подписанным uki ядром на уже работающем grub для UEFI

Стоит учитывать, что при переходе можно сломать загрузку системы, так как можно не учесть особенности именно вашей инсталляции. Чтобы избежать этого, нужно сделать следующее:

  1. Переведём загрузку ПК в режим настройки Secure boot. После этого он может быть настроен из операционной сиситемы.

  2. Поменяем приоритет bookworm-backports на приоритет как для bookworm. Это нужно для установки systemd-ukify:

cat >/etc/apt/preferences.d/bookworm-backports <<EOF
Package: *
Pin: release bookworm-backports
Pin-Priority: 500
EOF
  1. Обновим пакеты в новой системе, чтобы поставить версии из bookworm-backports:

apt update && apt full-upgrade -y"
  1. Установим необходимые пакеты:

apt-install systemd-boot-efi efibootmgr sbsigntool python3-pefile
  1. Запомним параметры запуска ядра:

root_fs=$(cat/etc/fstab | grep errors=remount-ro | grep " / " | awk '{print "root=" $1}')
echo "$root_fs ro" > /etc/kernel/cmdline
  1. Сделаем дополнительную настройку для kernel-install из состава systemd. Данные скрипты добавляют собранные EFI-файлы ядер в загрузочное меню. Также добавляется настройка для сборки ядер в EFI-файлы:

cat > /etc/kernel/install.d/99-efiboot.install <<EOF
#!/bin/sh
set -e
[ "\$KERNEL_INSTALL_LAYOUT" = "uki" ] || exit 0
COMMAND="\${1:?}"
KERNEL_VERSION="\${2:?}"
boot=\$(mount | grep "\$KERNEL_INSTALL_BOOT_ROOT" | awk '{print \$1}')

case "\$COMMAND" in
  remove)
    if [ "\$(efibootmgr | grep \$KERNEL_VERSION | wc -l)" -eq "1" ] ; then
      efibootmgr -B -b "\$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8)"
    fi
    ;;
  add)
    if [ "\$(efibootmgr | grep \$KERNEL_VERSION | wc -l)" -eq "0" ] ; then
      prefix='\EFI\Linux\'
      efibootmgr -d \$boot -C -L "\${KERNEL_VERSION}" -l "\$prefix\${KERNEL_INSTALL_ENTRY_TOKEN}-\${KERNEL_VERSION}.efi"
    fi
    if [ -f "/boot/initrd.img" ] ; then
      if [ "\$(ls -la /boot/initrd.img | grep \$KERNEL_VERSION | wc -l)" -eq "1" ] ; then
        efibootmgr -o "\$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8)"
      fi
    else
      if [ "\$(efibootmgr | grep BootOrder: | grep \$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8) | wc -l)" -eq "0" ] ; then
        list=""
        if [ "\$(efibootmgr | grep BootOrder: | awk '{print \$2}' | wc -l)" -eq "1" ] ; then
          list=",\$(efibootmgr | grep BootOrder: | awk '{print \$2}')"
        fi
        efibootmgr -o "\$(efibootmgr | grep \$KERNEL_VERSION | cut -c 5-8)\$list"
      fi
    fi
    ;;
  *)
    exit 0
    ;;
esac
EOF
chmod +x /etc/kernel/install.d/99-efiboot.install

cat > /etc/kernel/install.conf <<EOF
layout=uki
uki_generator=ukify
EOF

cat > /etc/kernel/postrm.d/zz-kernel-install <<EOF
#!/bin/bash
if [[ -z \${1:-} ]]; then
  echo "E: initramfs-tools: \${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number" >&2
  exit 1
else
  version="\$1"
fi
kernel-install remove "\${version}" "/boot/vmlinuz-\${version}" "/boot/initrd.img-\${version}"
EOF
chmod +x /etc/kernel/postrm.d/zz-kernel-install

cat > /etc/kernel/postinst.d/zz-kernel-install <<EOF
#!/bin/bash
if [[ -z \${1:-} ]]; then
  echo "E: initramfs-tools: \${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number" >&2
  exit 1
else
  version="\$1"
fi
kernel-install add "\${version}" "/boot/vmlinuz-\${version}" "/boot/initrd.img-\${version}"
EOF

mkdir -p /etc/initramfs/post-update.d/
ln /etc/kernel/postinst.d/zz-kernel-install /etc/initramfs/post-update.d/zz-kernel-install
chmod +x /etc/kernel/postinst.d/zz-kernel-install
  1. Используем sbctl, чтобы упростить создание сертификатов для Secure boot и их применение к UEFI. 

    sbctl на данный момент нет в Debian, поэтому приходится ставить с GitHub. После исполнения данной части Secure boot перейдёт в режим пользователя, так как sbctl загрузит в него созданные ключи:

ARCH=$(dpkg --print-architecture)
wget --quiet https://github.com/Foxboron/sbctl/releases/download/0.14/sbctl-0.14-linux-$ARCH.tar.gz -O - | tar -xz sbctl/sbctl -O > /usr/local/bin/sbctl
chmod +x /usr/local/bin/sbctl
  1. Также настроим /etc/kernel/uki.conf, чтобы при создании EFI-файлы подписывались указанными ключами:

cat > /etc/kernel/uki.conf <<EOF
[UKI]
SecureBootSigningTool=sbsign
SecureBootPrivateKey=/usr/share/secureboot/keys/db/db.key
SecureBootCertificate=/usr/share/secureboot/keys/db/db.pem
EOF
  1. Создадим ключи для EFI, загрузим их в UEFI и обновим initramfs и EFI-файлы:

sbctl create-keys
sbctl enroll-keys
update-initramfs -u -k all
  1. После перезапуска, если загрузка удачно завершилась, через новый вариант стоит закрыть доступ к изменению настроек BIOS.

Итоги

После завершения установки мы получим установленный Debian на зашифрованном диске с ограниченным списком того, что может быть загружено с ноутбука. В такой системе мы доверяем UEFI — что она не позволит загрузить неподписанные EFI-файлы.

Используемые ссылки:

Комментарии (17)


  1. NZakh61
    19.08.2024 07:57

    @le9i0nx, а будет инструкция, чтобы записать загрузочную флешку с windows 10/11 через любой дистр Линукса, например Debian, не прокидывая wine и не поднимая виндовую виртуалку?))))
    Знаю, что в компании сталкивались с таким кейсом))


    1. NZakh61
      19.08.2024 07:57

      А, ну и чтобы система точно установилась))


    1. le9i0nx Автор
      19.08.2024 07:57

      Маловероятно это не профиль нашей компании.


    1. agp88
      19.08.2024 07:57
      +4

      Попробуйте ventoy. Судя по всему, не совсем то, о чем вы спрашиваете, но на выходе таки получается загрузочная флешка с любой виндой и не только.


    1. dartraiden
      19.08.2024 07:57

      Если в прошивке машины, на которую будете ставить Windows, есть драйвер NTFS (на FAT32 install.wim может не влезть, если его размер превышаает ограничения FAT32), то можно просто распаковать содержимое образа на флешку - EFI-загрузка работает с любой ФС, лишь бы прошивка могла прочитать файловую систему.


    1. aapchy199
      19.08.2024 07:57

      Есть хорошая утилита windows2usb (https://github.com/ValdikSS/windows2usb), есть в виде готового appimage.

      Windows2usb is a bash script which writes Microsoft Windows
      7/8/8.1/10/11 installation DVD images to USB Flash Drive or external HDD
      on Linux. It was designed with compatibility in mind, and should work
      in all cases, contrary to other popular Linux tools.

      Если нужна загрузка только с EFI систем, то можно просто распаковать в раздел с fat32.

      Если wim файл больше 4 GiB, можно разбить на несколько файлов с помощью dism или open source утилиты wimlib-imagex. Работает быстро, без необходимости пережимать.


  1. isumix
    19.08.2024 07:57

    Эх, была бы возможность чекнуть галочку при установке, чтобы сделать всё хорошо.

    Не помню в каком дистре видел зашифрованный boot автоматом, без uefi правда.


    1. le9i0nx Автор
      19.08.2024 07:57
      +1

      В принципе это возможно в grub2 с какой то версии он стал поддерживать шифрованный раздел /boot/ но в нём есть ограничение на поддерживаемый формат LUKS и самое противное это необходимость вводить пароль дважды. И нет возможности удалённо ввести пароль по ssh


      1. alexkuzko
        19.08.2024 07:57

        На самом деле там есть обход через... Раскрытие основного раздела через сохраненный ключ. Т.е. бут раскрывается через ввод пароля (на первой версии luks, да), а дальше используется сохраненный на зашифрованном разделе ключ.

        Разумеется, минусы есть: лежащий в относительном доступе ключ (после загрузки), первая версия luks. Зато вектор атаки без основной дыры.


  1. OverThink
    19.08.2024 07:57

    Безопасней держать загрузчик на флешке


    1. le9i0nx Автор
      19.08.2024 07:57
      +2

      В принципе вы правы. Такое добавление ещё сильнее усложнит атаку. Но такое сработает насколько мне известно только при физическом доступе к загружаемому устройству.


  1. Johan_Palych
    19.08.2024 07:57
    +3

    Ручной вариант установки
    Нужно загрузиться с установщика и выбрать экспертный режим.

    debian-12.6.0-amd64-netinst.iso 2024-06-29 13:56 631M
    Рекомедую почитать Unified kernel image, Unified kernel image specification
    Рекомедую включить:

    network-console: Continue installation remotely using SSH

    Пакет: systemd-ukify в Debian trixie и sid dep: systemd (>= 256.4-3)
    В bookworm-backports systemd (254.16-1~bpo12+1)
    Лучший вариант для меня:
    Загрузка с live-cd. Для примера debian-live-12.6.0-amd64-lxde.iso
    Разметка диска. debootstrap, arch-install-scripts или systemd-nspawn


  1. AcidWave
    19.08.2024 07:57

    А зачем этот весь геморрой, если в арче при установке все можно сделать не особо напрягаясь? А учитывая что в заключительном комментарии предлагают использовать арчевые скрипты, то смысл статьи теряется чуть больше чем полностью


    1. le9i0nx Автор
      19.08.2024 07:57

      выбор дистрибутива это совсем другой разговор.
      А вот что вы подразумеваете под "арчевые скрипты" непонятно. Не могли бы вы раскрыть эту часть более подробно?


  1. 0xC0CAC01A
    19.08.2024 07:57

    А как с этим дела у мобильных платформ?


  1. ivanrt
    19.08.2024 07:57

    Недавно настаивал свои ключи и secure boot с подписанным systemd-boot и ядрами в Gentoo. Как вы храните ключ которым подписываете новые ядра?

    Обидно что secure boot на AMD сломали, см. AMD sinkclose.


    1. le9i0nx Автор
      19.08.2024 07:57
      +2

      ключи хранятся локально на зашифрованном root. Это не 100% безопасно т.к. ключи не защищены паролем или шифрованием.