Skip to content

Instantly share code, notes, and snippets.

@HacKanCuBa
Last active October 24, 2024 10:36
Show Gist options
  • Save HacKanCuBa/b8565369de9620ba1eb24b75ee836857 to your computer and use it in GitHub Desktop.
Save HacKanCuBa/b8565369de9620ba1eb24b75ee836857 to your computer and use it in GitHub Desktop.
Encrypted SWAP hibernation in Debian 9+

Encrypted SWAP hibernation in Debian 9+

It took me about 6 hours to find out all of this, but after reading a ton of man pages, initram scripts, and bug reports, I got a working result that takes about 2' to set up...

The point is to have a SWAP partition encrypted with LUKS, and it should be decypted during boot.

When using SysV, initram hooks and scripts in Debian worked like a charm but then, Systemd came and it's not yet fully implemented so this kind of crap happens. Systemd's cryptsetup doesn't support parameters in /etc/crypttab so using a script there is ignored:

/* Options Debian's crypttab knows we don't:

    precheck=
    check=
    checkargs=
    noearly=
    loud=
    keyscript=
*/

Source.

Trying to decrypt SWAP partition via /etc/crypttab ends up usable but it can't resume from hibernation because Systemd tries to open it way too late, so the kernel gives up looking for a SWAP. To make it work, you need to pass parameters to the kernel, which Systemd will elegantly take.

Here's the whole deal (read everything first before starting doing things):

Let's say that your encrypted root partition is at /dev/disk/by-partlabel/root and your SWAP, /dev/disk/by-partlabel/swap (here I used GPT partition labels, you can also use UUID). I'll be naming cryptoroot and cryptoswap to the opened devices (accesible via /dev/mapper/...).

1- Edit or create /etc/initramfs-tools/conf.d/resume:
RESUME=/dev/mapper/cryptoswap

2- Remove references to SWAP in /etc/crypttab, i.e. a line like cryptoswap /dev/disk... /dev/urandom swap must be commented out or deleted.

3- Edit /etc/default/grub to set the kernel parameters for resume device and encryption: GRUB_CMDLINE_LINUX="resume=/dev/mapper/cryptoswap cryptopts=source=/dev/disk/by-partlabel/root,target=cryptoroot cryptopts=source=/dev/disk/by-partlabel/swap,target=cryptoswap"

4- Update initramfs and grub: sudo update-initramfs -u && sudo update-grub

Up to this point, you will need a password to open both devices, which is horribly anoying. However, a neat script, provided by cryptsetup, can be used: decypt_derived, which derives a key from an opened device (say, the root device). So, here's what you need to do (skip previous 3rd and 4th steps):

3- Add derived key to SWAP: sudo cryptsetup luksAddKey /dev/disk/by-partlabel/swap <(/lib/cryptsetup/scripts/decrypt_derived cryptoroot)
Verify that it worked: cryptsetup -v --test-passphrase -d <(/lib/cryptsetup/scripts/decrypt_derived cryptoroot) luksOpen /dev/disk/by-partlabel/swap

4- Edit /etc/default/grub: GRUB_CMDLINE_LINUX="resume=/dev/mapper/cryptoswap cryptopts=source=/dev/disk/by-partlabel/root,target=cryptoroot cryptopts=source=/dev/disk/by-partlabel/swap,target=cryptoswap,keyscript=/lib/cryptsetup/scripts/decrypt_derived,key=cryptoroot"
Double-check device and partition names!

5- Add an initram hook to copy decypt_derived binary to the initramfs, create the file /etc/initramfs-tools/hooks/cp_decrypt_derived:

#!/bin/sh
PREREQ=""
prereqs()
{
   echo "$PREREQ"
}

case $1 in
prereqs)
   prereqs
   exit 0
   ;;
esac

. /usr/share/initramfs-tools/hook-functions
# Begin real processing below this line

copy_exec /lib/cryptsetup/scripts/decrypt_derived /lib/cryptsetup/scripts/ >&2

6- Update initramfs and grub: sudo update-initramfs -u && sudo update-grub

That's it! You can hibernate with systemctl hibernate (no need for sudo). If you use a keyfile for the root partition, the same can be done by pointing the kernel to an appropriate keyscript that reads and pushes the key, and a hook that copies said script into initramfs.

Sources

@domenpk
Copy link

domenpk commented Oct 24, 2024

The script problem on Debian bookworm can also be resolved by changing "copy_exec" line into (it seems it gets confused by the "target" argument, and "copy_exec" should work without that as well):

copy_file script /usr/lib/cryptsetup/scripts/decrypt_derived

Only now have I read https://gist.github.com/HacKanCuBa/b8565369de9620ba1eb24b75ee836857?permalink_comment_id=3432544#gistcomment-3432544 which seems like a more sensible option! Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment