|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +## This early-setup hook finds a LUKS volume by looking for a partition with |
| 4 | +## label "KEYSTORE". (Partition labels are supported on GPT and a few obsolete |
| 5 | +## disklabel formats; see the "name" command in parted(8) for details.) |
| 6 | +## |
| 7 | +## If a KEYSTORE partition is found, the hook attempts repeatedly to unlock and |
| 8 | +## mount the encrypted volume (read-only) at /etc/zfs/keys. If successful, this |
| 9 | +## will allow ZFSBootMenu to automatically unlock any ZFS datasets that define |
| 10 | +## the ZFS property |
| 11 | +## |
| 12 | +## keylocation=file:///etc/zfs/keys |
| 13 | +## |
| 14 | +## If the partition cannot be found, does not appear to be a LUKS volume or has |
| 15 | +## already been activated, the hook will terminate and allow ZFSBootMenu to |
| 16 | +## proceed with its ordinary startup process. Once the hook begins the unlock |
| 17 | +## loop, it will not terminate until either the volume is successfully unlocked |
| 18 | +## or the user presses Ctrl-C to abandon the attempts. After every failed |
| 19 | +## unlock cycle, an emergency shell will be invoked to allow manual |
| 20 | +## intervention; type `exit` in the shell to continue the unlock loop. |
| 21 | +## |
| 22 | +## Because this script is intended to provide unlock keys *before* ZFSBootMenu |
| 23 | +## imports ZFS pools, it should be run as an early hook. To install, put this |
| 24 | +## script somewhere, make sure it is executable, and add the path to the |
| 25 | +## `zfsbootmenu_early_setup` space-separated list with, e.g., |
| 26 | +## |
| 27 | +## zfsbootmenu_early_setup+=" <path to script> " |
| 28 | +## |
| 29 | +## in a dracut.conf(5) file inside the directory specified for the option |
| 30 | +## `Global.DracutConfDir` in the ZFSBootMenu `config.yaml`. |
| 31 | + |
| 32 | +# shellcheck disable=SC1091 |
| 33 | +[ -f /lib/zfsbootmenu-lib.sh ] && source /lib/zfsbootmenu-lib.sh |
| 34 | + |
| 35 | +luks="/dev/disk/by-partlabel/KEYSTORE" |
| 36 | +dm="/dev/mapper/KEYSTORE" |
| 37 | + |
| 38 | +if [ ! -b "${luks}" ] ; then |
| 39 | + zinfo "keystore device ${luks} does not exist" |
| 40 | + exit |
| 41 | +fi |
| 42 | + |
| 43 | +if ! cryptsetup isLuks ${luks} >/dev/null 2>&1 ; then |
| 44 | + zwarn "keystore device ${luks} missing LUKS partition header" |
| 45 | + exit |
| 46 | +fi |
| 47 | + |
| 48 | +if cryptsetup status "${dm}" >/dev/null 2>&1 ; then |
| 49 | + zinfo "${dm} already active, not continuing" |
| 50 | + exit |
| 51 | +fi |
| 52 | + |
| 53 | +header="$( center_string "[CTRL-C] cancel luksOpen attempts" )" |
| 54 | + |
| 55 | +while true; do |
| 56 | + tput clear |
| 57 | + colorize red "${header}\n\n" |
| 58 | + |
| 59 | + cryptsetup --tries=5 luksOpen "${luks}" KEYSTORE |
| 60 | + ret=$? |
| 61 | + |
| 62 | + # successfully entered a passphrase |
| 63 | + if [ "${ret}" -eq 0 ] ; then |
| 64 | + mkdir -p /etc/zfs/keys |
| 65 | + mount -r "${dm}" /etc/zfs/keys |
| 66 | + zdebug "$( |
| 67 | + cryptsetup status "${dm}" |
| 68 | + mount | grep KEYSTORE |
| 69 | + )" |
| 70 | + exit |
| 71 | + fi |
| 72 | + |
| 73 | + # ctrl-c'd the process |
| 74 | + if [ "${ret}" -eq 1 ] ; then |
| 75 | + zdebug "canceled luksOpen attempts via SIGINT" |
| 76 | + exit |
| 77 | + fi |
| 78 | + |
| 79 | + # failed all password attempts |
| 80 | + if [ "${ret}" -eq 2 ] ; then |
| 81 | + if prompt="Continuing in %0.2d seconds" timed_prompt "[RETURN] continue unlock attempts" "[ESCAPE] emergency shell" "" ; then |
| 82 | + continue |
| 83 | + else |
| 84 | + emergency_shell "Unable to unlock LUKS partition" |
| 85 | + fi |
| 86 | + fi |
| 87 | +done |
0 commit comments