mapped-devices: Allow unlocking by a key file.

Requiring the user to input their password in order to unlock a device is not
always reasonable, so having an option to unlock the device using a key file
is a nice quality of life change.

* gnu/system/mapped-devices.scm (open-luks-device): Add #:key-file argument.
(luks-device-mapping-with-options): New procedure.
* doc/guix.texi (Mapped Devices): Describe the new procedure.

Change-Id: I1de4e045f8c2c11f9a94f1656e839c785b0c11c4
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
Tomas Volf 2024-01-11 18:35:39 +01:00 committed by Ludovic Courtès
parent db43edaa0a
commit d082312ef7
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
2 changed files with 67 additions and 25 deletions

View File

@ -123,6 +123,7 @@ Copyright @copyright{} 2023 Foundation Devices, Inc.@*
Copyright @copyright{} 2023 Thomas Ieong@*
Copyright @copyright{} 2023 Saku Laesvuori@*
Copyright @copyright{} 2023 Graham James Addis@*
Copyright @copyright{} 2023 Tomas Volf@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@ -17992,6 +17993,30 @@ command from the package with the same name. It relies on the
@code{dm-crypt} Linux kernel module.
@end defvar
@deffn {Procedure} luks-device-mapping-with-options [#:key-file]
Return a @code{luks-device-mapping} object, which defines LUKS block
device encryption using the @command{cryptsetup} command from the
package with the same name. It relies on the @code{dm-crypt} Linux
kernel module.
If @code{key-file} is provided, unlocking is first attempted using that
key file. This has an advantage of not requiring a password entry, so
it can be used (for example) to unlock RAID arrays automatically on
boot. If key file unlock fails, password unlock is attempted as well.
Key file is not stored in the store and needs to be available at the
given location at the time of the unlock attempt.
@lisp
;; Following definition would be equivalent to running:
;; cryptsetup open --key-file /crypto.key /dev/sdb1 data
(mapped-device
(source "/dev/sdb1)
(target "data)
(type (luks-device-mapping-with-options
#:key-file "/crypto.key")))
@end lisp
@end deffn
@defvar raid-device-mapping
This defines a RAID device, which is assembled using the @code{mdadm}
command from the package with the same name. It requires a Linux kernel

View File

@ -2,6 +2,7 @@
;;; Copyright © 2014-2022 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2016 Andreas Enge <andreas@enge.fr>
;;; Copyright © 2017, 2018 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2024 Tomas Volf <~@wolfsden.cz>
;;;
;;; This file is part of GNU Guix.
;;;
@ -64,6 +65,7 @@
check-device-initrd-modules ;XXX: needs a better place
luks-device-mapping
luks-device-mapping-with-options
raid-device-mapping
lvm-device-mapping))
@ -188,7 +190,7 @@ option of @command{guix system}.\n")
;;; Common device mappings.
;;;
(define (open-luks-device source targets)
(define* (open-luks-device source targets #:key key-file)
"Return a gexp that maps SOURCE to TARGET as a LUKS device, using
'cryptsetup'."
(with-imported-modules (source-module-closure
@ -198,7 +200,8 @@ option of @command{guix system}.\n")
((target)
#~(let ((source #$(if (uuid? source)
(uuid-bytevector source)
source)))
source))
(keyfile #$key-file))
;; XXX: 'use-modules' should be at the top level.
(use-modules (rnrs bytevectors) ;bytevector?
((gnu build file-systems)
@ -215,29 +218,35 @@ option of @command{guix system}.\n")
;; 'cryptsetup open' requires standard input to be a tty to allow
;; for interaction but shepherd sets standard input to /dev/null;
;; thus, explicitly request a tty.
(zero? (system*/tty
#$(file-append cryptsetup-static "/sbin/cryptsetup")
"open" "--type" "luks"
;; Note: We cannot use the "UUID=source" syntax here
;; because 'cryptsetup' implements it by searching the
;; udev-populated /dev/disk/by-id directory but udev may
;; be unavailable at the time we run this.
(if (bytevector? source)
(or (let loop ((tries-left 10))
(and (positive? tries-left)
(or (find-partition-by-luks-uuid source)
;; If the underlying partition is
;; not found, try again after
;; waiting a second, up to ten
;; times. FIXME: This should be
;; dealt with in a more robust way.
(begin (sleep 1)
(loop (- tries-left 1))))))
(error "LUKS partition not found" source))
source)
#$target)))))))
(let ((partition
;; Note: We cannot use the "UUID=source" syntax here
;; because 'cryptsetup' implements it by searching the
;; udev-populated /dev/disk/by-id directory but udev may
;; be unavailable at the time we run this.
(if (bytevector? source)
(or (let loop ((tries-left 10))
(and (positive? tries-left)
(or (find-partition-by-luks-uuid source)
;; If the underlying partition is
;; not found, try again after
;; waiting a second, up to ten
;; times. FIXME: This should be
;; dealt with in a more robust way.
(begin (sleep 1)
(loop (- tries-left 1))))))
(error "LUKS partition not found" source))
source)))
;; We want to fallback to the password unlock if the keyfile fails.
(or (and keyfile
(zero? (system*/tty
#$(file-append cryptsetup-static "/sbin/cryptsetup")
"open" "--type" "luks"
"--key-file" keyfile
partition #$target)))
(zero? (system*/tty
#$(file-append cryptsetup-static "/sbin/cryptsetup")
"open" "--type" "luks"
partition #$target)))))))))
(define (close-luks-device source targets)
"Return a gexp that closes TARGET, a LUKS device."
@ -276,6 +285,14 @@ option of @command{guix system}.\n")
(close close-luks-device)
(check check-luks-device)))
(define* (luks-device-mapping-with-options #:key key-file)
"Return a luks-device-mapping object with open modified to pass the arguments
into the open-luks-device procedure."
(mapped-device-kind
(inherit luks-device-mapping)
(open (λ (source targets) (open-luks-device source targets
#:key-file key-file)))))
(define (open-raid-device sources targets)
"Return a gexp that assembles SOURCES (a list of devices) to the RAID device
TARGET (e.g., \"/dev/md0\"), using 'mdadm'."