Как загрузить парольную фразу LUKS с USB, возвращаясь к клавиатуре?

Я хочу настроить безголовый компьютер с Linux (Debian Wheezy) с шифрованием всего диска, с возможностью разблокировать диск либо с помощью USB-накопителя, либо путем ввода парольной фразы с клавиатуры. Моя отправная точка — это новая установка с использованием базовой опции шифрования всего диска в установщике Debian, которая управляет всем, кроме /boot, как группа логических томов, зашифрованная с помощью LUKS, и дает мне опцию клавиатуры. Я опишу свое текущее решение в ответе в надежде, что оно будет полезным и что другие смогут его улучшить.

Вот некоторые из проблем, которые у меня были:

  • Установка парольной фразы и размещение ее на USB-накопителе.

  • Своевременная загрузка USB-модулей.

  • Ожидание распознавания USB-накопителя Linux перед попыткой чтения с него.

  • Определение правильного USB-накопителя (а не какого-либо другого вставленного диска).

  • Написание «keyscript» для получения парольной фразы с USB-накопителя.

  • Обеспечение того, чтобы возврат к клавиатуре срабатывал во всех случаях сбоя USB.

Я приму ответ со значительными улучшениями и проголосую за ответы, которые предлагают вклад.


person Andrew    schedule 31.10.2013    source источник


Ответы (5)


Большая часть моего решения взята из сообщения Использование A USB-ключ для парольной фразы LUKS.

  1. Создайте случайную парольную фразу:

    dd if=/dev/urandom bs=1 count=256 > passphrase
    
  2. Вставьте USB-накопитель. вывод dmesg покажет имя устройства; предположим /dev/sdd. Выясните его размер:

    blockdev --getsize64 /dev/sdd
    
  3. Я решил установить кодовую фразу в конце необработанного устройства, полагая, что она может пережить любое случайное использование USB-накопителя.

    dd if=passphrase of=/dev/sdd bs=1 seek=<size-256>
    
  4. Добавьте парольную фразу в том LUKS:

    cryptsetup luksAddKey /dev/sda5 passphrase
    

    Это не влияет на существующую парольную фразу, введенную вручную из программы установки. Файл с парольной фразой можно удалить:

    rm passphrase
    
  5. Найдите уникальное имя для USB-накопителя, чтобы мы могли идентифицировать его при наличии:

    ls -l /dev/disk/by-id | grep -w sdd
    

    Вы должны увидеть одну символическую ссылку. Я назову его /dev/disk/by-id/<ID>.

  6. Изменить /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
    
  7. Упомянутому выше keyscript потребуется прочитать парольную фразу с USB-устройства. Однако для этого нужно сделать больше. Чтобы понять, как он используется, проверьте /usr/share/initramfs-tools/scripts/local-top/cryptroot, сценарий, который запускается во время загрузки для разблокировки корневого устройства. Обратите внимание, что когда установлено значение keyscript, оно просто запускается, и выходные данные направляются в luksOpen без какой-либо другой проверки. Невозможно сообщить об ошибке (отсутствует USB-накопитель) или вернуться к вводу с клавиатуры. Если кодовая фраза не работает, ключевой скрипт запускается снова в цикле до определенного количества раз; однако нам не говорят, на какой итерации мы находимся. Кроме того, мы не можем контролировать, когда запускается ключевой скрипт, поэтому мы не можем быть уверены, что Linux распознал USB-накопитель.

    Я решил это с помощью некоторых хаков:

    1. Опросите USB-накопитель и подождите 3 секунды, пока он не появится. Это работает для меня, но я хотел бы знать лучший способ.

    2. Создайте фиктивный файл /passphrase-from-usb-tried при первом запуске, чтобы указать, что мы запускались хотя бы один раз.

    3. Если мы были запущены хотя бы один раз или 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
    
  8. Драйверов USB не было в моих initramfs. (Похоже, в более поздних версиях Debian они по умолчанию.) Мне пришлось добавить их, добавив к /etc/initramfs-tools/modules:

    uhci_hcd
    ehci_hcd
    usb_storage
    
  9. Когда все будет готово, обновите initramfs:

    update-initramfs -u
    
