An Open Dialogue on Truly Disposible Qubes

I wanted to open this thread as there are many “solutions” I have tried (and broken Qubes with) but nothing seems to be really the right fix right now.

After trying many methods here I think the solution is probably:

Combining RAM-based DispVMs with dom0 live mode (from Whonix thread) using zram (with compression?) with ephemeral encryption on volatile volumes.

why? qubes run ephemerally in RAM, dom0 leaves no traces, and logs/metadata are nullified.
It’s superior to standard DispVMs (which persist logs) and secondary storage wipers (ineffective on SSDs due to wear-leveling).

So i am thinking to start with ZRAM setup with compression: (with maybe lz4?)

then

dom0 in live mode

with Ephemeral Thin-Pool Full (encrypted volatile/private)

I know there is the thread with @qubist (amazing work) that had alot of collaboration and I don’t want to thread hijack that one but just leave this open for more discussion.
Although that is a great solution I still think there is more dialogue needed.
I was also interested in what @rustybird commented

that there maybe a simpler, less messy solution that I am unaware of now.

1 Like

This is rough idea and wont work obviously but maybe something like a qubes-ephemeral manager?

#!/bin/bash

set -e

SCRIPT_VERSION="1.0.0"
SCRIPT_NAME="Qubes Ephemeral Volume Manager"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'

show_header() {
    clear
    echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
    echo -e "${CYAN}  ${SCRIPT_NAME} v${SCRIPT_VERSION}${NC}"
    echo -e "${CYAN}  Manage truly ephemeral Qubes volumes with RAM-only encryption${NC}"
    echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
    echo ""
}

show_info() {
    echo -e "${BLUE}ℹ${NC} $1"
}

show_success() {
    echo -e "${GREEN}✓${NC} $1"
}

show_warning() {
    echo -e "${YELLOW}⚠${NC} $1"
}

show_error() {
    echo -e "${RED}✗${NC} $1"
}

check_qubes_environment() {
    if ! command -v qvm-volume &> /dev/null; then
        show_error "This script must be run in Qubes OS dom0"
        show_error "qvm-volume command not found"
        exit 1
    fi
    
    if ! command -v qvm-pool &> /dev/null; then
        show_error "qvm-pool command not found"
        exit 1
    fi
}

check_volume_properties() {
    local vm_name="$1"
    local volume_name="$2"
    
    local snap_on_start=$(qvm-volume info "${vm_name}:${volume_name}" | grep "snap_on_start:" | awk '{print $2}')
    local save_on_stop=$(qvm-volume info "${vm_name}:${volume_name}" | grep "save_on_stop:" | awk '{print $2}')
    
    if [[ "$snap_on_start" != "False" ]] || [[ "$save_on_stop" != "False" ]]; then
        return 1
    fi
    return 0
}

display_volume_info() {
    local vm_name="$1"
    local volume_name="$2"
    
    echo ""
    echo -e "${CYAN}Volume Information: ${vm_name}:${volume_name}${NC}"
    echo "────────────────────────────────────────────────"
    
    qvm-volume info "${vm_name}:${volume_name}" 2>/dev/null | grep -E "(ephemeral|snap_on_start|save_on_stop|size|pool)" | while read line; do
        if echo "$line" | grep -q "ephemeral.*True"; then
            echo -e "${GREEN}${line}${NC}"
        elif echo "$line" | grep -q "ephemeral.*False"; then
            echo -e "${YELLOW}${line}${NC}"
        else
            echo "$line"
        fi
    done
    echo ""
}

list_all_vms() {
    echo -e "${CYAN}Available VMs:${NC}"
    echo "────────────────────────────────────────────────"
    qvm-ls --raw-list | sort
    echo ""
}

list_vm_volumes() {
    local vm_name="$1"
    
    if ! qvm-ls --raw-list | grep -q "^${vm_name}$"; then
        show_error "VM '${vm_name}' not found"
        return 1
    fi
    
    echo ""
    echo -e "${CYAN}Volumes for VM: ${vm_name}${NC}"
    echo "────────────────────────────────────────────────"
    qvm-volume list "$vm_name" 2>/dev/null
    echo ""
}

