#774647 can't a use key file stored on an encrypted rootfs to unlock the resume device at initramfs stage

#774647#5
Date:
2015-01-05 18:15:48 UTC
From:
To:
Dear Maintainer,

I have the following setup (see below for the files)

+ /boot on /dev/sda1
+ root filesystem on /dev/mapper/sda2_cryptblk
+ /home, /var and swap on LVM on /dev/mapper/sdb2_crypt
--- blkid ---
/dev/mapper/vg1-home: LABEL="HOME" UUID="3c300542" TYPE="ext4"
/dev/mapper/vg1-var:  LABEL="VAR"  UUID="c4be931f" TYPE="ext4"
/dev/mapper/vg1-swap: LABEL="swap" UUID="cb8020c2" TYPE="swap"
-------------

sda2_crypt is protected by a password I enter upon boot-up,
sdb2_crypt with a key file stored on the root filesystem.

I added resume=UUID=... pointing to my swap to the kernel command
line and ran update-initramfs and got the following message

cryptsetup: WARNING: target sdb2_crypt uses a key file, skipped

Apparently the cryptsetup initramfs scripts do not support my
case where a key file for a partition is stored on another
encrypted partition.

A simmilar use case has been described here:

https://bugs.launchpad.net/ubuntu/+source/cryptsetup/+bug/238163

#774647#10
Date:
2015-01-18 13:11:58 UTC
From:
To:
Hi,

The main reason reported this problem is that I want to enter
a single password to decrypt all my partitions. In such case
there is a way to work the problem around:

a) set the same password for all the devices you want initrd to decrypt,
b) use keyctl to cache the password.

My /etc/crypttab now looks like this:
-----8<----- sda2_crypt UUID=e499987ab017 root_key luks,keyscript=decrypt_keyctl sdb2_crypt UUID=c3b74b86b567 root_key luks,keyscript=decrypt_keyctl -----8<----- The procedure is described in the README.keyctl file. http://anonscm.debian.org/viewvc/pkg-cryptsetup/cryptsetup/trunk/debian/README.keyctl?revision=977&view=co
#774647#15
Date:
2015-12-09 05:14:10 UTC
From:
To:
Control: merge 776409 -1

Hi,

Yeah, it's because in the initramfs (before pivot_root) the key files
are relative to the real rootfs's mountpoint (/root).  Sergio Gelato has
found another workaround [0] using a dummy keyscript.

I'll see how to support this use case natively.  As documented in
crypttab(5), “the initramfs hook processes the root device, any resume
devices and any devices with the initramfs option set”, so indeed we
could safely include a keyfile if stored on an encrypted device that's
processed earlier.  AFAICT it's mostly a matter of getting the file's
mountpoint and finding out whether the device was already included in
conf.d/cryptroot.

Cheers,

#774647#28
Date:
2015-12-12 19:38:32 UTC
From:
To:
Here is a simple patch that adds keyfile support for non-root devices.
Ideally we would also warn the user if the key file is stored on an
unencrypted device, but that requires some code refactoring in the hook
file so it'll come in a later patch.

#774647#35
Date:
2015-12-17 09:55:06 UTC
From:
To:
Hi Guilhem,

Am 12.12.2015 um 20:38 schrieb Guilhem Moulin:

I like your idea to check whether the keyfile is on the rootfs for
resume and other non-rootfs devices that are processed during initramfs.

I've some comments and questions regarding your patch though. Have you
tested the patch already?

What's the reason for introducing the isrootdev variable? Why not use
the "rootdev" option that's already there? Honestly, I don't get it.
Probably I miss something, though ;)

*/ You set isrootdev=n at the beginning of add_device(). If the device
   is an underlying device for rootfs , then rootdev is added to $opts
   (old) and now, isrootdev=y is set additionally (new). The value of
   isrootdev is given to get_device_opts() as $3.
*/ At the start of get_device_opts(), isrootdev is set to the value of
   $3. If the opt "rootdev" is set, then isrootdev=y is set.
*/ Up to now, the variable $isrootdev is only set to 'y' in exactly the
   same situations as that the opt "rootdev" is set as well. If rootdev
   is not set in $opts, then $isroodev=n. So in my eyes, the opt
   "rootdev" and the variable $isrootdev are consistent.
*/ Later in get_device_opts() when the keyfiles are processed, you
   check for $isrootdev and warn if it $isrootdev!=n. Why not simply
   search for "rootdev" in $OPTIONS instead? See below for a suggestion.

See above.

See above. I think, that the following would be sufficient:

if echo "$OPTIONS" | grep -q "\brootdev\b"; then

Would need to stat against the canonical file (e.g. after readlink)
here. Otherwise, the keyfile path can be a link on the rootfs that
points to another partition. Should be as easy as adding:

keylink=$(readlink -e "$dev")

just before the case selection and running stat against $keylink.

No need for the TODO in my eyes. We only get here, if the key is stored
on the rootfs. In this case, we don't fiddle around with the keyfile at
all. All we do, is change the path to the keyfile in cryptroot-script,
so the keyfile itself is not touched by us at all.

