
Безопасная эксплуатация ноутбуков, или Защита мастер-ключа LUKS с помощью пользовательского ключа на USB-накопителе
Как мы уже знаем из первой части, система LUKS подбирает параметры хеширования ключей таким образом, чтобы для проверки одной парольн��й фразы нужно было не менее секунды, в связи с чем для взлома пароля длиной 8 символов требуется более ста миллионов лет. Однако рост производительности CPU/GPU и развитие квантовых технологий может привести к тому, что лет через десять текущие оценки окажутся неактуальными, поэтому с учетом будущих угроз длину пароля можно увеличить, и LUKS позволяет использовать фразы длиной до 512 символов. Тем не менее, каждый дополнительный символ существенно усложняет пользовательский опыт и повышает шансы того, что пользователь просто забудет свой пароль и потеряет доступ к данным. Сегодня мы покажем, как можно защитить мастер-ключ LUKS с помощью случайного пользовательского ключа, который трудно подобрать, легко потерять и невозможно забыть.
Чтобы не вводить идентификатор системного диска вручную, запишем его значение в переменную UUID:
UUID=$(sudo blkid --match-token TYPE=crypto_LUKS --output value -s UUID) echo $UUID
Результат выполнения команд...
localadmin@pc:~$ UUID=$(sudo blkid --match-token TYPE=crypto_LUKS --output value -s UUID) localadmin@pc:~$ echo $UUID 453fa939-a585-4e1f-a030-b11991a78d3f
Парольные фразы могут быть длиной до 512 символов, а файл ключа может быть размером до 8 мегабайт, но учитывая то, что для шифрования мастер-ключа в конечном итоге используется ключ шифрования длиной 256 бит, который мы получаем методом многократного хеширования, создавать случайные ключи размером более 32 байт не имеет практического смысла. Для генерации ключа воспользуемся утилитой dd и генератором случайных чисел urandom:
dd if=/dev/urandom bs=1 count=32 > $UUID.lek
Добавим ключ в заголовок LUKS с помощью команды luksAddKey. Утилите cryptsetup потребуется извлечь мастер-ключ, для чего она запросит одну из ранее установленных парольных фраз.
sudo cryptsetup luksAddKey /dev/sda5 $UUID.lek
Отметим, что преимуществом случайного ключа является то, что он обладает крайне высокой энтропией, поэтому подбирать его не имеет смысла и проще тогда уж сразу перейти к перебору 2256 комбинаций в попытке взломать один из слотов заголовка LUKS.

Чтобы удостовериться, что ключ был успешно добавлен, можно воспользоваться командой test-passphrase. Добавим в цепочку вызовов утилиту time, чтобы посчитать время выполнения запроса.
time sudo cryptsetup open --test-passphrase /dev/sda5 \ --key-file=$UUID.lek && echo 'Ключ подошел'
Поскольку в заголовке у нас три ключа, а проверяемый ключ находится последним по списку, для выполнения операции требуется не менее трех секунд:
localadmin@pc-1:~$ time sudo cryptsetup open --test-passphrase /dev/sda5 \ > --key-file=$UUID.lek && echo 'Ключ подошел' real 0m4,361s user 0m12,697s sys 0m0,462s Ключ подошел
Скопируем файл ключа на внешний USB-накопитель с разметкой vfat, чтобы этот ключ можно было использовать для разблокирования системного диска. Это пока еще не Рутокен от компании «Актив», но всему свое время.
sudo mount /dev/sdb1 /mnt sudo cp $UUID.lek /mnt

