Sometimes it is necessary that the system disk is encrypted and the system is loaded automatically. In this short outline I will focus on how to make it work.

I will use the key file on the USB storage device. You must consider the risks of using such a configuration.

Create a key:

dd if=/dev/random of=secretkey bs=1 count=4096

Add the key to your LUKS volume:

cryptsetup luksAddKey /dev/your_luks_dev secretkey

Copy the key to your usb storage (it is ext3 filesystem and labeled ‘USBCRYPT’ in my case).

mount /dev/disk/by-label/USBCRYPT /mnt
cp secretkey /mnt/
umount /mnt

Edit volume the record in /etc/crypttab:

Before:

root_volume_crypt UUID=714e7ed2-af19-4214-8981-34ba71652bf7 none luks

After:

root_volume_crypt UUID=714e7ed2-af19-4214-8981-34ba71652bf7 /dev/disk/by-label/USBCRYPT:/secretkey luks,keyscript=/lib/cryptsetup/scripts/passdev

Check whether the file /lib/cryptsetup/scripts/passdev exists in your setup.

If you have other partitions that you want to mount after boot, the records for them might look like this:

another_volume_crypt UUID=11f68a35-9fac-4f2b-81d2-593f739c8273 /path/to/anothersecretkey luks

Don’t forget to configure mounts in /etc/fstab:

/dev/mapper/root_volume_crypt / ext4 errors=remount-ro,nodelalloc,data=ordered 0 1
/dev/mapper/another_volume_crypt /your/mountpoint ext4 defaults,nodelalloc,data=ordered,user,users,nodev 0 2

After that I got some problems using this configuration, because one volume was mounted by passdev script and the others by systemd.

Systemd waited for the already mounted drive 90 seconds during boot. This timer could not be disabled, because the systemd automatically generates the service from the template using systemd-cryptsetup-generator.

I did not find any simple way to solve that and had to make a patch:

diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index 2d00bcf..0ca9c4b 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -384,6 +384,7 @@ static int add_crypttab_devices(void) {
                 char line[LINE_MAX], *l, *uuid;
                 crypto_device *d = NULL;
                 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
+                bool keyscript;

                 if (!fgets(line, sizeof(line), f))
                         break;
@@ -413,6 +414,12 @@ static int add_crypttab_devices(void) {
                         continue;
                 }

+                keyscript = fstab_test_option(options, "keyscript\0");
+                if (keyscript) {
+                        log_info("Not creating device '%s' because it has keyscript option.", name);
+                        continue;
+                }
+
                 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
                 if (r < 0)
                         return r;

I used Docker for compiling and building that systemd debian package to keep the system clean.

Dockerfile:

FROM debian:9

RUN echo "deb-src http://ftp.ru.debian.org/debian/ stretch main non-free contrib" >> /etc/apt/sources.list && \
    apt update && \
    apt install -y dpkg-dev devscripts && \
    apt build-dep -y systemd

RUN apt install -y nano

RUN useradd -m builder

WORKDIR /home/builder
USER builder
ENTRYPOINT [ "bash" ]

Systemd docker image build command:

docker build -t systemd .

Systemd docker container run command:

docker run -it --rm --name systemd -v $(pwd)/data:/home/builder -v $(pwd)/patch:/patch systemd

Systemd package build command sequence:

apt source systemd
cd systemd-*
git apply /patch
dpkg-source --commit
debuild -us -uc

You will get several .deb packages. systemd-cryptsetup-generator is in systemd_232-*.deb.