show_vm_ephemeral_status() {
    local vm_name="$1"
    
    if ! qvm-ls --raw-list | grep -q "^${vm_name}$"; then
        show_error "VM '${vm_name}' not found"
        return 1
    fi
    
    echo ""
    echo -e "${CYAN}Ephemeral Status for: ${vm_name}${NC}"
    echo "════════════════════════════════════════════════"
    
    for volume in root private volatile; do
        if qvm-volume info "${vm_name}:${volume}" &>/dev/null; then
            local ephemeral=$(qvm-volume info "${vm_name}:${volume}" 2>/dev/null | grep "ephemeral:" | awk '{print $2}')
            local snap_on_start=$(qvm-volume info "${vm_name}:${volume}" 2>/dev/null | grep "snap_on_start:" | awk '{print $2}')
            local save_on_stop=$(qvm-volume info "${vm_name}:${volume}" 2>/dev/null | grep "save_on_stop:" | awk '{print $2}')
            
            printf "%-12s " "$volume:"
            
            if [[ "$ephemeral" == "True" ]]; then
                echo -e "${GREEN}✓ EPHEMERAL ENABLED${NC} (RAM-only encryption)"
            elif [[ "$snap_on_start" == "False" ]] && [[ "$save_on_stop" == "False" ]]; then
                echo -e "${YELLOW}○ Eligible for ephemeral${NC} (snap_on_start=False, save_on_stop=False)"
            else
                echo -e "  Not eligible (snap_on_start=$snap_on_start, save_on_stop=$save_on_stop)"
            fi
        fi
    done
    echo ""
}

show_pool_info() {
    local pool_name="$1"
    
    if ! qvm-pool list | grep -q "^${pool_name} "; then
        show_error "Pool '${pool_name}' not found"
        return 1
    fi
    
    echo ""
    echo -e "${CYAN}Pool Information: ${pool_name}${NC}"
    echo "════════════════════════════════════════════════"
    qvm-pool info "$pool_name" 2>/dev/null | grep -E "(ephemeral_volatile|driver|dir_path|usage|size)" | while read line; do
        if echo "$line" | grep -q "ephemeral_volatile.*True"; then
            echo -e "${GREEN}${line}${NC}"
        elif echo "$line" | grep -q "ephemeral_volatile.*False"; then
            echo -e "${YELLOW}${line}${NC}"
        else
            echo "$line"
        fi
    done
    echo ""
}

list_pools() {
    echo ""
    echo -e "${CYAN}Available Storage Pools:${NC}"
    echo "════════════════════════════════════════════════"
    qvm-pool list
    echo ""
}

enable_volume_ephemeral() {
    local vm_name="$1"
    local volume_name="$2"
    
    if ! qvm-ls --raw-list | grep -q "^${vm_name}$"; then
        show_error "VM '${vm_name}' not found"
        return 1
    fi
    
    if ! qvm-volume info "${vm_name}:${volume_name}" &>/dev/null; then
        show_error "Volume '${volume_name}' not found for VM '${vm_name}'"
        return 1
    fi
    
    if ! check_volume_properties "$vm_name" "$volume_name"; then
        show_error "Volume ${vm_name}:${volume_name} is not eligible for ephemeral encryption"
        show_warning "Ephemeral encryption requires snap_on_start=False AND save_on_stop=False"
        show_info "Only volatile volumes typically meet these requirements"
        return 1
    fi
    
    show_info "Enabling ephemeral encryption for ${vm_name}:${volume_name}..."
    
    if qvm-volume set "${vm_name}:${volume_name}" ephemeral True 2>/dev/null; then
        show_success "Ephemeral encryption enabled successfully!"
        show_info "Volume will be encrypted with a RAM-only key on next VM start"
        display_volume_info "$vm_name" "$volume_name"
        return 0
    else
        show_error "Failed to enable ephemeral encryption"
        return 1
    fi
}

disable_volume_ephemeral() {
    local vm_name="$1"
    local volume_name="$2"
    
    if ! qvm-ls --raw-list | grep -q "^${vm_name}$"; then
        show_error "VM '${vm_name}' not found"
        return 1
    fi
    
    if ! qvm-volume info "${vm_name}:${volume_name}" &>/dev/null; then
        show_error "Volume '${volume_name}' not found for VM '${vm_name}'"
        return 1
    fi
    
    show_info "Disabling ephemeral encryption for ${vm_name}:${volume_name}..."
    
    if qvm-volume set "${vm_name}:${volume_name}" ephemeral False 2>/dev/null; then
        show_success "Ephemeral encryption disabled successfully!"
        display_volume_info "$vm_name" "$volume_name"
        return 0
    else
        show_error "Failed to disable ephemeral encryption"
        return 1
    fi
}

enable_pool_ephemeral() {
    local pool_name="$1"
    
    if ! qvm-pool list | grep -q "^${pool_name} "; then
        show_error "Pool '${pool_name}' not found"
        return 1
    fi
    
    show_info "Enabling ephemeral_volatile for pool: ${pool_name}..."
    show_warning "This will affect all DispVM volatile volumes using this pool"
    
    if qvm-pool set "$pool_name" -o ephemeral_volatile=True 2>/dev/null; then
        show_success "Pool ephemeral_volatile enabled successfully!"
        show_info "All new DispVMs using this pool will have ephemeral volatile volumes"
        show_pool_info "$pool_name"
        return 0
    else
        show_error "Failed to enable pool ephemeral_volatile"
        return 1
    fi
}

disable_pool_ephemeral() {
    local pool_name="$1"
    
    if ! qvm-pool list | grep -q "^${pool_name} "; then
        show_error "Pool '${pool_name}' not found"
        return 1
    fi
    
    show_info "Disabling ephemeral_volatile for pool: ${pool_name}..."
    
    if qvm-pool set "$pool_name" -o ephemeral_volatile=False 2>/dev/null; then
        show_success "Pool ephemeral_volatile disabled successfully!"
        show_pool_info "$pool_name"
        return 0
    else
        show_error "Failed to disable pool ephemeral_volatile"
        return 1
    fi
}

scan_ephemeral_volumes() {
    echo ""
    echo -e "${CYAN}Scanning All VMs for Ephemeral Volumes...${NC}"
    echo "════════════════════════════════════════════════"
    
    local found_ephemeral=false
    
    while IFS= read -r vm; do
        for volume in root private volatile; do
            if qvm-volume info "${vm}:${volume}" &>/dev/null; then
                local ephemeral=$(qvm-volume info "${vm}:${volume}" 2>/dev/null | grep "ephemeral:" | awk '{print $2}')
                if [[ "$ephemeral" == "True" ]]; then
                    echo -e "${GREEN}✓${NC} ${vm}:${volume} - EPHEMERAL ENABLED"
                    found_ephemeral=true
                fi
            fi
        done
    done < <(qvm-ls --raw-list)
    
    if ! $found_ephemeral; then
        show_info "No volumes with ephemeral encryption found"
    fi
    
    echo ""
    echo -e "${CYAN}Storage Pool Ephemeral Settings:${NC}"
    echo "────────────────────────────────────────────────"
    
    while IFS= read -r line; do
        local pool_name=$(echo "$line" | awk '{print $1}')
        if qvm-pool info "$pool_name" 2>/dev/null | grep -q "ephemeral_volatile.*True"; then
            echo -e "${GREEN}✓${NC} $pool_name - ephemeral_volatile ENABLED"
        fi
    done < <(qvm-pool list | tail -n +2)
    
    echo ""
}

interactive_menu() {
    while true; do
        show_header
        echo -e "${CYAN}Main Menu:${NC}"
        echo "  1) View VM ephemeral status"
        echo "  2) Enable ephemeral encryption on volume"
        echo "  3) Disable ephemeral encryption on volume"
        echo "  4) Enable pool-wide ephemeral for DispVMs"
        echo "  5) Disable pool-wide ephemeral for DispVMs"
        echo "  6) Scan all ephemeral volumes"
        echo "  7) List all VMs"
        echo "  8) List storage pools"
        echo "  9) Show detailed volume information"
        echo "  h) Help and information"
        echo "  q) Quit"
        echo ""
        read -p "Select option: " choice
        
        case $choice in
            1)
                echo ""
                read -p "Enter VM name: " vm_name
                show_vm_ephemeral_status "$vm_name"
                read -p "Press Enter to continue..."
                ;;
            2)
                echo ""
                read -p "Enter VM name: " vm_name
                read -p "Enter volume name (usually 'volatile'): " volume_name
                enable_volume_ephemeral "$vm_name" "$volume_name"
                read -p "Press Enter to continue..."
                ;;
            3)
                echo ""
                read -p "Enter VM name: " vm_name
                read -p "Enter volume name: " volume_name
                disable_volume_ephemeral "$vm_name" "$volume_name"
                read -p "Press Enter to continue..."
                ;;
            4)
                echo ""
                list_pools
                read -p "Enter pool name (usually 'vm-pool'): " pool_name
                enable_pool_ephemeral "$pool_name"
                read -p "Press Enter to continue..."
                ;;
            5)
                echo ""
                list_pools
                read -p "Enter pool name: " pool_name
                disable_pool_ephemeral "$pool_name"
                read -p "Press Enter to continue..."
                ;;
            6)
                scan_ephemeral_volumes
                read -p "Press Enter to continue..."
                ;;
            7)
                list_all_vms
                read -p "Press Enter to continue..."
                ;;
            8)
                list_pools
                read -p "Press Enter to continue..."
                ;;
            9)
                echo ""
                read -p "Enter VM name: " vm_name
                read -p "Enter volume name: " volume_name
                display_volume_info "$vm_name" "$volume_name"
                read -p "Press Enter to continue..."
                ;;
            h|H)
                show_help
                read -p "Press Enter to continue..."
                ;;
            q|Q)
                echo ""
                show_info "Exiting..."
                exit 0
                ;;
            *)
                show_error "Invalid option"
                sleep 1
                ;;
        esac
    done
}

