Skip to content

Instantly share code, notes, and snippets.

@craigphicks
Last active May 22, 2025 22:42
Show Gist options
  • Save craigphicks/115c02969e0d69a9a672ca8c2992f06f to your computer and use it in GitHub Desktop.
Save craigphicks/115c02969e0d69a9a672ca8c2992f06f to your computer and use it in GitHub Desktop.
Unlocking LUKS full disk encryption with a USB key (for e.g., headless)

Unlocking LUKS full disk encryption with a USB key

Use case

It is being used to boot up a headless system running debian12 (bookworm)

Prepare USB

Prepare a USB containing a file keyfile which contains a password to open the LUKS partition. The UUID of the USB will be used. In this example case it is 69A0-9BE4.

ls -l /dev/sda1

total 1
-rwxr-xr-x 1 root root 20 May 22 11:15 keyfile

lsblk -o NAME,UUID,MOUNTPOINTS

gupper@gupper:~/github/codium-server$ lsblk -o NAME,UUID,MOUNTPOINTS
NAME                    UUID                                   MOUNTPOINTS
sda                                                            
└─sda1                  69A0-9BE4                              /mnt/sda1
nvme0n1                                                        
├─nvme0n1p1             XXXX-XXXX                              /boot/efi
├─nvme0n1p2             XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX   /boot
└─nvme0n1p3             XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX   
  └─nvme0n1p3_crypt     XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 

Prepare files

sudo nano /etc/crypttab

nvme0n1p3_crypt /dev/nvme0n1p3 69A0-9BE4 luks,discard,tries=0,keyscript=/bin/luks-unlock

sudo nano /bin/luks-unlock

#!/bin/sh
# Function for logging
log_msg() {
    local formatted=$(date '+%Y-%m-%d %H:%M:%S')
    # during initramfs, cannot write to journal, must write to /dev/kmesg
    echo "[${formatted}] luks-unlock: $1" >> /dev/kmsg
    # use the following line for testing
    #echo "luks-unlock: $1"
}

check_timeout_return() {
    # On the 10th failure (about 10 seconds) 
    if [ "${CRYPTTAB_TRIED:-0}" -ge 10 ]; then
        log_msg "Maximum attempts (${CRYPTTAB_TRIED}) reached, falling back to manual password entry"
        /lib/cryptsetup/askpass "Enter password manually (or insert usb) and press enter"
        exit 0
    fi
    exit 1
}

# Log all arguments and relevant environment variables
log_msg "================================================"
log_msg "Script started"
log_msg "Arguments:"
log_msg "  \$1='$1'"
log_msg "  \$2='$2'"
log_msg "Environment:"
log_msg "  CRYPTTAB_NAME='$CRYPTTAB_NAME'"
log_msg "  CRYPTTAB_SOURCE='$CRYPTTAB_SOURCE'"
log_msg "  CRYPTTAB_KEY='$CRYPTTAB_KEY'"
log_msg "  CRYPTTAB_OPTIONS='$CRYPTTAB_OPTIONS'"
log_msg "  CRYPTTAB_TRIED='$CRYPTTAB_TRIED'"
log_msg "================================================"
ls -al /dev/disk/by-uuid | while read -r line; do
    log_msg "$line"
done
mkdir -p /mnt/tmp
if usbdevice=$(blkid --uuid "$CRYPTTAB_KEY"); then
    # blkid succeeded, usbdevice contains the path
    log_msg "Found device: $usbdevice"
else
    # blkid failed
    log_msg "Failed to find device with UUID $CRYPTTAB_KEY"
    check_timeout_return
fi
usbdevice=$(readlink -f "/dev/disk/by-uuid/$CRYPTTAB_KEY")
log_msg "usbdevice: $usbdevice"
if mount -t vfat "$usbdevice" /mnt/tmp 2>/dev/null; then
    if [ -e /mnt/tmp/keyfile ]; then
        cat /mnt/tmp/keyfile
        umount "$usbdevice"
        exit 0
    else
        log_msg "/mnt/tmp/keyfile not found"
    fi
    umount "$usbdevice"
else 
    log_msg "mount usbdevice failed"
fi
check_timeout_return

sudo nano /etc/initramfs-tools/modules file