Добавим опцию keyscript в конец строки из файла /etc/crypttab, чтобы сист��ма запрашивала пользовательский ключ с помощью нашего кастомного скрипта:
# Удалим параметр keyscript из конца строк, если он есть sudo sed -i 's|,keyscript=.*||' /etc/crypttab # Добавим параметр keyscript с нужным нам значением sudo sed -i 's|$|,keyscript=/bin/unlock_luks_with_usb|' /etc/crypttab # Проверим результат cat /etc/crypttab
Результат выполнения команд...
localadmin@pc:~$ sudo sed -i 's|$|,keyscript=/bin/unlock_luks_with_usb|' /etc/crypttab localadmin@pc:~$ cat /etc/crypttab sda5_crypt UUID=453fa939-a585-4e1f-a030-b11991a78d3f none luks,discard,keyscript=/bin/unlock_luks_with_usb
Каждая строка из файла /etc/crypttab имеет следующий формат:
<имя_раздела> <UUID_диска> <имя_ключа> <параметры>
По результатам своей работы скрипт должен передать в стандартный поток вывода набор байт, соответствующий пользовательскому ключу или его парольной фразе. В скрипте можно использовать следующие переменные:
$CRYPTTAB_NAME– имя раздела, первый параметр в строке монтирования шифрованного диска в файле/etc/crypttab. В приведенном примереsda5_crypt;$CRYPTTAB_SOURCE– путь к шифрованному диску. В приведенном примере/dev/sda5;$CRYPTTAB_KEY– имя пользовательского ключа, третий параметр. В приведенном примереnone;$CRYPTTAB_OPTIONS– опции монтирования, четвертый параметр. В приведенном примере «luks,discard,keyscript=/bin/unlock_luks_with_usb»;$CRYPTTAB_TRIED– количество предыдущих попыток монтирования шифрованного диска (счет идет до тех пор, пока не будет достигнуто максимально допустимое количество попыток).
Создадим скрипт unlock_luks_with_usb со следующим набором команд:
Файл /bin/unlock_luks_with_usb
#!/bin/sh set -e # Делаем выход из скрипта при ошибке # Переключаемся в текстовый режим и устанавливаем кириллический шрифт /usr/bin/plymouth hide-splash setfont /usr/share/consolefonts/CyrSlav-Fixed16.psf.gz >&2 # Возвращаем графический интерфейс /usr/bin/plymouth show-splash mkdir -p /mnt UUID=$(blkid -s UUID -o value $CRYPTTAB_SOURCE) for USBPARTITION in /dev/disk/by-id/usb-*-part1; do USBDEVICE=$(readlink -f $USBPARTITION) if mount -t vfat $USBDEVICE /mnt -o iocharset=cp866 2>/dev/null; then if [ -e /mnt/$UUID.lek ]; then cat /mnt/$UUID.lek umount $USBDEVICE /usr/bin/plymouth display-message --text="Пользовательский ключ успешно извлечен, для продолжения загрузки требуется извлечь USB-токен..." while [ -e $USBPARTITION ]; do sleep 1 done /usr/bin/plymouth display-message --text="USB-накопитель извлечен, продолжаем загрузку" exit fi umount $USBDEVICE fi done /usr/bin/plymouth display-message --text="Вы можете нажать клавишу <Enter>, чтобы переключиться на загрузку с помощью USB-накопителя" /usr/bin/plymouth ask-for-password --prompt="Введите пароль"
Логику работы скрипта можно описать несколькими тезисами.
Сначала скрипт проверяет, подключен ли к компьютеру USB-накопитель, в корне которого находится файл
<UUID>.lek, гдеUUIDсоответствует идентификатору системного диска.Если LEK-файл будет найден, то:
скрипт с помощью утилиты
catпередаст содержимое этого файла в стандартный поток вывода и завершит свою работу;далее, если ключ подойдет, то загрузчик разблокирует диск и продолжит загрузку операционной системы;
если же ключ из LEK-файла окажется некорректным, то загрузчик предпримет еще одну попытку монтирования системного диска, поэтому скрипт будет запущен еще раз.
Если скрипт не найдет LEK-файл, то:
скрипт предложит пользователю ввести парольную фразу вручную с помощью команды
ask-for-password;если парольная фраза окажется некорректной или пользователь просто нажмет клавишу
<Enter>, то загрузчик предпримет еще одну попытку монтирования диска, поэтому скрипт будет запущен еще раз.
По результатам опроса участников фокус-группы из нашего профессионального сообщества более 70% инженеров считают, что для продолжения загрузки нужно предлагать или даже требовать извлечения USB-устройства, на котором находится ключ. Если вы придерживаетесь иного мнения, то удалите строки 21-25 между вызовом команд display-message.
Сделаем скрипт исполняемым:
sudo chmod +x /bin/unlock_luks_with_usb
Для работы скрипта в образ initramfs требуется добавить несколько файлов:
CyrSlav-Fixed16.psf.gz— шрифт с поддержкой кириллических символов для возможности использования уведомлений на русском языке;setfont— утилита, которая позволяет установить в консоли произвольный шрифт.
Для добавления файлов в образ initramfs нужно создать хук unlock_luks_with_usb_hook, который будет выполняться каждый раз при сборке образа:
Файл /etc/initramfs-tools/hooks/unlock_luks_with_usb_hook
#!/bin/sh PREREQ="" prereqs() { echo "$PREREQ" } add_file() { # Удаляем файл, если такой уже есть, поскольку функция copy_exec не умеет перезаписывать существующие файлы rm -f ${DESTDIR}$1 # Добавляем указанный файл в initramfs copy_exec $1 $1 } case $1 in prereqs) prereqs exit 0 ;; esac # Подключаем скрипт с функцией copy_exec . /usr/share/initramfs-tools/hook-functions # Добавляем кириллический шрифт и утилиту для его установки в консоли add_file /usr/share/consolefonts/CyrSlav-Fixed16.psf.gz add_file /usr/bin/setfont
Сделаем файл хука исполняемым:
sudo chmod +x /etc/initramfs-tools/hooks/unlock_luks_with_usb_hook
Для возможности монтирования USB-накопителей на ранних стадиях загрузки системы в ядро initramfs нужно включить несколько дополнительных модулей, для чего в файл modules требуется добавить следующие строки:
Файл /etc/initramfs-tools/modules
... # Модуль для работы с USB-накопителями usb_storage # Модуль для работы с файловой системой VFAT vfat
Соберем образ initramfs:
sudo update-initramfs -u
Если вы еще не читали первую часть статьи, то выполните дополнительно следующие команды, чтобы настроить plymouth. Мы рекомендуем использовать тему spinner, которая отображает текстовые сообщения в центре экрана под индикатором загрузки, что делает их достаточно заметными.
sudo apt install --yes plymouth-themes sudo plymouth-set-default-theme spinner --rebuild-initrd sudo sed -i 's|"quiet|"splash quiet|' /etc/default/grub sudo update-grub
Перезагрузим виртуальную машину, дождемся приглашения, подключим USB-накопитель и нажмем клавишу <Enter> как показано на рисунке 7 (сквозная нумерация рисунков, начало см. в первой части). При необходимости пользователь сможет разблокировать диск без USB-накопителя, используя сложный пароль, который был использован при установке системы.

Использование случайного 256-битного ключа и его хранение на внешнем USB-накопителе существенно повышают безопасность данных, но если компьютер будет похищен вместе с этим накопителем, то злоумышленник сможет получить доступ к данным так, как если бы они не были защищены вовсе. Это все равно, что хранить ключ от квартиры под ковриком. Мы, конечно, добавили в скрипт команды, которые не позволят завершить загрузку системы до тех пор, пока USB-накопитель с ключом не будет извлечен из компьютера, что существенно снижает вероятность кражи ноутбука вместе с таким накопителем, но исключать указанный сценарий развития событий будет все же неправильно. Можно попытаться установить пароль на файл ключа, но простой PIN-код не поможет, а если устанавливать сложный пароль, то зачем тогда вообще нужна флешка.

В третьей части мы расскажем о наиболее перспективном по нашему мнению способе решения поставленной задачи — защите мастер-ключа с помощью USB-токена на примере Рутокен ЭЦП 3.0. Такие устройства позволяют, с одной стороны, использовать простые PIN-коды, а с другой — обеспечить надежную защиту от перебора, поскольку USB-токены позволяют установить ограничение на количество попыток ввода PIN-кода.