show_help() {
    show_header
    echo -e "${CYAN}What are Ephemeral Volumes?${NC}"
    echo "────────────────────────────────────────────────"
    echo "Ephemeral volumes are encrypted with a temporary key stored"
    echo "only in RAM. When the system shuts down, the key is lost,"
    echo "making the data unrecoverable."
    echo ""
    echo -e "${CYAN}Security Benefits:${NC}"
    echo "  • Enhanced anti-forensics protection"
    echo "  • Protection against cold-boot attacks"
    echo "  • Data is truly ephemeral and cannot be recovered"
    echo "  • Useful for DispVMs and sensitive operations"
    echo ""
    echo -e "${CYAN}Requirements:${NC}"
    echo "  • Only works on volatile volumes"
    echo "  • Volume must have snap_on_start=False"
    echo "  • Volume must have save_on_stop=False"
    echo ""
    echo -e "${CYAN}Usage Examples:${NC}"
    echo "  ${GREEN}Per-VM Configuration:${NC}"
    echo "    qvm-volume set my-vm:volatile ephemeral True"
    echo ""
    echo "  ${GREEN}Pool-Wide for DispVMs:${NC}"
    echo "    qvm-pool set vm-pool -o ephemeral_volatile=True"
    echo ""
    echo -e "${CYAN}Performance Note:${NC}"
    echo "  Ephemeral encryption adds an extra encryption layer,"
    echo "  which may slightly degrade performance."
    echo ""
}

show_usage() {
    echo "Usage: $0 [OPTIONS]"
    echo ""
    echo "Options:"
    echo "  -i, --interactive          Launch interactive menu (default)"
    echo "  -s, --scan                 Scan and display all ephemeral volumes"
    echo "  -e, --enable VM:VOLUME     Enable ephemeral encryption on volume"
    echo "  -d, --disable VM:VOLUME    Disable ephemeral encryption on volume"
    echo "  -p, --pool-enable POOL     Enable ephemeral_volatile on pool"
    echo "  -P, --pool-disable POOL    Disable ephemeral_volatile on pool"
    echo "  -v, --view VM              View VM ephemeral status"
    echo "  -h, --help                 Show this help message"
    echo ""
    echo "Examples:"
    echo "  $0 --scan"
    echo "  $0 --enable my-vm:volatile"
    echo "  $0 --pool-enable vm-pool"
    echo "  $0 --view my-dispvm"
    echo ""
}

main() {
    check_qubes_environment
    
    if [[ $# -eq 0 ]]; then
        interactive_menu
        exit 0
    fi
    
    case "$1" in
        -i|--interactive)
            interactive_menu
            ;;
        -s|--scan)
            show_header
            scan_ephemeral_volumes
            ;;
        -e|--enable)
            if [[ -z "$2" ]] || [[ ! "$2" =~ ^[^:]+:[^:]+$ ]]; then
                show_error "Invalid format. Use: VM:VOLUME"
                exit 1
            fi
            show_header
            IFS=':' read -r vm_name volume_name <<< "$2"
            enable_volume_ephemeral "$vm_name" "$volume_name"
            ;;
        -d|--disable)
            if [[ -z "$2" ]] || [[ ! "$2" =~ ^[^:]+:[^:]+$ ]]; then
                show_error "Invalid format. Use: VM:VOLUME"
                exit 1
            fi
            show_header
            IFS=':' read -r vm_name volume_name <<< "$2"
            disable_volume_ephemeral "$vm_name" "$volume_name"
            ;;
        -p|--pool-enable)
            if [[ -z "$2" ]]; then
                show_error "Pool name required"
                exit 1
            fi
            show_header
            enable_pool_ephemeral "$2"
            ;;
        -P|--pool-disable)
            if [[ -z "$2" ]]; then
                show_error "Pool name required"
                exit 1
            fi
            show_header
            disable_pool_ephemeral "$2"
            ;;
        -v|--view)
            if [[ -z "$2" ]]; then
                show_error "VM name required"
                exit 1
            fi
            show_header
            show_vm_ephemeral_status "$2"
            ;;
        -h|--help)
            show_header
            show_usage
            echo ""
            show_help
            ;;
        *)
            show_error "Unknown option: $1"
            show_usage
            exit 1
            ;;
    esac
}