# List of modules that you want to include in your initramfs.
# They will be loaded at boot time in the order below.
#
# Syntax:  module_name [args ...]
#
# You must run update-initramfs(8) to effect this change.
#
# Examples:
#
# raid1
# sd_mod
vfat
nls_cp437
nls_ascii
usb_storage
libblkid

Update the initramfs file

In order to prevent being locked out of your system if the initramfs file is not function do two things:

  1. Ensure you have an older OS version you can log in with (that has an older initramfs file)
  2. Backup the current initramfs file

E.g. ls -l /boot/initrd*

-rw-r--r-- 1 root root 35894187 May 9 13:49 /boot/initrd.img-6.1.0-34-amd64
-rw-r--r-- 1 root root 35898841 May 22 14:35 /boot/initrd.img-6.1.0-35-amd64
-rw-r--r-- 1 root root 35846206 May 21 09:49 /boot/initrd.img-6.1.0-35-amd64.backup

The update command: sudo update-initramfs -u

Now reboot to test.

Example log

sudo journalctl | grep luks

May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: ================================================
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: Script started
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: Arguments:
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock:   $1='69A0-9BE4'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock:   $2=''
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: Environment:
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock:   CRYPTTAB_NAME='nvme0n1p3_crypt'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock:   CRYPTTAB_SOURCE='/dev/nvme0n1p3'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock:   CRYPTTAB_KEY='69A0-9BE4'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock:   CRYPTTAB_OPTIONS='luks,discard,tries=0,keyscript=/bin/luks-unlock'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock:   CRYPTTAB_TRIED='0'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: ================================================
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: total 0
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: drwxr-xr-x    2 0        0              100 May 22 22:28 .
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: drwxr-xr-x    7 0        0              140 May 22 22:28 ..
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: lrwxrwxrwx    1 0        0               15 May 22 22:28 0BBC-867A -> ../../nvme0n1p1
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: lrwxrwxrwx    1 0        0               15 May 22 22:28 5029d8c7-0006-4f5b-9cdf-348f245ff7a9 -> ../../nvme0n1p2
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: lrwxrwxrwx    1 0        0               15 May 22 22:28 f04fedd7-814f-4dc9-9b44-9b8fab90ddbf -> ../../nvme0n1p3
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:51] luks-unlock: Failed to find device with UUID 69A0-9BE4
......
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: ================================================
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: Script started
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: Arguments:
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock:   $1='69A0-9BE4'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock:   $2=''
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: Environment:
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock:   CRYPTTAB_NAME='nvme0n1p3_crypt'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock:   CRYPTTAB_SOURCE='/dev/nvme0n1p3'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock:   CRYPTTAB_KEY='69A0-9BE4'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock:   CRYPTTAB_OPTIONS='luks,discard,tries=0,keyscript=/bin/luks-unlock'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock:   CRYPTTAB_TRIED='7'
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: ================================================
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: total 0
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: drwxr-xr-x    2 0        0              120 May 22 22:28 .
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: drwxr-xr-x    7 0        0              140 May 22 22:28 ..
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: lrwxrwxrwx    1 0        0               15 May 22 22:28 0BBC-867A -> ../../nvme0n1p1
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: lrwxrwxrwx    1 0        0               15 May 22 22:28 5029d8c7-0006-4f5b-9cdf-348f245ff7a9 -> ../../nvme0n1p2
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: lrwxrwxrwx    1 0        0               10 May 22 22:28 69A0-9BE4 -> ../../sda1
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: lrwxrwxrwx    1 0        0               15 May 22 22:28 f04fedd7-814f-4dc9-9b44-9b8fab90ddbf -> ../../nvme0n1p3
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: Found device: /dev/sda1
May 22 15:29:04 gupper unknown: [2025-05-22 22:28:58] luks-unlock: usbdevice: /dev/sda1

References

  1. https://tqdev.com/2022-luks-with-usb-unlock
  2. https://www.reddit.com/r/linux/comments/sag9xw/unlocking_luks_full_disk_encryption_with_a_usb_key/
  3. https://cryptsetup-team.pages.debian.net/cryptsetup/README.initramfs.html
  4. https://manpages.debian.org/bookworm/cryptsetup/crypttab.5.en.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment