Как загрузить парольную фразу LUKS с USB, возвращаясь к клавиатуре?
Я хочу настроить безголовый компьютер Linux (Debian Wheezy) с полным шифрованием диска, с возможностью разблокировки диска с помощью USB-накопителя или путем ввода ключевой фразы с клавиатуры. Моя отправная точка - новая установка, использующая базовую опцию шифрования всего диска в установщике Debian, которая управляет всем, кроме /boot, как группа логических томов с шифрованием LUKS и дает мне опцию клавиатуры. Я опишу свое текущее решение в ответе в надежде, что оно будет полезным и что другие могут улучшить его.
Вот некоторые из проблем, которые у меня были:
Настройка парольной фразы и ее установка на USB-накопитель.
Загрузка модулей USB вовремя.
Ожидание распознавания USB-накопителя Linux, прежде чем пытаться читать с него.
Определение правильного USB-накопителя (а не другого накопителя, который был вставлен).
Написание "скрипта ключа" для извлечения ключевой фразы с USB-накопителя.
Обеспечение возврата к клавиатуре во всех случаях сбоя USB.
Я приму ответ с существенными улучшениями и откликнусь с ответами, которые предлагают вклад.
5 ответов
Многое из моего решения взято из поста " Использование USB-ключа для парольной фразы LUKS".
Создайте случайную фразу-пароль:
dd if=/dev/urandom bs=1 count=256 > passphrase
Вставьте USB-накопитель.
dmesg
вывод покажет имя устройства; предполагать/dev/sdd
, Выяснить его размер:blockdev --getsize64 /dev/sdd
Я решил установить парольную фразу в конце исходного устройства, полагая, что оно может выжить при любом случайном использовании USB-накопителя.
dd if=passphrase of=/dev/sdd bs=1 seek=<size-256>
Добавьте ключевую фразу в том LUKS:
cryptsetup luksAddKey /dev/sda5 passphrase
Это не влияет на существующую парольную фразу, введенную вручную от установщика. Файл парольной фразы может быть удален:
rm passphrase
Найдите уникальное имя для USB-накопителя, чтобы мы могли определить его при наличии:
ls -l /dev/disk/by-id | grep -w sdd
Вы должны увидеть одну символическую ссылку. Я назову это
/dev/disk/by-id/<ID>
,редактировать
/etc/crypttab
, Вы должны увидеть строку вроде:sdc5_crypt UUID=b9570e0f-3bd3-40b0-801f-ee20ac460207 none luks
Изменить его на:
sdc5_crypt UUID=b9570e0f-3bd3-40b0-801f-ee20ac460207 /dev/disk/by-id/<ID> luks,keyscript=/bin/passphrase-from-usb
keyscript
упомянутый выше должен будет прочитать парольную фразу с устройства USB. Тем не менее, он должен сделать больше, чем это. Чтобы понять, как это используется, проверьте/usr/share/initramfs-tools/scripts/local-top/cryptroot
, скрипт, который запускается во время загрузки, чтобы разблокировать корневое устройство. Обратите внимание, когдаkeyscript
установлен, он просто запускается, а выводluksOpen
без других проверок. Нет способа сообщить об ошибке (USB-накопитель отсутствует) или вернуться к вводу с клавиатуры. Если парольная фраза не срабатывает, сценарий запускается снова в цикле, до некоторого количества раз; однако нам не говорят, на какой итерации мы находимся. Кроме того, мы не контролируем, когда запускается сценарий с ключом, поэтому мы не можем быть уверены, что Linux распознал USB-накопитель.Я обратился к этому с некоторыми взломами:
Проведите опрос на USB-накопителе и подождите 3 секунды, пока он не появится. Это работает для меня, но я хотел бы узнать лучший способ.
Создать фиктивный файл
/passphrase-from-usb-tried
при первом запуске, чтобы показать, что мы запускались хотя бы один раз.Если мы запустились хотя бы один раз или USB-накопитель не найден, запустите
askpass
программа используетсяcryptroot
для ввода с клавиатуры.
Финальный сценарий:
#!/bin/sh set -e if ! [ -e /passphrase-from-usb-tried ]; then touch /passphrase-from-usb-tried if ! [ -e "$CRYPTTAB_KEY" ]; then echo "Waiting for USB stick to be recognized..." >&2 sleep 3 fi if [ -e "$CRYPTTAB_KEY" ]; then echo "Unlocking the disk $CRYPTTAB_SOURCE ($CRYPTTAB_NAME) from USB key" >&2 dd if="$CRYPTTAB_KEY" bs=1 skip=129498880 count=256 2>/dev/null exit else echo "Can't find $CRYPTTAB_KEY; USB stick not present?" >&2 fi fi /lib/cryptsetup/askpass "Unlocking the disk $CRYPTTAB_SOURCE ($CRYPTTAB_NAME)\nEnter passphrase: "
Наконец, нам нужно убедиться, что этот скрипт доступен в initramfs. Создайте
/etc/initramfs-tools/hooks/passphrase-from-usb
содержащий:#!/bin/sh PREREQ="" prereqs() { echo "$PREREQ" } case "$1" in prereqs) prereqs exit 0 ;; esac . "${CONFDIR}/initramfs.conf" . /usr/share/initramfs-tools/hook-functions copy_exec /bin/passphrase-from-usb /bin
Драйверы USB не присутствовали в моих initramfs. (Похоже, они по умолчанию в более поздних версиях Debian.) Я должен был добавить их, добавив в
/etc/initramfs-tools/modules
:uhci_hcd ehci_hcd usb_storage
Когда все будет сделано, обновите initramfs:
update-initramfs -u
Для меня было бы идеально, если бы я мог просто иметь небольшую флешку с парольной фразой, которая разблокирует диск. Это будет не только удобно для серверов (где вы можете оставить USB-накопитель на сервере - цель состоит в том, чтобы иметь возможность возвращать сломанные жесткие диски, не беспокоясь о конфиденциальных данных), это также будет полезно для моего ноутбука: вставьте Флешка при загрузке и вынимайте ее после разблокировки криптодиска.
Теперь я написал патч, который будет искать файл "cryptkey.txt" в корневом каталоге всех устройств и пытаться расшифровать каждую строку в качестве ключа. Если это не помогло: вернитесь к вводу парольной фразы.
Это означает, что ключ не может содержать \ n, но это также относится к любому введенному ключу. Хорошая часть заключается в том, что вы можете использовать один и тот же USB-диск для хранения ключа для нескольких машин: вам не нужен отдельный USB-диск для каждой. Таким образом, если у вас есть USB-накопитель в вашем физическом брелоке, вы можете использовать один и тот же накопитель для всех машин, которые вы загружаете, когда физически находитесь рядом.
Вы добавляете ключ с помощью:
cryptsetup luksAddKey /dev/sda5
Затем вставьте тот же ключ, что и строку, в файл на USB /MMC-диске с именем "cryptkey.txt". Патч здесь:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=864647
Если драйверы USB, драйверы MMC или файловые системы отсутствуют в ваших initramfs, вам необходимо добавить их, добавив в / etc / initramfs-tools / modules:
uhci_hcd
ehci_hcd
usb_storage
nls_utf8
nls_cp437
vfat
fat
sd_mod
mmc_block
tifm_sd
tifm_core
mmc_core
tifm_7xx1
sdhci
sdhci_pci
Когда все будет сделано, обновите initramfs:
update-initramfs -u
Его можно найти в виде патча и файла по адресу: https://gitlab.com/ole.tange/tangetools/tree/master/decrypt-root-with-usb
Несмотря на отличный ответ от @Andrew, который работает в предыдущих версиях. Решение на самом деле устарело и требует большой настройки для ubuntu 18.04 и 19.10. Итак, я хочу поделиться своим исследованием по этому поводу.
По поводу crypttab есть несколько уловок. С 14 апреля по 18 апреля и по 19 октября число секций сильно изменилось. Он начинает поддерживать больше параметров для cryptsetup. Например, смещение ключевого файла, размер ключевого файла и т.д. Некоторые параметры, например nobootwait, пропали. Некоторые параметры уже поддерживаются в другом дистрибутиве, но еще не поддерживаются в ubuntu (например, очень хороший параметр keyfile-timeout. Это может устранить весь сценарий клавиш, поскольку он автоматически откатится к вводу с клавиатуры после тайм-аута ключевого файла).
Основная проблема crypttab в ubuntu заключается в том, что он фактически обрабатывается двумя разными процессами. Один из них - это традиционно initramfs, а другой - современный systemd. Systemd должен быть более продвинутым и гибким во многих аспектах. Однако systemd плохо поддерживает crypptab, есть много опций, таких как keycript, которые просто игнорируются. поэтому я понятия не имею, что происходит, пока не заметил этот пост. Почти все сообщения в Интернете о настройках crypttab предназначены для initramfs, а не для systemd. Итак, нам нужно добавитьinitramfs
ко всем записям в crypttab, чтобы избежать проблем.
Я также обнаружил хороший способ отладки нашего скрипта ключей и crypttab без виртуальной машины или многократной перезагрузки. этоcryptdisks_start
. Прежде чем мы фактически распространим наши изменения в initramfs, мы всегда должны тестировать его с помощью этой красивой команды. В противном случае вы должны быть заблокированы в своей системе и сможете восстановить ее только через среду chroot.
@andrew опубликовал хороший способ использовать скрытие данных в необработанной области файловой системы. Однако я обнаружил, что это очень раздражает, когда мы хотим автоматически создавать разделы и распределять необработанные данные на множество usbkeys, мы должны рассчитывать смещение для всех различных файловых систем и разных размеров разделов. Более того, если пользователь случайно напишет на ФС, есть риск, что ключ будет перезаписан. Необработанный раздел без ФС в этом случае имеет больше смысла. Однако необработанный раздел не имеет UUID, который не очень полезен для автоматической разблокировки. Таким образом, я хотел бы представить способ использования обычных файлов парольных фраз в файловой системе usbkey. Основная проблема passdev в том, что он не ищет / не останавливается во время чтения файла. Таким образом, мы не можем использовать параметры keyfile-offset и keyfile-size, когда хотим вернуться к вводу с клавиатуры. Поскольку cryptsetup на самом деле пытается пропустить входной контент, и если контент короче, чем размер ключевого файла, это вызывает ошибку. Это также означает, что при большом смещении passdev может быть очень медленным, поскольку всегда читается с начала. Однако нет смысла реализовывать смещение и размер ключевого файла для реального файла в файловой системе. Я считаю, что они созданы для необработанного устройства.
Crypttab
luks-part UUID="<uuid>" /dev/disk/by-uuid/<keyfile FS uuid>:/<keyfile path relative to usbkey root>:<timeout in sec> luks,keyfile-offset=<seek to the key>,keyfile-size=<>,keyscript=/bin/passphrase-from-usbfs.sh,tries=<number of times to try>,initramfs
ключевой скрипт passphrase-from-usbfs.sh использовал /lib/cryptsetup/scripts/passdev
который будет ждать USB-устройство и смонтировать файловую систему, а затем передать содержимое файла. Он поддерживаетCRYPTTAB_KEY
в формате /device-path/<keyfile FS uuid>:/<keyfile path relative to usbkey root>:<timeout in sec>
.
#!/bin/sh
#all message need to echo to stderr, the stdout is used for passphrase
# TODO: we may need to do something about the plymouth
echo "CRYPTTAB_KEY=$CRYPTTAB_KEY" >&2
echo "CRYPTTAB_OPTION_keyfile_offset=$CRYPTTAB_OPTION_keyfile_offset" >&2
#set your offset and file size here if your system does not support those paramters
#CRYPTTAB_OPTION_keyfile_offset=
#CRYPTTAB_OPTION_keyfile_size=
echo "timeout=$CRYPTTAB_OPTION_keyfile_timeout" >&2
CRYPTTAB_OPTION_keyfile_timeout=10 # keyfile-timeout is not supported yet
pass=$(/lib/cryptsetup/scripts/passdev $CRYPTTAB_KEY)
rc=$?
if ! [ $rc -eq 0 ]; then
echo "Can't find $CRYPTTAB_KEY; USB stick not present?" >&2
/lib/cryptsetup/askpass "Unlocking the disk $CRYPTTAB_SOURCE ($CRYPTTAB_NAME) Enter passphrase: "
else
echo "successfully load passphrase." >&2
echo -n $pass
fi
Хук сообщает update-initramfs, что нужно скопировать наши скрипты.
#!/bin/sh
PREREQ=""
prereqs() {
echo "$PREREQ"
}
case "$1" in
prereqs)
prereqs
exit 0
;;
esac
. "${CONFDIR}/initramfs.conf"
. /usr/share/initramfs-tools/hook-functions
copy_exec /bin/passphrase-from-usbfs.sh
copy_exec /bin/passphrase-from-usb.sh
#when using passdev we need to hook additionaly FS and binary
copy_exec /lib/cryptsetup/scripts/passdev
manual_add_modules ext4 ext3 ext2 vfat btrfs reiserfs xfs jfs ntfs iso9660 udf
Наконец, я опубликовал обновленную версию passphrase-from-usb.sh, которая может использовать новые параметры в crypttab:
Чтобы сопровождать превосходные ответы выше, пожалуйста, смотрите подпрограммы C, которые вы можете использовать для записи / генерации и чтения необработанного блочного ключа устройства "Readkey.c" извлекает ключ заданного размера из блочного устройства, а "writekey.c" может генерировать или записывать существующий ключ в необработанное устройство. После компиляции "readkey.c" можно использовать в пользовательском скрипте для извлечения ключа известного размера из необработанного блочного устройства, например, так:
readkey </path/to/device> <keysize>
Чтобы увидеть использование "writekey", после компиляции запустите его без флагов.
Для компиляции просто используйте:
gcc readkey.c -o readkey
gcc writekey.c -o writekey
Я протестировал оба устройства на USB-флешке Verbatim 16 ГБ USB 2.0 с пользовательским параметром "keyscript=" в crypttab, также опубликованном ниже. Идея "crypto-usb.sh" взята из руководства по cryptsetup debian etch.
crypto-usb.sh:
#!/bin/sh
echo ">>> Trying to get the key from agreed space <<<" >&2
modprobe usb-storage >/dev/null 2>&1
sleep 4
OPENED=0
disk="/sys/block/sdb"
boot_dir="/boot"
readkey="/boot/key/readkey"
echo ">>> Trying device: $disk <<<" >&2
F=$disk/dev
if [ 0`cat $disk/removable` -eq 1 -a -f $F ]; then
mkdir -p $boot_dir
mount /dev/sda1 $boot_dir -t ext2 >&2
echo ">>> Attempting key extraction <<<" >&2
if [ -f $readkey ]; then
# prints key array to the caller
$readkey /dev/sdb 4096
OPENED=1
fi
umount $boot_dir >&2
fi
if [ $OPENED -eq 0 ]; then
echo "!!! FAILED to find suitable key !!!" >&2
echo -n ">>> Try to enter your password: " >&2
read -s -r A
echo -n "$A"
else
echo ">>> Success loading key <<<" >&2
fi
Когда необходимо указать размер ключа, сгенерированный ключ сохраняется в файле ".tmpckey" с правами доступа 0600 для последующего использования. При записи существующего ключа размер определяется путем измерения существующего размера ключа. Это похоже на сложный подход, однако после компиляции с простым "gcc" он может обеспечить простой способ манипулирования содержимым необработанного ключа.
readkey.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(int argc, char *argv[])
{
int blockSize = 512;
int keySize = 2048;
FILE *device;
if ( argc == 3
&& (sizeof(argv[1]) / sizeof(char)) > 1
&& (sizeof(argv[2]) / sizeof(char)) > 1
&& (atoi(argv[2]) % 512) == 0
) {
device = fopen(argv[1], "r");
if(device == NULL) {
printf("\nI got trouble opening the device %s\n", argv[1]);
exit(EXIT_FAILURE);
}
keySize = atoi(argv[2]);
}
else if ( argc == 2
&& (sizeof(argv[1]) / sizeof(char)) > 1
) {
device = fopen(argv[1], "r");
if(device == NULL) {
printf("\nI got trouble opening the device %s\n", argv[1]);
exit(EXIT_FAILURE);
}
}
else {
printf("\nUsage: \n");
printf("\nKey Size Provided: \n");
printf("\n\t\treadkey </path/to/device> <keysize> \n");
printf("\nDefault key size: %d\n", keySize);
printf("\n\t\treadkey </path/to/device>\n");
exit(1);
}
int count;
char *block;
/* Verify if key is multiple of blocks */
int numBlocks = 0;
if (keySize % 512 != 0) {
printf("\nSory but key size is not multiple of block size, try again. TA.\n");
exit(1);
}
/* Seek till the end to get disk size and position to start */
fseek(device, 0, SEEK_END);
/* Determine where is the end */
long endOfDisk = ftell(device);
/* Make sure we start again */
rewind(device); // Do I need it ???
/* Get the required amount minus block size */
long startFrom = endOfDisk - blockSize - keySize;
/* Allocate space for bloc */
block = calloc(keySize, sizeof(char));
/* Start reading from specified block */
fseek(device, startFrom, SEEK_SET);
fread(block, 1, keySize, device);
/* Do something with the data */
for(count = 0; count < keySize/*sizeof(block)*/; count++){
printf("%c", block[count]);
}
/* Close file */
fclose(device);
/* Make sure freed array is zeroed */
memset(block, 0, keySize);
free(block);
}
writekey.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int blockSize = 512;
int keySize = 2048;
int count;
unsigned char *block;
/*
Thing to always remember that argv starts from 0 - the name of the program, and argc starts from 1 i.e. 1 is the name of the program.
*/
if ( argc == 3
&& strcmp(argv[1], "genwrite") != 0
&& (sizeof(argv[2]) / sizeof(char)) > 2
) {
char ch;
FILE *keyF;
keyF = fopen(argv[1], "r");
if (keyF == NULL) exit(EXIT_FAILURE);
/* Tell key Size */
fseek(keyF, 0, SEEK_END);
keySize = ftell(keyF);
rewind(keyF);
printf("\nKey Size: %d\n", keySize);
block = calloc(keySize, sizeof(char));
printf("\n-- Start Key --:\n");
for(count = 0; count < keySize/*sizeof(block)*/; count++){
char ch = fgetc(keyF);
block[count] = ch;
/*
Uncomment below to see your key on screen
*/
// printf("%c",ch);
}
printf("\n-- End Key --:\n");
fclose(keyF);
}
else if ( argc == 3
&& strcmp(argv[1], "genwrite") == 0
&& (sizeof(argv[2]) / sizeof(char)) > 2
)
{
printf("\n-- Attempting to create random key(ish --) of size: %d\n", keySize);
block = calloc(keySize, sizeof(char));
int count;
for(count = 0; count < keySize/*sizeof(block)*/; count++){
block[count] = (char) rand();
}
FILE *tmpfile;
tmpfile = fopen(".tmpckey", "w");
if(tmpfile == NULL) exit(EXIT_FAILURE);
fwrite(block, 1, keySize, tmpfile);
fclose(tmpfile);
chmod(".tmpckey", 0600);
}
else if ( argc == 4
&& strcmp(argv[1], "genwrite") == 0
&& (sizeof(argv[2]) / sizeof(char)) > 2
&& ((atoi(argv[3]) % 512) == 0)
)
{
keySize = atoi(argv[3]);
printf("\n-- Attempting to create random key(ish --) of size: %d\n", keySize);
block = calloc(keySize, sizeof(char));
int count;
for(count = 0; count < keySize/*sizeof(block)*/; count++){
block[count] = (char) rand();
}
FILE *tmpfile;
tmpfile = fopen(".tmpckey", "w");
if(tmpfile == NULL) exit(EXIT_FAILURE);
fwrite(block, 1, keySize, tmpfile);
fclose(tmpfile);
chmod(".tmpckey", 0600);
}
else {
printf("\n");
printf("################################################################################\n");
printf("# #\n");
printf("# Usage: #\n");
printf("# #\n");
printf("################################################################################\n");
printf("#> To write existing key to device: #\n");
printf("# #\n");
printf("# writekey </path/to/keyfile> </path/to/removable/sd*> #\n");
printf("# #\n");
printf("#> To generate and write pseudo random key, #\n");
printf("#> key will be saved to temporary file .tmpckey #\n");
printf("# #\n");
printf("# writekey genwrite </path/to/removable/sd*> <keysize in multiples of 512> #\n");
printf("# #\n");
printf("#> When keysize is not provided default size is set to %d. #\n", keySize);
printf("# #\n");
printf("################################################################################\n");
exit(1);
}
/*
Some printf debugging below, uncomment when needed to see what is going on.
*/
/*
printf("\nNumber of Args: %d\n", argc);
printf("\nCurrently block array contains: \n");
for(count = 0; count < keySize; count++){
printf("%c", block[count]);
}
printf("\n-- End block -- \n");
*/
/* Open Device itp... */
FILE *device = fopen(argv[2], "a");
if(device == NULL) exit(EXIT_FAILURE);
printf("\nDevice to write: %s\n", argv[2]);
fseek(device, 0, SEEK_END);
/* Determine where is the end */
long endOfDisk = ftell(device);
printf("\nDevice Size: %ld\n", endOfDisk);
/* Verify if key is multiple of blocks */
int numBlocks = 0;
if (keySize % 512 != 0 || endOfDisk < (blockSize + keySize) ) {
printf("\nSorry but key size is not multiple of block size or device you trying to write to is too small, try again. TA.\n");
fclose(device);
exit(1);
}
/* Make sure we start again */
rewind(device);
/* Get the required amount sunbstracting block size */
long startFrom = endOfDisk - blockSize - keySize;
/* Write some data to the disk */
printf("\nWriting data starting from: %ld\n", startFrom);
fseek(device, startFrom, SEEK_SET);
fwrite(block, 1, keySize, device);
printf("\nBlock Position after data write procedure : %ld\n", ftell(device));
/*
Below is just for convenience, to read what was written,
can aid in debugging hence left commented for later.
*/
/*
printf("\nAmount of Data written : %ld\n", ftell(device) - startFrom);
// Start reading from specified block
printf("\n>>>>>>>> DEBUGGING SECTION <<<<<<<<<\n");
rewind(device); //
fseek(device, startFrom, SEEK_SET);
printf("\nBlock Position before read attempted: %d\n", ftell(device));
printf("\nKey size: %d\n", keySize);
fread(block, 1, keySize, device);
// Do something with the data
printf("\nBlock Position startFrom: %ld\n", startFrom);
printf("\nBlock Position after read: %d\n", ftell(device));
printf("\n-- Buffer Read: --\n");
for(count = 0; count < keySize; count++){
printf("%c", block[count]);
}
printf("\n-- End block -- \n");
printf("\n-- -- \n");
printf("\n-- -- \n");
*/
/* Close file */
fclose(device);
/* Make sure freed array is zeroed */
memset(block, 0, keySize);
free(block);
/* Return success, might change it to be useful return not place holder */
return 0;
}
Чтобы убедиться, что ключ, записанный в raw-устройство, совпадает с ключом в файле (ниже ничего не выводится, если ключи идентичны):
diff -B <(./readkey </path/to/device> 4096) <(cat .tmpckey)
Или для существующего ключа, сгенерированного с использованием собственных средств:
diff -B <(./readkey </path/to/device> <generated elsewhere key size>) <(cat </path/to/keyfile>)
Благодарю вас
Вот решение, похожее на решение Эндрю, но
использование CRYPTTAB_TRIED, описанное на справочной странице Debian crypttab, для различения попыток, и
вызов существующего стандартного ключа
/lib/cryptsetup/scripts/passdev
с первой попытки.
Создайте свой ключевой файл или ключевой раздел, как обычно для скрипта.
Создайте следующий файл
/usr/local/bin/key-from-usb
и сделать его исполняемым.#!/bin/sh set -e if [ $CRYPTTAB_TRIED -ge 1 ]; then /lib/cryptsetup/askpass "Second try to unlock $CRYPTTAB_SOURCE ($CRYPTTAB_NAME). Please enter passphrase: " else /lib/cryptsetup/scripts/passdev $CRYPTTAB_KEY fi
В
/etc/crypttab
используйте параметрkeyscript=/usr/local/bin/key-from-usb
.Создавать
/etc/initramfs-tools/hooks/key-from-usb
с этим содержанием:#!/bin/sh PREREQ="" prereqs() { echo "$PREREQ" } case "$1" in prereqs) prereqs exit 0 ;; esac . "${CONFDIR}/initramfs.conf" . /usr/share/initramfs-tools/hook-functions manual_add_modules vfat copy_exec /usr/lib/cryptsetup/scripts/passdev /usr/lib/cryptsetup/scripts/passdev copy_exec /usr/local/bin/key-from-usb /usr/local/bin/key-from-usb
Первый
copy_exec
линия здесь необходима, потому чтоpassdev
не копируется, если он не упоминается вcrypttab
. Сходным образом,manual_add_modules vfat
обеспечит возможность использования vfat usb-диска.
Подсказка: используйте
lsinitramfs /boot/initrd.img-...
и сравните/сравните результаты, чтобы убедиться, что скрипт и все его зависимости включены.