Sure, it would be nice to warn the user if she stores the keyfile on an
unencrypted root fs, but then this is just one more corner case where a
user implements an uncommon custom setup in an unsecure fashion, and we
cannot check against all those corner cases anyway.

Would be key="$keylink" then.

See above.

I like :)

See above.

See above.

Cheers
 jonas

#774647#40
Date:
2015-12-19 18:20:06 UTC
From:
To:
Hi,

Yes, but only on a resume device (and without LVM).  (But I don't see
how the same wouldn't work for other devices and/or LVM.)

I only wanted to avoid parsing the $OPTIONS string :-P  But indeed it's
easier to grep through $OPTIONS.  And not more error prone if we assume
that crypttab fields don't contain blanks or commas.

As pointed out on IRC, word delimiters are error prone since
‘key=/path/to/rootdev.key’ would match, for instance.  But

    if echo "$OPTIONS" | grep -q "^(.*,)?rootdev(,.*)?$"; then

looks fine.

Oh, right, stat's -L flag deferences symlink, but doesn't take the
canonical path, indeed.

Makes sense, fixed and new patch enclosed.

Cheers,

#774647#45
Date:
2015-12-19 18:35:55 UTC
From:
To:
Sorry, typo :-P
#774647#50
Date:
2015-12-19 23:39:06 UTC
From:
To:
current init which requires all node devices to be present before the
rootfs is being mounted, as found in initramfs-tools(8):

	local-top OR nfs-top After these scripts have been executed, the root
	device node is expected to be present (local) or the network interface is
	expected to be usable (NFS).

	local-block These scripts are called with the name of a local block
	device. After these scripts have been executed, that device node should be
	present. If the local-top or local-block scripts fail to create the wanted
	device node, the local-block scripts will be called periodically to try
	again.

	local-premount OR nfs-premount are run after the sanity of the root device
	has been verified (local) or the network interface has been brought up
	(NFS), but before the actual root fs has been mounted.

	local-bottom OR nfs-bottom are run after the rootfs has been mounted
	(local) or the NFS root share has been mounted.

So I guess we'll have to mount the roofs read-only in a temporary
directory, and unmount it afterwards.

#774647#55
Date:
2016-09-15 13:34:42 UTC
From:
To:
I just added support for unlocking devices at initramfs stage using a
key file stored on the encrypted root FS.  However the resume device
won't be unlocked this way since the resume boot script is currently run
before mounting the root FS.

#774647#76
Date:
2023-03-25 23:15:21 UTC
From:
To:
Hey.

I recently considered to do the same, i.e.:
- have a passphrase only for the dm-crypt encrypted rootfs
- have a separate dm-crypt encrypted swap device for hibernate only
- use a high-entropy key-file on the rootfs to decrypt the swap device


My understanding of the initramfs-tools boot is as follows:
init-top
...
local-top => here cryptroot opens ("decrypts") the root- and resume-
             device as well as any with "initramfs"-option in crypttab
local-bottom => it retries the same here
local-premount => here, none of these devices has been mounted, yet
                  also here, the resume happens, at which point
                  the system is completely replaced, the initramfs used
                  just before for booting into the resume no longer
                  exists, no mounting of the devices will take place,
                  no pivot_root either
                  (none of this is anyway necessary, as the resumed
                  system has all that already done)

So the only way to get a key-file within the (not mounted) rootfs after
local-top/bottom but before the resume in local-premount would be to
actually mount the root fs before.

This is however pretty dangerous.
Even if the mounting is done read-only, filesystems may perform changes
(at least btrfs does, and I think ext4 may do so too).


There was recently [0], where someone mounted the root-fs in-between
suspend and resume and got corruptions.
While it was argued that the filesystem was frozen at suspend and that
btrfs would *try* to detect (since 6.2) whether it was mounted in-
between,... it was also argued that caching (in the resumed system) may
cause corruptions.


The blockdevice would need to be blockdev --setro first, but even that
may be more complex than one might think:
Consider e.g. multi-device filesystems (again e.g. btrfs), where the
other devices are auto-detected via UUID.


So IMO, this feature cannot be safely implemented.


Maybe the only way to do it safely was a hack:
- create a swapfile in the rootfs (this is anyway required to be not
  moved)
- get it's physical offest into the device (beware: for btrfs special
  commands are needed for that)
- let cryptroot read the key raw from that offest

But, again, quite ugly and hacky.


Cheers,
Chris.



[0] https://lore.kernel.org/linux-btrfs/ba9fb1c9-ccbc-4b93-92f9-a8c17ffab7f6@business-insulting.de/

#774647#81
Date:
2023-03-27 14:29:40 UTC
From:
To:
Hey.

I rather think now that even my hack with the swapfile isn't really
save.
The idea with that was that it's just the file, but not activated as
swap of course. But who knows for sure that in this case the file is
never moved.


Anyway, @Guilhem, would you agree to close this as wontfix and add a
README.x entry that describes why - with hibernation/resume - the key
file cannot safely be loaded from a filesystem that is hibernated, too?

Not sure whether to better put it in README.initramfs (yes it happens
in that phase) or README.Debian (in principle a user could just hack
something together on his own with the same issue and not even install
cryptsetup-initramfs).


Cheers,
Chris.