person Andrew    schedule 31.10.2013
comment
Вероятно, это один из немногих случаев, когда стоит использовать /dev/random вместо /dev/urandom. - person Samuel Edwin Ward; 30.07.2014
comment
@SamuelEdwinWard - со страницы руководства crypttab: «... использование /dev/random может помешать завершению загрузки, если в системе недостаточно энтропии для генерации действительно случайного ключа шифрования». Однако использование /dev/random может стоить этой проблемы для дополнительной безопасности. - person Rondo; 13.10.2014
comment
@Rondo, я думаю, что в этом предложении говорится о создании нового случайного ключа при каждой загрузке. В этом случае ключ генерируется один раз перед загрузкой, так что это не должно быть проблемой. В худшем случае пользователь может генерировать больше энтропии, пока ключ не будет готов. - person Samuel Edwin Ward; 13.10.2014
comment
@SamuelEdwinWard - ах, правда. - person Rondo; 23.10.2014
comment
Мне эта процедура отлично подошла. Мне даже не пришлось использовать этот материал initramfs (достаточно было иметь ключевой скрипт) в Debian Jessie 8.0. - person metakermit; 10.11.2015
comment
В Debian 9 (stretch) с systemd, если ключевой файл в /etc/crypttab не является none (например, /dev/disk/by-id/<ID>) и USB-диск отсутствует, он запросит ключ, но также создаст службу (? ), который ожидает монтирования USB-диска, чтобы появился ключевой файл. Чтобы избежать этого, я явно установил CRYPTTAB_KEY в ключевом скрипте и установил для параметра ключевого файла в /etc/crypttab значение none. - person Diab Jerius; 16.10.2017

Для меня было бы идеально, если бы я мог просто иметь небольшой USB-накопитель с кодовой фразой, которая разблокирует диск. Это было бы удобно не только для серверов (где вы можете оставить флешку в сервере — цель состоит в том, чтобы иметь возможность возвращать сломанные жесткие диски, не беспокоясь о конфиденциальных данных), но и для моего ноутбука: вставьте 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

person Ole Tange    schedule 04.05.2014
comment
лучше дать ссылку непосредственно на .sh файл. - person kyb; 07.10.2017

Чтобы сопровождать отличные ответы выше, см. Подпрограммы C, которые вы можете использовать для записи/генерации и чтения необработанного ключа блочного устройства. «readkey.c» извлекает ключ заданного размера из блочного устройства, а «writekey.c» может генерировать или записывать существующий ключ на необработанное устройство. После компиляции «readkey.c» можно использовать в пользовательском скрипте для извлечения ключа известного размера из необработанного блочного устройства следующим образом:

readkey </path/to/device> <keysize>

Чтобы увидеть использование «writekey», после компиляции запустите его без флагов.
Для компиляции просто используйте:

gcc readkey.c -o readkey

gcc writekey.c -o writekey

Я протестировал оба варианта на флэш-накопителе USB 2.0 Verbatim емкостью 16 ГБ с пользовательским «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;
}

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

diff -B <(./readkey </path/to/device> 4096) <(cat .tmpckey)

Или для существующего ключа, сгенерированного собственными средствами:

diff -B <(./readkey </path/to/device> <generated elsewhere key size>) <(cat </path/to/keyfile>)

Благодарю вас

person Daniel    schedule 24.06.2016

несмотря на отличный ответ @Andrew, который работает в предыдущих версиях. Решение на самом деле устарело и требует много настроек для Ubuntu 18.04 и 19.10. Поэтому я хочу поделиться своим исследованием на эту тему.

