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

This commit is contained in:
Anderson Rosenberg 2025-10-22 14:34:13 -04:00
parent 8a3ae37678
commit 501d68bf7e
No known key found for this signature in database
GPG key ID: 7ACF448C0590AB9C
7 changed files with 130 additions and 20 deletions

View file

@ -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).

View file

@ -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

View file

@ -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

@ -1 +1 @@
Subproject commit 96c1557cdde6d92c299592cbfcd39af1e51b7c29
Subproject commit 635fcbd228dcd28b748ccc47bee14dcfc7434dd2

View file

@ -33,11 +33,49 @@ 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
@ -45,9 +83,19 @@ find /dev/mapper -maxdepth 1 -name 'sflc_*' | while read -r device; do
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

View file

@ -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."

View file

@ -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