From 501d68bf7e5e02ee567848dfc5d7b0bf2bc741bb Mon Sep 17 00:00:00 2001 From: Anderson Rosenberg Date: Wed, 22 Oct 2025 14:34:13 -0400 Subject: [PATCH] reliant.profile and reliant.e2fsck commandline options; unseal and seal now mount and unmount instead of changing flags; displayed volume limit via reliant.dvl=VID --- README.md | 10 ++- dracut/99reliant/scripts/reliant-initramfs.sh | 6 ++ extra/shufflecake-close.sh | 15 ++++- qubes-sflc/shufflecake-c | 2 +- tools/reliant-mount | 66 ++++++++++++++++++- tools/reliant-seal | 8 ++- tools/reliant-unseal | 43 ++++++++---- 7 files changed, 130 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 341d23e..b3c908d 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,14 @@ action=accept dst4=1.1.1.1 dstports=80 proto=tcp ``` Due to potential security implications of arbitrary code execution (firewall.rules is not sanitized when a volume is unsealed), the user is asked for manual confirmation before the firewall configuration is executed. +### Commandline arguments + +- `systemd.volatile=overlay` controls the switch between Protected Mode (when present) and Maintenance Mode (when omitted) +- `reliant.e2fsck` runs `e2fsck -p` on every volume before mount to avoid needing an extra reboot in case of accidental data corruption +- `reliant.profile` mounts the upperdir and workdir of OverlayFS in an accessible location under /run/reliant for debugging purposes +- `reliant.dvl=VOLUME_ID` limits the amount of volumes displayed in Qubes app menu, manager, and other places, useful for when you want to avoid showing your hidden qubes in public + - Displayed volume limit does **NOT** provide plausible deniability or actual device inspection + ## Known issues - All known issues of [Shufflecake](https://shufflecake.net/#faq) and [QubesOS](https://www.qubes-os.org/faq). @@ -154,4 +162,4 @@ This seems to be an issue caused by Qube IDs above 10000 (the default limit). Th [^5]: In deniable systems, not only your password but the presence information (e.g. number of volumes in case of Shufflecake) is confidential. Side channels can and will leak information about your system. Make sure you keep your phone away from your workplace, preferably turned off unless you it has hardware switches for the camera and microphone. Your keystrokes can absolutely be recorded and used to infer data about your passwords and the number of volumes. For information about other potential side channel attacks you are encouraged to look up information on [TEMPEST](https://en.wikipedia.org/wiki/Tempest_%28codename%29) and [Van Eck phreaking](https://en.wikipedia.org/wiki/Van_Eck_phreaking) and relevant mitigation methods. Network access in deniable qubes is also a side channel which should be carefully worked around. -[^6]: 'Skipped-Mount Protected Mode' is not a normal operation mode and the system will be highly broken under such circumstances since `surgeon-suture` cannot run. Nevertheless, dom0 and keyboard should remain functional, and this is all you need - switch to a TTY by pressing Ctrl+Alt+F2, log in and run `shufflecake init $RELIANT_SECURE_DEVICE`, where `$RELIANT_SECURE_DEVICE` is your soon-to-be Shufflecake device. \ No newline at end of file +[^6]: 'Skipped-Mount Protected Mode' is not a normal operation mode and the system will be highly broken under such circumstances since `surgeon-suture` cannot run. Nevertheless, dom0 and keyboard should remain functional, and this is all you need - switch to a TTY by pressing Ctrl+Alt+F2, log in and run `shufflecake init $RELIANT_SECURE_DEVICE`, where `$RELIANT_SECURE_DEVICE` is your soon-to-be Shufflecake device. diff --git a/dracut/99reliant/scripts/reliant-initramfs.sh b/dracut/99reliant/scripts/reliant-initramfs.sh index e94afd0..94bcd1e 100755 --- a/dracut/99reliant/scripts/reliant-initramfs.sh +++ b/dracut/99reliant/scripts/reliant-initramfs.sh @@ -111,6 +111,12 @@ main() { # Run the surgeon script to fix qubes.xml surgeon-suture + + # Seal all volumes + for path in '/run/shufflecake/'*; do + name="${path##*/}" + reliant-seal "$name" + done } main diff --git a/extra/shufflecake-close.sh b/extra/shufflecake-close.sh index 658099b..21bcebe 100755 --- a/extra/shufflecake-close.sh +++ b/extra/shufflecake-close.sh @@ -8,11 +8,20 @@ if [ ! -d /run/shufflecake ]; then exit 0 fi +# Shut down everything first +qvm-shutdown --force --all --wait --timeout 10 || true + +# Detach loop devices +losetup -D || true + # Seal and close each Shufflecake volume for path in /run/shufflecake/*; do - volume="${path##*/}" - reliant-seal "$volume" - umount "$path" + volume="${path##*/}" || true + reliant-seal "$volume" || true + + # reliant-seal does unmount now, but leave this for good measure + # or if the user has old reliant-seal for some reason + umount "$path" || true done # Close the Shufflecake device diff --git a/qubes-sflc/shufflecake-c b/qubes-sflc/shufflecake-c index 96c1557..635fcbd 160000 --- a/qubes-sflc/shufflecake-c +++ b/qubes-sflc/shufflecake-c @@ -1 +1 @@ -Subproject commit 96c1557cdde6d92c299592cbfcd39af1e51b7c29 +Subproject commit 635fcbd228dcd28b748ccc47bee14dcfc7434dd2 diff --git a/tools/reliant-mount b/tools/reliant-mount index 3a35458..ffd0e43 100755 --- a/tools/reliant-mount +++ b/tools/reliant-mount @@ -33,21 +33,69 @@ while true; do fi done +# Check for the displayed volume limit in commandline arguments +# This is only useful when booting in public to hide deniable qubes +dvl_required=$RELIANT_FALSE +for argument in $(cat /proc/cmdline); do + if [[ "$argument" == reliant.dvl=* ]]; then + dvl_id="${argument##*/}" + dvl_required=$RELIANT_TRUE + fi +done + +# Set the displayed volume limit +if [ -n "$dvl_id" ]; then + dvl_device="/dev/mapper/sflc_0_$dvl_id" + + # Notify the user so they know the limit has been detected + if [ -b "$dvl_device" ]; then + plymouth display-message --text="Displayed volumes limited" + sleep 1 + plymouth hide-message --text="Displayed volumes limited" + else + while true; do + dvl_id=$(plymouth ask-question --prompt="Displayed volume limit is invalid. Provide a new one") + dvl_device="/dev/mapper/sflc_0_$dvl_id" + + # Validate user input + if [ -b $dvl_device ]; then + plymouth display-message --text="Limit adjusted successfully" + sleep 1 + plymouth hide-message --text="Limit adjusted successfully" + break + fi + done + fi +fi + # Create the volume root directory under /run mkdir -m 750 /run/shufflecake +# Check if we need to e2fsck +reliant_e2fsck=$(grep -q reliant.e2fsck /proc/cmdline) + # Mount each volume -find /dev/mapper -maxdepth 1 -name 'sflc_*' | while read -r device; do +find /dev/mapper -maxdepth 1 -name 'sflc_0_*' | while read -r device; do # IMPORTANT: Seal it blockdev --setro "$device" || exit 1 # Determine the name and mountpoint name="${device##*/}" mountpoint="/run/shufflecake/$name" + + # e2fsck if requested + if [ "$reliant_e2fsck" ]; then + e2fsck -p "$device" &> /dev/null || true # If the user has mixed filesystems, it will fail for some + fi # Mount in /run/shufflecake mkdir -m 750 "$mountpoint" - mount -o ro,noatime,nodiratime "$device" "$mountpoint" &> /dev/null || true # allow it to silently fail in case there's no filesystem or it is corrupted + mount -o ro,noatime,nodiratime "$device" "$mountpoint" &> /dev/null || true # Allow it to silently fail in case there's no filesystem or it is corrupted + + # Apply the displayed volume limit if required + if [ $dvl_required = $RELIANT_TRUE ] && [ "$device" = "$dvl_device" ]; then + break + fi done # Set up the volatile image pool @@ -57,3 +105,17 @@ for path in '/sysroot/var/lib/qubes/appvms/'*; do name="${path##*/}" mkdir -m 750 "/run/volatile/appvms/$name" done + +# Check if we need to set up profiling +if grep -q reliant.profile /proc/cmdline; then + mkdir -m 750 /run/reliant + mkdir -m 750 /run/reliant/profile + + # Upperdir + mkdir -m 750 /run/reliant/profile/upper + mount --bind /run/systemd/overlay-sysroot/upper /run/reliant/profile/upper + + # Workdir + mkdir -m 750 /run/reliant/profile/work + mount --bind /run/systemd/overlay-sysroot/work /run/reliant/profile/work +fi diff --git a/tools/reliant-seal b/tools/reliant-seal index 6124fe8..c5b4fc8 100755 --- a/tools/reliant-seal +++ b/tools/reliant-seal @@ -38,11 +38,15 @@ done echo "Done." echo -n "Sealing mountpoint... " -mount -o remount,ro,noatime,nodiratime /run/shufflecake/$name +mount -o remount,ro,noatime,nodiratime "/run/shufflecake/$name" echo "Done." echo -n "Sealing device... " -blockdev --setro /dev/mapper/$name +blockdev --setro "/dev/mapper/$name" +echo "Done." + +echo -n "Unmounting device... " +umount "/run/shufflecake/$name" && rmdir "/run/shufflecake/$name" echo "Done." echo -e "${GREEN}Sealed.${ENDCOLOR} See reliant-status for more information." diff --git a/tools/reliant-unseal b/tools/reliant-unseal index 1138f5f..3403788 100755 --- a/tools/reliant-unseal +++ b/tools/reliant-unseal @@ -5,8 +5,8 @@ RED="\e[31;1m" GREEN="\e[32;1m" ENDCOLOR="\e[0m" -if [ "$#" -ne 1 ]; then - echo "Expected 1 argument." +if [ "$#" -lt 1 ]; then + echo "Expected at least 1 argument." exit 1 fi @@ -21,23 +21,44 @@ if [ ! -d /run/shufflecake ]; then fi name=$1 +device="/dev/mapper/$name" echo -n "Unsealing device... " -blockdev --setrw /dev/mapper/$name +blockdev --setrw "$device" echo "Done." echo -n "Unsealing mountpoint... " -mount -o remount,rw,noatime,nodiratime /run/shufflecake/$name +mkdir "/run/shufflecake/$name" +mount -o rw,noatime,nodiratime "$device" "/run/shufflecake/$name" echo "Done." +# Check if we were given a qube list +allowed_qubes="${*:2}" + echo "Creating links... " -for appvm in /run/shufflecake/$name/appvms/*; do +for appvm in "/run/shufflecake/$name/appvms/"*; do qube="${appvm##*/}" + # Filter if user provided a list of qubes + if [ ! -z "$allowed_qubes" ]; then + allowed=0 + for allowed_qube in $allowed_qubes; do + if [ "$qube" = "$allowed_qube" ]; then + allowed=1 + break + fi + done + + # Only unseal explicitly requested qubes + if [ $allowed -ne 1 ]; then + continue + fi + fi + # Directory link directory="/var/lib/qubes/appvms/$qube" - if [ ! -d $directory ]; then - install -d -o root -g qubes -m 0750 $directory + if [ ! -d "$directory" ]; then + install -d -o root -g qubes -m 0750 "$directory" fi # App menus @@ -46,9 +67,9 @@ for appvm in /run/shufflecake/$name/appvms/*; do su user -c "qvm-shutdown $qube" &>/dev/null || true # Firewall rules - if [ -f $appvm/firewall.rules ]; then + if [ -f "$appvm/firewall.rules" ]; then echo "Found firewall.rules. Approve?" - cat $appvm/firewall.rules + cat "$appvm/firewall.rules" read -p "[Y/N]: " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then @@ -59,9 +80,9 @@ for appvm in /run/shufflecake/$name/appvms/*; do echo "Approved." su user -c "qvm-firewall $qube reset" su user -c "qvm-firewall $qube del --rule-no 0" - while read; do + while read -r; do su user -c "qvm-firewall $qube add $REPLY" - done < $appvm/firewall.rules + done < "$appvm/firewall.rules" su user -c "qvm-firewall $qube add action=drop" fi done