Есть несколько уловок о crypttab. Sepcs на самом деле сильно изменились с 14.04 по 18.04 и по 19.10. Он начинает поддерживать больше параметров для cryptsetup. Например, смещение ключевого файла, размер ключевого файла и т. д. Некоторые параметры, например. nobootwait исчезли. Некоторые параметры уже поддерживаются в других дистрибутивах, но еще не поддерживаются в Ubuntu (например, очень хороший параметр keyfile-timeout. Это может исключить весь ключевой скрипт, поскольку он автоматически вернется к вводу с клавиатуры после keyfile-timeout).

Основная ошибка crypttab в Ubuntu заключается в том, что он фактически обрабатывается двумя разными процессами. Один из них — традиционно initramfs, а другой — современный systemd. Systemd должен быть более продвинутым и гибким во многих аспектах. Тем не менее, systemd плохо поддерживает cryptab, многие параметры, такие как keyscript, просто молча игнорируются. поэтому я понятия не имею, что происходит, пока не заметил этот пост. Почти все сообщения в Интернете о настройках crypttab предназначены для initramfs, а не для systemd. Поэтому нам нужно добавить initramfs ко всем записям в crypttab, чтобы избежать проблем.

Я также обнаружил хороший способ отлаживать наш keyscript и crypttab без виртуальной машины или многократной перезагрузки. Это cryptdisks_start. Прежде чем мы фактически распространим наши изменения в initramfs, мы всегда должны протестировать их с помощью этой замечательной команды. В противном случае вы окажетесь заблокированы в своей системе и сможете восстановить ее только через среду chroot.

@andrew опубликовал хороший способ использования скрытия данных в необработанной области файловой системы. Тем не менее, я обнаружил, что это очень раздражает, когда мы хотим автоматически создавать разделы и добавлять необработанные данные во множество usb-ключей, нам приходится вычислять смещение для всех разных файловых систем и разных размеров разделов. Более того, если пользователь случайно запишется на ФС, существует некоторый риск того, что ключ будет перезаписан. Необработанный раздел без какой-либо ФС в этом случае имеет больше смысла. Однако необработанный раздел не имеет UUID, что не очень полезно для автоматической разблокировки. Таким образом, я хотел бы представить способ просто использовать обычные файлы фраз-паролей в файловой системе usbkey. Основная проблема passdev заключается в том, что он не выполняет поиск/остановку во время чтения файла. Таким образом, мы не можем использовать параметр keyfile-offset и keyfile-size, когда хотим вернуться к вводу с клавиатуры. Поскольку cryptsetup на самом деле попытается пропустить входное содержимое, и если содержимое короче размера ключевого файла, ошибка. Это также означает, что если имеется большое смещение, passdev может работать очень медленно, поскольку он всегда читается с самого начала. Однако нет смысла реализовывать смещение и размер ключевого файла для реального файла в файловой системе. Я считаю, что они созданы для необработанного устройства.

Криптатаблица

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

фраза-пароль ключевого скрипта-из-usbfs.sh использовала /lib/cryptsetup/scripts/passdev, который будет ждать USB-устройство и монтировать fs, а затем передавать содержимое файла. Он поддерживает 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

Наконец, я опубликовал обновленную версию парольной фразы из-usb.sh, которая может использовать новые параметры в crypttab:

person Wang    schedule 29.10.2019

Вот решение, похожее на от Эндрю, но

  • используя CRYPTTAB_TRIED, описанный в справочной странице Debian crypttab, различать попытки и

  • вызов существующего стандартного ключевого скрипта /lib/cryptsetup/scripts/passdev с первой попытки.

  1. Создайте свой ключевой файл или ключевой раздел, как обычно для сценария passdev.

  2. Создайте следующий файл /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
    
  3. В /etc/crypttab используйте параметр keyscript=/usr/local/bin/key-from-usb.

  4. Создайте /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 обеспечит возможность использования USB-диска vfat.

Подсказка: используйте lsinitramfs /boot/initrd.img-... и сравните результаты, чтобы убедиться, что скрипт и все его зависимости включены.

person m4lvin    schedule 31.12.2020