main "$@"

@James369

If you make dom0’s journal volatile (or disable it completely) and use RAM-based disposables, I think that this can be considered truly disposable (though it can’t save one from karmic consequences).

ZRAM/compression and similar gym seems to me a CPU overhead and additional complexity.

2 Likes

Why combined with Zram / Compression? for those of us with not so much RAM, running Dom0 in RAM and increasing its RAM usage is not really feasible without a more efficient RAM utilization / swap / compression. Encrypted RAM also seems necessary.
Yes I agree it could lead to more CPU overhead I will try it. Ultimately it would be great to have the choice (as we do now with normal DispVMs), of which VM’s / disposables we would like to run in RAM.
Ideally I would like to be able to run dom0 as usual to make changes etc, and run / update other qubes and at the same time be able run a few qubes that are truly disposable in RAM.
Which is why I thought a ephemeral manager would be good. To be able to start, then wipe dispVM’s and also to better manage DispVM’s footprints.
I have other ongoing tasks and rarely reboot.
Having to go through the whole dom0 live process just run a truly DispVM is most unfortunately not really a solution to me. ( I also don’t have that much RAM).

I am not sure I understand.

  1. What is your actual goal with a VM being truly disposable?
  2. What do you mean by:

running Dom0 in RAM

Why is it necessary and how is it related to having truly disposable domUs? (which IIUC is your desire)

Encrypted RAM also seems necessary.

For what?

My opinion:

  1. Avoid charges for war crimes in the International Criminal Court and for plotting coup when devices was forensically examined.

  2. I agree.

  3. Cold boot attack

https://www.forensicfocus.com/forums/general/freezing-ram-to-retain-data/

Sorry if I wasn’t clear.

The goal is the same with the current Kicksecure live mode.
Anti-forensics as well as leaving minimal traces on Dom0 of activities.

(as you can see when running your cleanup script, records of every dispVM etc…)

The current solutions as far as I can see here are:

Running dom0 in RAM:
Qubes dom0 ZRAM Live Mode

Qubes dom0 OverlayFS Live Mode

and your script:
Really disposable (RAM based) qubes

The advantages of dom0 in live mode seems to be better than deleting logs with a script as the latter can be quite easily found even after deleting.

The first solution requires copying the entire dom0 to RAM at boot, via a grub change.
Perhaps just mounting some directories in RAM rather than copying the whole dom0 ie:

[zram0]
zram-size = 8G
compression-algorithm = zstd
mount-point = /var

[zram1]
zram-size = 2G
compression-algorithm = zstd
mount-point = /tmp

Encrypted RAM not only for cold-boot attacks, side-chain etc…

@James369

Consider this explanation:

and the man page of journald.conf:

       Storage=
           Controls where to store journal data. One of "volatile",
           "persistent", "auto" and "none". If "volatile", journal log data
           will be stored only in memory, i.e. below the /run/log/journal
           hierarchy (which is created if needed).

Could you please explain what forensic traces in dom0 you are concerned with?

Encrypted RAM not only for cold-boot attacks, side-chain etc…

The ephemeral keys used by qvm-pool protect the pool (the “disk” of the VM) but not its memory. AFAIK, effective RAM encryption requires kernel-level support and/or proper hardware.

The only Xen-related thing I found is this:

https://lists.xenproject.org/archives/html/xen-devel/2017-07/msg01534.html

There is also:

Perhaps you know all that.

1. Isn’t implementing rules for BleachBit shred in dom0 an easier solution?

The challange is the in-system data sanitization (not the whole disk). Can the DISCARD command (fstrim) also mark Logical Block Addressing as unused without the inconvenience of deleting all data?



2. Regarding memory encryption, something that protects against memory corruption vulnerabilities may already be a step forward in Qubes:

Hardened Malloc

Recommended reading for anyone chasing disposability for hiding whatever: