diff --git a/README.md b/README.md index c52f1ea..12d18f6 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Tiny initramfs written in POSIX shell for eweOS, forked from https://github.com/ - Portable, not distro specific - Easy to use configuration - Make time and init time hooks +- LUKS (detached header, key) - mdev supported - Resume from swap partition @@ -27,6 +28,8 @@ Tiny initramfs written in POSIX shell for eweOS, forked from https://github.com/ - Optional. Required for UUID, LABEL, PARTUUID support * `mdev` OR CONFIG_UEVENT_HELPER - Optional. Required for modular kernel, /dev/mapper/* and /dev/disk/* creation +* `cryptsetup` + - Optional. Required for LUKS support * `busybox loadkmap` OR `kbd loadkeys` - Optional. Required for keymap support * `plymouth` diff --git a/config.example.conf b/config.example.conf index 88a3443..009dd8c 100644 --- a/config.example.conf +++ b/config.example.conf @@ -95,3 +95,28 @@ live_ram_opts="size=50%,mode=0755" # define keymap file to be used keymap_path= + +################################### +## hook: luks +# activated if "luks" hook is included in hooks + +# define luks root partition to be used, unset to use $root and override $root when volume is decrypted +# /dev/xdY or /dev/nvme0nXpY +# LABEL=label +# PARTUUID=partuuid +# UUID=uuid +#luks_root= + +# define to allow the use of discard (TRIM) requests for your luks device +# WARNING*: This option may have a negative security impact. For more info read *cryptsetup*(8). +#luks_discard= + +# define path to LUKS header +#luks_header= + +# define path to LUKS keyfile +#luks_key= + +# name to map LUKS device to +# default: crypt-${device##*/} +#luks_name= \ No newline at end of file diff --git a/doc/tinyramfs.5 b/doc/tinyramfs.5 index 3e76d69..1064d4a 100644 --- a/doc/tinyramfs.5 +++ b/doc/tinyramfs.5 @@ -5,7 +5,7 @@ .nh .ad l .\" Begin generated content: -.TH "tinyramfs" "5" "2024-12-17" "tinyramfs" "2024-12-03" +.TH "tinyramfs" "5" "2025-01-03" "tinyramfs" "2024-12-03" .P .SH NAME .P @@ -108,6 +108,41 @@ keymap_path Path to your keymap.\& .P .RE +.SS LUKS +.P +luks_discard +.P +.RS 4 +(bool) Allow the use of discard (TRIM) requests for your luks device.\& +.P +\fBWARNING\fR: This option may have a negative security impact.\& For more info +read \fBcryptsetup\fR(8).\& +.P +.RE +luks_header +.P +.RS 4 +Path to your LUKS header.\& +.P +.RE +luks_root +.P +.RS 4 +The device your LUKS volume is located on.\& +.P +.RE +luks_name +.P +.RS 4 +The name to map your LUKS device to.\& +.P +.RE +luks_key +.P +.RS 4 +Path to your LUKS keyfile.\& +.P +.RE .SS RESUME .P resume diff --git a/doc/tinyramfs.5.scd b/doc/tinyramfs.5.scd index d20d6a8..5f1a11a 100644 --- a/doc/tinyramfs.5.scd +++ b/doc/tinyramfs.5.scd @@ -83,6 +83,31 @@ keymap_path Path to your keymap. +## LUKS + +luks_discard + + (bool) Allow the use of discard (TRIM) requests for your luks device. + + *WARNING*: This option may have a negative security impact. For more info + read *cryptsetup*(8). + +luks_header + + Path to your LUKS header. + +luks_root + + The device your LUKS volume is located on. + +luks_name + + The name to map your LUKS device to. + +luks_key + + Path to your LUKS keyfile. + ## RESUME resume diff --git a/doc/tinyramfs.8 b/doc/tinyramfs.8 index aac5932..2badc4c 100644 --- a/doc/tinyramfs.8 +++ b/doc/tinyramfs.8 @@ -5,7 +5,7 @@ .nh .ad l .\" Begin generated content: -.TH "tinyramfs" "8" "2024-12-17" "tinyramfs" "2024-12-03" +.TH "tinyramfs" "8" "2025-01-03" "tinyramfs" "2024-12-03" .P .SH NAME .P @@ -66,6 +66,12 @@ Use helper scripts in $PWD/lib/, instead of /lib/tinyramfs/.\& Look for kernel modules in , instead of /lib/modules/.\& .P .RE +\fB-v\fR +.P +.RS 4 +Increase logging verbosity.\&.\& +.P +.RE .SH FILES .P /lib/tinyramfs/hook.\&d/ diff --git a/hook/luks/luks b/hook/luks/luks new file mode 100644 index 0000000..3bba191 --- /dev/null +++ b/hook/luks/luks @@ -0,0 +1,36 @@ +# vim: set ft=sh: +# shellcheck shell=sh +# +# https://shellcheck.net/wiki/SC2154 +# shellcheck disable=2154 + +if [ ! -f "/usr/bin/cryptsetup" ]; then + panic "cryptsetup not installed, please check your configuration" +fi + +[ "$luks_key" ] && { + copy_file "${luks_key#*=}" /root/luks_key 0400 + + sed "s|${luks_key#*=}|/root/luks_key|" \ + "${tmpdir}/etc/tinyramfs/config" > "${tmpdir}/_" + + mv "${tmpdir}/_" "${tmpdir}/etc/tinyramfs/config" +} + +[ "$luks_header" ] && { + copy_file "${luks_header#*=}" /root/luks_header 0400 + + sed "s|${luks_header#*=}|/root/luks_header|" \ + "${tmpdir}/etc/tinyramfs/config" > "${tmpdir}/_" + + mv "${tmpdir}/_" "${tmpdir}/etc/tinyramfs/config" +} + +for _mod in \ + aes ecb xts lrw wp512 sha256 \ + sha512 twofish serpent dm-crypt +do + copy_kmod "$_mod" +done + +copy_exec cryptsetup diff --git a/hook/luks/luks.init b/hook/luks/luks.init new file mode 100644 index 0000000..52c5a0c --- /dev/null +++ b/hook/luks/luks.init @@ -0,0 +1,85 @@ +# vim: set ft=sh: +# shellcheck shell=sh +# +# https://shellcheck.net/wiki/SC2154 +# shellcheck disable=2154 + +# https://shellcheck.net/wiki/SC2034 +# shellcheck disable=2034 +DM_DISABLE_UDEV=1 + +if [ ! -f "/usr/bin/cryptsetup" ]; then + panic "cryptsetup not installed" +fi + +mkdir -p /run/cryptsetup + +if [ -z "$luks_root" ]; then + luks_root=$root +fi + +luks_discard=${luks_discard:+--allow-discards} +luks_header=${luks_header:+--header="$luks_header"} +luks_key=${luks_key:+--key-file="$luks_key"} +luks_name="${luks_name:-crypt-${device##*/}}" + +resolve_device "$luks_root" + +if [ -n "$luks_key" ] && [ ! -f "$luks_key" ]; then + print_warn "Keyfile could not be opened. Reverting to passphrase." + unset luks_key +fi + +if [ -b "/dev/mapper/${luks_name}" ]; then + print_warn "Device ${luks_name} already exists, not doing any crypt setup." +else + if cryptsetup isLuks "${device}" >/dev/null 2>&1; then + luks_succeeded=0 + + # If keyfile exists, try to use that first + if [ -n "$luks_key" ]; then + # https://shellcheck.net/wiki/SC2086 + # shellcheck disable=2086 + if eval cryptsetup open ${luks_discard} ${luks_header} ${luks_key} -- "$device" "$luks_name"; then + luks_succeeded=1 + else + print_warn "Invalid keyfile. Reverting to passphrase." + fi + fi + + # Ask for a passphrase + if [ "$luks_succeeded" -ne "1" ]; then + if [ -f "/usr/bin/plymouthd" ] && \ + [ -f "/usr/bin/plymouth" ] && \ + [ -z "$plymouth_nosplash" ] && \ + plymouth --ping 2>/dev/null; then + plymouth ask-for-password \ + --prompt="A password is required to access the ${luks_name} volume" \ + --command="cryptsetup open --key-file=- ${luks_discard} ${luks_header} -- $device $luks_name" + else + echo "" + echo "A password is required to access the ${luks_name} volume:" + + #loop until we get a real password + # https://shellcheck.net/wiki/SC2086 + # shellcheck disable=2086 + while ! eval cryptsetup open ${luks_discard} ${luks_header} -- "$device" "$luks_name"; do + sleep 2; + done + fi + fi + + unset luks_succeeded + + if [ -e "/dev/mapper/${luks_name}" ]; then + if [ "$luks_root" = "$root" ]; then + root="/dev/mapper/${luks_name}" + fi + else + panic "Password succeeded, but ${luks_name} creation failed, aborting..." + fi + + else + panic "Failed to open encryption mapping: The device ${device} is not a LUKS volume." + fi +fi diff --git a/test/Makefile b/test/Makefile index 3c3e23e..35cebc4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,7 +3,7 @@ .SUFFIXES: .SUFFIXES: .test -all: bare +all: bare luks .test: ./$< > $@.out 2>&1 diff --git a/test/luks.test b/test/luks.test new file mode 100755 index 0000000..9914af8 --- /dev/null +++ b/test/luks.test @@ -0,0 +1,133 @@ +#!/bin/sh + +# https://shellcheck.net/wiki/SC1091 +# shellcheck disable=1091 +. ../lib/common.sh + +cleanup() +{ + umount "${tmpdir}/root" || : + cryptsetup close "$name" || : + qemu-nbd -d "$nbd" || : + rm -rf "$tmpdir" +} + +set -ef +trap cleanup EXIT INT + +nbd=${NBD:-/dev/nbd2} +devmgr=${DEVMGR:-proc} +arch=${ARCH:-$(uname -m)} +kernel=${KERNEL:-$(uname -r)} +vmlinuz=${VMLINUZ:-"/boot/vmlinuz-${kernel}"} + +mkdir -p "${tmpdir:=${TMPDIR:-/tmp}/${0##*/}.$$}" + +name="luks$$" +root="${tmpdir}/root" +config="${tmpdir}/config" +image="${tmpdir}/root.qcow2" +initrd="${tmpdir}/initramfs-$(uname -r)" + +qemu-img create -f qcow2 "$image" 1G +qemu-nbd -c "$nbd" "$image" + +sleep 1 + +# o: Create MBR table. +# n: Add new partition to table. +# p: Primary partition. +# 1: Partition number. +# newline: Use default value for first sector. +# newline: Use default value for last sector. +# w: Write changes and re-read partition table. +fdisk "$nbd" << EOF +o +n +p +1 + + +w +EOF + +dd bs=512 count=1 if=/dev/urandom of="${tmpdir}/key" + +cryptsetup -qd "${tmpdir}/key" --pbkdf=pbkdf2 luksFormat "${nbd}p1" +cryptsetup -d "${tmpdir}/key" open "${nbd}p1" "$name" + +cat > "$config" << EOF +hooks=$devmgr,luks +root=LABEL=root +luks_root=UUID=$(cryptsetup luksUUID "${nbd}p1") +luks_key=${tmpdir}/key +EOF + +mkdir -p "$root" + +mkfs.ext4 -L root "/dev/mapper/${name}" +mount "/dev/mapper/${name}" "$root" + +( + _tmpdir=$root; cd "$_tmpdir" + + mkdir -p \ + dev sys tmp run proc \ + root usr/lib usr/bin + + ln -s lib usr/lib64 + ln -s usr/lib lib64 + ln -s usr/lib lib + ln -s usr/bin bin + ln -s usr/bin sbin + ln -s bin usr/sbin + + copy_exec sh + copy_exec e2label + + cat > sbin/init << EOF +#!/bin/sh +exec e2label /dev/disk/by-label/root success +EOF + + chmod +x sbin/init +) + +umount "$root" +cryptsetup close "$name" +qemu-nbd -d "$nbd" + +(cd .. && ./tinyramfs -lk "$kernel" -c "$config" "$initrd") + +set -- \ + -m 1G \ + -no-reboot \ + -initrd "$initrd" \ + -kernel "$vmlinuz" \ + -device virtio-scsi \ + -drive file="$image",if=virtio + +if [ -c /dev/kvm ]; then + set -- -enable-kvm -cpu host "$@" +fi + +if [ "$DEBUG" ]; then + set -- -append 'panic=-1 rdpanic debug rddebug console=ttyS0' -nographic "$@" +else + set -- -append 'panic=-1 rdpanic' -display none "$@" +fi + +"qemu-system-${arch}" "$@" + +qemu-nbd -c "$nbd" "$image" + +sleep 1 + +# Re-read partition table. +fdisk "$nbd" << EOF +w +EOF + +cryptsetup -d "${tmpdir}/key" open "${nbd}p1" "$name" + +[ "$(e2label "/dev/mapper/${name}")" = success ]