#非持久化dom0
###参考该帖子forum post的方案一的方法
该方案优点:
1.不额外消耗内存 dom0修改本身几乎不占用多少硬盘
2.所有对dom0的修改都会放到一个临时加密overlay中,关机时清除ram中密钥 保证无法解密
实现dom0的非持久化
缺点:
1.
该模式仅仅针对dom0 非持久化
对其他各种vm没有非持久化(添加 删除vm 在appvm里编辑文件等 重启后不会恢复原状态)
appvm非持久化参考第二部分
2.安装后会将部分目录如/boot只读 更新dom0内核相关包前 先删除该功能(remove参数)
使用ai编写成完整的部署脚本 测试后 运行良好(
请根据自己需求修改部分参数 如果你没有将vm clone到varlibqubes pool运行 使该appvm也非持久化的化 dom0 overlay的大小不需要太大1G都够用)
使用前提:qubes os安装时使用lvm/lvm简单配置进行安装
在dom0 xfce4-terminal 使用
sudo lvs
sudo vgs
查看有无输出判断是否是该安装方式
使用方法 传到dom0终端运行:
sudo ./xx.sh install
sudo ./xx.sh remove
###部署脚本如下
#!/bin/bash
# ============================================================
# Qubes OS dom0 Live Mode - 加密覆盖层无痕模式
# ============================================================
# 功能:为dom0提供无痕启动模式,所有运行时修改写入LUKS2加密容器,
# 关机后数据自动销毁,防止取证分析。
#
# 【原理说明】
# 1. 将原系统根分区以只读方式挂载为lowerdir
# 2. 在/var/lib/创建LUKS2加密的稀疏容器文件作为upperdir
# 3. 使用OverlayFS将两者合并,所有写操作重定向到加密层
# 4. 关机时自动销毁加密密钥,容器数据不可读
#
# 【使用限制】
# - 系统必须使用LVM分区方案,卷组名为'qubes_dom0'
# - 根分区需有足够空闲空间存放加密容器
# - dom0更新需在正常持久模式下进行
# - 本脚本已适配 Qubes OS 4.2+(使用独立的 root-pool 和 vm-pool)
# ============================================================
# ============================================================
# 【可修改的配置参数】- 请根据实际情况调整
# ============================================================
# --- 加密容器大小 (单位: MB) ---
# 用途:定义存放dom0运行时修改的LUKS2加密容器大小(稀疏文件)
# 影响:设置过大会占用过多硬盘空间;设置过小可能导致运行时空间耗尽
# 建议值:普通使用 20480 (20GB);轻量使用 10240 (10GB)
# 可修改范围:5120 - 51200 (5GB - 50GB)
OVERLAY_CONTAINER_SIZE_MB="5000"
# --- dom0根逻辑卷目标大小 (单位: GB) ---
# 用途:确保 / 根逻辑卷的虚拟大小足够容纳加密容器
# 影响:如果当前根逻辑卷太小,容器可能无法创建
# 建议值:比容器大小至少大10GB(如容器20GB,则此值设为30GB)
# 可修改范围:大于容器大小+5GB
TARGET_ROOT_LV_SIZE_GB="40"
# --- Thin Pool 扩容触发阈值 (百分比) ---
# 用途:当 root-pool 的 Data% 超过此值时,尝试自动扩容
# 影响:阈值太低可能导致频繁扩容,太高可能来不及扩容
# 建议值:80 (即 Data% > 80% 时触发)
# 可修改范围:60 - 90
POOL_EXTEND_THRESHOLD_PERCENT="80"
# --- Thin Pool 每次扩容的增量 (单位: GB) ---
# 用途:当需要扩容时,给 pool 增加多少空间
# 影响:增量太小可能无法满足需求,太大可能浪费空间
# 建议值:20 (每次扩展20GB)
# 可修改范围:10 - 50
POOL_EXTEND_STEP_GB="20"
# --- dom0最大内存限制 (单位: MB) ---
# 用途:限制dom0可使用的最大内存量
# 影响:设置过低可能影响dom0性能;过高可能挤占其他VM内存
# 建议值:系统总内存的80%(自动计算),或手动指定
# 可修改范围:4096 - 系统总内存
# 特殊值:设为 "auto" 则自动使用总内存的80%
DOM0_MAX_MEM="4000"
# --- 是否启用RAM-Wipe ---
# 用途:关机时覆写内存,防止冷启动攻击
# 建议值:true(启用)
# 可修改值:true / false
ENABLE_RAM_WIPE="true"
# --- 是否启用内核加固 ---
# 用途:应用sysctl安全加固参数
# 建议值:true(启用)
# 可修改值:true / false
ENABLE_HARDENING="true"
# --- GRUB启动项名称 ---
# 用途:自定义GRUB菜单中显示的启动项名称
# 建议值:保持默认
# 可修改值:任意字符串
GRUB_MENU_TITLE="Qubes Encrypted-Overlay Amnesic Mode"
# ============================================================
set -euo pipefail
# --- 颜色输出 ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
print_info() { echo -e "${GREEN}[INFO]${NC} $*"; }
print_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
print_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $*"; }
check_root() {
if [[ $EUID -ne 0 ]]; then
print_error "This script must be run as root (sudo)."
fi
}
# --- 获取系统信息 ---
get_boot_uuid() {
local uuid
uuid=$(findmnt -n -o UUID /boot 2>/dev/null)
if [[ -z "$uuid" ]]; then
uuid=$(blkid -s UUID -o value "$(findmnt -n -o SOURCE /boot 2>/dev/null)" 2>/dev/null)
fi
echo "$uuid"
}
get_luks_uuid() {
local device uuid
device=$(blkid -t TYPE="crypto_LUKS" -o device 2>/dev/null | head -n1)
if [[ -n "$device" ]]; then
uuid=$(cryptsetup luksUUID "$device" 2>/dev/null)
fi
echo "$uuid"
}
get_latest_xen() {
ls /boot/xen*.gz 2>/dev/null | sort -V | tail -1 | xargs basename 2>/dev/null || echo "/xen-4.19.4.gz"
}
get_latest_kernel_initrd() {
local kernel initrd
kernel=$(ls /boot/vmlinuz-*qubes*.x86_64 2>/dev/null | grep -E 'qubes\.fc[0-9]+' | sort -V | tail -1 | xargs basename)
initrd="/initramfs-${kernel#vmlinuz-}.img"
echo "$kernel|$initrd"
}
# --- 存储相关辅助函数 ---
# 获取存放 / 根文件系统的 Thin Pool 名称
get_root_pool() {
# 方法:从 /dev/mapper/qubes_dom0-root 的 thin pool 属性获取
local pool
pool=$(lvs -o pool_lv --noheadings --nosuffix qubes_dom0/root 2>/dev/null | tr -d ' ')
if [[ -z "$pool" ]]; then
# 尝试查找带有 "pool" 且与 root 关联的池
pool=$(lvs -o lv_name,pool_lv --noheadings --nosuffix qubes_dom0 | grep -E "\s+root$" | awk '{print $2}' | tr -d ' ')
fi
if [[ -z "$pool" ]]; then
# 如果还没有,可能命名为 pool00
pool="pool00"
# 验证是否存在
if ! lvs qubes_dom0/"$pool" &>/dev/null; then
print_error "Cannot determine root thin pool. Please check LVM layout."
fi
fi
echo "$pool"
}
# 获取根逻辑卷的当前大小(GB)
get_root_lv_size_gb() {
lvs -o lv_size --noheadings --nosuffix --units g qubes_dom0/root 2>/dev/null | tr -d ' ' | sed 's/g//' | awk '{print int($1)}'
}
# 获取 Thin Pool 的 Data% (整数)
get_pool_data_percent() {
local pool="$1"
lvs -o data_percent --noheadings --nosuffix qubes_dom0/"$pool" 2>/dev/null | tr -d ' ' | sed 's/\..*//'
}
# 获取 Thin Pool 的当前大小(GB)
get_pool_size_gb() {
local pool="$1"
lvs -o lv_size --noheadings --nosuffix --units g qubes_dom0/"$pool" 2>/dev/null | tr -d ' ' | sed 's/g//' | awk '{print int($1)}'
}
# 获取卷组空闲空间(GB)
get_vg_free_gb() {
vgs -o vg_free --noheadings --nosuffix --units g qubes_dom0 2>/dev/null | tr -d ' ' | sed 's/g//' | awk '{print int($1)}'
}
# 扩容 root-pool(如果 Data% 高于阈值且有足够空闲空间)
extend_pool_if_needed() {
local pool="$1"
local current_data_percent
current_data_percent=$(get_pool_data_percent "$pool")
if [[ -z "$current_data_percent" ]]; then
print_warn "Unable to get Data% for pool $pool. Skipping pool extension."
return 1
fi
if [[ "$current_data_percent" -lt "$POOL_EXTEND_THRESHOLD_PERCENT" ]]; then
print_info "Pool $pool Data% = ${current_data_percent}% (threshold: ${POOL_EXTEND_THRESHOLD_PERCENT}%) - no need to extend."
return 0
fi
print_warn "Pool $pool Data% = ${current_data_percent}% exceeds threshold ${POOL_EXTEND_THRESHOLD_PERCENT}%."
local vg_free
vg_free=$(get_vg_free_gb)
if [[ "$vg_free" -lt "$POOL_EXTEND_STEP_GB" ]]; then
print_error "Insufficient free space in VG (only ${vg_free}GB) to extend pool by ${POOL_EXTEND_STEP_GB}GB. Aborting."
fi
local current_pool_size
current_pool_size=$(get_pool_size_gb "$pool")
local new_pool_size=$((current_pool_size + POOL_EXTEND_STEP_GB))
print_info "Extending pool $pool from ${current_pool_size}GB to ${new_pool_size}GB..."
if lvextend -L "${new_pool_size}G" qubes_dom0/"$pool"; then
print_success "Pool $pool extended successfully."
else
print_error "Failed to extend pool $pool."
fi
}
# 确保根逻辑卷的大小足够容纳加密容器
extend_root_lv_if_needed() {
local container_size_gb=$(( (OVERLAY_CONTAINER_SIZE_MB + 1023) / 1024 )) # 向上取整
local required_root_size=$((container_size_gb + 5)) # 额外5GB余量
if [[ "$required_root_size" -lt "$TARGET_ROOT_LV_SIZE_GB" ]]; then
required_root_size="$TARGET_ROOT_LV_SIZE_GB"
fi
local current_root_size
current_root_size=$(get_root_lv_size_gb)
if [[ -z "$current_root_size" ]]; then
print_error "Cannot determine current root LV size."
fi
if [[ "$current_root_size" -ge "$required_root_size" ]]; then
print_info "Root LV size ${current_root_size}GB is sufficient (need ${required_root_size}GB)."
return 0
fi
print_warn "Root LV size ${current_root_size}GB is less than required ${required_root_size}GB. Extending..."
if lvextend -L "${required_root_size}G" qubes_dom0/root; then
print_success "Root LV extended to ${required_root_size}GB."
# 扩展文件系统
if resize2fs /dev/mapper/qubes_dom0-root 2>/dev/null; then
print_info "File system resized successfully."
else
print_warn "File system resize failed. You may need to run 'resize2fs /dev/mapper/qubes_dom0-root' manually."
fi
else
print_error "Failed to extend root LV."
fi
}
# --- 主要的存储空间检查(在安装时调用)---
ensure_storage_space() {
print_info "Checking storage space for overlay container..."
local pool
pool=$(get_root_pool)
print_info "Root thin pool detected: $pool"
# 1. 检查并扩展 pool 如果过满
extend_pool_if_needed "$pool"
# 2. 确保根逻辑卷大小足够
extend_root_lv_if_needed
print_success "Storage space check completed."
}
# --- 安装功能 ---
install_mode() {
print_info "Starting installation of Qubes OS Ephemeral Overlay Mode..."
# 收集系统信息
BOOT_UUID=$(get_boot_uuid)
LUKS_UUID=$(get_luks_uuid)
XEN_PATH=$(get_latest_xen)
KERNEL_INITRD=$(get_latest_kernel_initrd)
LATEST_KERNEL="${KERNEL_INITRD%|*}"
LATEST_INITRAMFS="${KERNEL_INITRD#*|}"
USER_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6 2>/dev/null || echo "$HOME")
# 计算dom0内存限制
if [[ "$DOM0_MAX_MEM" == "auto" ]]; then
system_total_mb=$(xl info 2>/dev/null | grep total_memory | awk '{print $3}' || echo "0")
if [[ -n "$system_total_mb" ]] && [[ "$system_total_mb" -gt 0 ]]; then
DOM0_MAX_MB=$((system_total_mb * 80 / 100))
else
DOM0_MAX_MB=10240
fi
else
DOM0_MAX_MB=$DOM0_MAX_MEM
fi
DOM0_MAX_RAM="dom0_mem=max:${DOM0_MAX_MB}M"
print_info "Detected system parameters:"
echo " BOOT_UUID: $BOOT_UUID"
echo " LUKS_UUID: $LUKS_UUID"
echo " XEN: $XEN_PATH"
echo " Kernel: $LATEST_KERNEL"
echo " Initramfs: $LATEST_INITRAMFS"
echo " Dom0 Max Memory: ${DOM0_MAX_MB}M"
# 1. 确保存储空间足够
ensure_storage_space
# 2. 禁用swap
print_info "Disabling swap in /etc/fstab..."
if grep -q '^[^#].*swap' /etc/fstab; then
sed -i.bak '/swap/s/^/# /' /etc/fstab
swapoff -a 2>/dev/null || true
fi
# 3. 创建Dracut模块: 90overlay-crypt
print_info "Creating Dracut module: 90overlay-crypt..."
local MODULE_DIR="/usr/lib/dracut/modules.d/90overlay-crypt"
mkdir -p "$MODULE_DIR"
cat > "$MODULE_DIR/module-setup.sh" << 'EOF'
#!/bin/bash
check() {
require_binaries cryptsetup || return 1
require_binaries losetup || return 1
require_binaries mkfs.ext4 || return 1
return 0
}
depends() { return 0; }
installkernel() {
hostonly='' instmods overlay 2>/dev/null || true
hostonly='' instmods dm-crypt 2>/dev/null || true
}
install() {
inst_multiple cryptsetup losetup mkfs.ext4 dd modprobe mount umount shred
inst_hook pre-pivot 10 "$moddir/overlay-crypt.sh"
}
EOF
chmod 755 "$MODULE_DIR/module-setup.sh"
cat > "$MODULE_DIR/overlay-crypt.sh" << EOF
#!/bin/bash
. /lib/dracut-lib.sh
if ! getargbool 0 cryptovl ; then
return
fi
modprobe overlay 2>/dev/null || true
modprobe dm-crypt 2>/dev/null || true
mount -o remount,ro /sysroot 2>/dev/null || true
mkdir -p /live/image
mount --bind /sysroot /live/image
umount /sysroot
dd if=/dev/urandom bs=64 count=1 of=/dev/shm/overlay-key status=none
chmod 600 /dev/shm/overlay-key
mkdir -p /var/lib
dd if=/dev/zero of=/var/lib/overlay-crypt.img bs=1M count=0 seek=$OVERLAY_CONTAINER_SIZE_MB status=none
LOOP_DEV=\$(losetup -f --show /var/lib/overlay-crypt.img)
cryptsetup luksFormat --type luks2 \\
--cipher aes-xts-plain64 --key-size 512 \\
--hash sha256 --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \\
--batch-mode --key-file /dev/shm/overlay-key "\$LOOP_DEV"
cryptsetup open --type luks2 --key-file /dev/shm/overlay-key "\$LOOP_DEV" overlaycrypt
mkfs.ext4 -F -L "overlaycrypt" /dev/mapper/overlaycrypt
mkdir -p /cow
mount -o noatime,nodiratime,nobarrier /dev/mapper/overlaycrypt /cow
mkdir -p /cow/work /cow/rw
mount -t overlay -o noatime,nodiratime,volatile,lowerdir=/live/image,upperdir=/cow/rw,workdir=/cow/work,default_permissions,relatime overlay /sysroot
mkdir -p /sysroot/live/cow /sysroot/live/image
mount --bind /cow/rw /sysroot/live/cow
mount --bind /live/image /sysroot/live/image
umount /cow 2>/dev/null || true
umount /live/image 2>/dev/null || true
shred -u /dev/shm/overlay-key 2>/dev/null || rm -f /dev/shm/overlay-key
EOF
chmod 755 "$MODULE_DIR/overlay-crypt.sh"
# 4. 创建RAM-Wipe模块(可选)
if [[ "$ENABLE_RAM_WIPE" == "true" ]]; then
print_info "Creating Dracut module: 40ram-wipe..."
local RW_DIR="/usr/lib/dracut/modules.d/40ram-wipe"
mkdir -p "$RW_DIR"
cat > "$RW_DIR/module-setup.sh" << 'EOF'
#!/bin/bash
check() {
require_binaries sync || return 1
require_binaries sleep || return 1
require_binaries dmsetup || return 1
return 0
}
depends() { return 0; }
install() {
inst_multiple sync sleep dmsetup
inst_hook shutdown 40 "$moddir/wipe-ram.sh"
inst_hook cleanup 80 "$moddir/wipe-ram-needshutdown.sh"
}
installkernel() { return 0; }
EOF
chmod +x "$RW_DIR/module-setup.sh"
cat > "$RW_DIR/wipe-ram.sh" << 'EOF'
#!/bin/sh
. /lib/dracut-lib.sh
kernel_wiperam_setting="$(getarg wiperam)"
if [ "$kernel_wiperam_setting" = "skip" ]; then
echo "wipe-ram.sh: Skip."
return 0
fi
echo "wipe-ram.sh: Starting RAM wipe..."
sync
echo 3 > /proc/sys/vm/drop_caches
sync
echo "wipe-ram.sh: Completed."
EOF
chmod +x "$RW_DIR/wipe-ram.sh"
cat > "$RW_DIR/wipe-ram-needshutdown.sh" << 'EOF'
#!/bin/sh
. /lib/dracut-lib.sh
if [ "$(getarg wiperam)" != "skip" ]; then
need_shutdown
fi
EOF
chmod +x "$RW_DIR/wipe-ram-needshutdown.sh"
fi
# 5. 创建Dracut配置
print_info "Creating Dracut configuration..."
echo 'add_dracutmodules+=" ram-wipe "' > /etc/dracut.conf.d/30-ram-wipe.conf
# 6. 更新Initramfs
print_info "Updating initramfs with dracut..."
dracut --verbose --force
# 7. 创建GRUB自定义菜单项
print_info "Creating GRUB custom entries in /etc/grub.d/40_custom..."
cat > /etc/grub.d/40_custom << EOF
#!/usr/bin/sh
exec tail -n +3 \$0
menuentry '$GRUB_MENU_TITLE' --class qubes --class gnu-linux --class gnu --class os --class xen \$menuentry_id_option 'xen-gnulinux-simple-/dev/mapper/qubes_dom0-root' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root $BOOT_UUID
echo 'Loading Xen ...'
if [ "\$grub_platform" = "pc" -o "\$grub_platform" = "" ]; then
xen_rm_opts=
else
xen_rm_opts="no-real-mode edd=off"
fi
insmod multiboot2
multiboot2 /$XEN_PATH placeholder console=none dom0_mem=min:1024M $DOM0_MAX_RAM ucode=scan smt=off gnttab_max_frames=2048 gnttab_max_maptrack_frames=4096 \${xen_rm_opts}
echo 'Loading Linux $LATEST_KERNEL ...'
module2 /$LATEST_KERNEL placeholder root=/dev/mapper/qubes_dom0-root ro rd.luks.uuid=$LUKS_UUID rd.lvm.lv=qubes_dom0/root rd.lvm.lv=qubes_dom0/swap plymouth.ignore-serial-consoles rhgb cryptovl quiet module.sig_enforce=1 bootscrub=on usbcore.authorized_default=0 xen_privcmd.unrestricted init_on_alloc=1 init_on_free=1
echo 'Loading initial ramdisk ...'
insmod multiboot2
module2 --nounzip $LATEST_INITRAMFS
}
EOF
chmod 755 /etc/grub.d/40_custom
# 8. 更新GRUB
print_info "Updating GRUB configuration..."
grub2-mkconfig -o /boot/grub2/grub.cfg
# 9. 创建硬化和提示脚本
if [[ "$ENABLE_HARDENING" == "true" ]]; then
print_info "Creating hardening autostart script..."
mkdir -p "$USER_HOME/.config"
cat > "$USER_HOME/.config/harden.sh" << 'EOF'
#!/bin/bash
sleep 6
if findmnt -n -o SOURCE / | grep -qE "(overlay)"; then
notify-send --expire-time=20000 "Amnesic session is running" "dom0 mode: $(findmnt -n -o SOURCE /)" --icon=dialog-information
sudo sysctl -w kernel.sysrq=0 2>/dev/null
sudo sysctl -w kernel.perf_event_paranoid=3 2>/dev/null
sudo sysctl -w kernel.kptr_restrict=2 2>/dev/null
sudo sysctl -w kernel.panic=5 2>/dev/null
sudo sysctl -w fs.protected_regular=2 2>/dev/null
sudo sysctl -w fs.protected_fifos=2 2>/dev/null
sudo sysctl -w kernel.printk="3 3 3 3" 2>/dev/null
sudo sysctl -w kernel.kexec_load_disabled=1 2>/dev/null
sudo sysctl -w kernel.io_uring_disabled=2 2>/dev/null
sudo chattr +i /boot/grub2/grub.cfg 2>/dev/null
sudo chattr +i /boot 2>/dev/null
fi
EOF
chmod 755 "$USER_HOME/.config/harden.sh"
mkdir -p "$USER_HOME/.config/autostart"
cat > "$USER_HOME/.config/autostart/harden.desktop" << EOF
[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=harden
Comment=
Exec=$USER_HOME/.config/harden.sh
OnlyShowIn=XFCE;
RunHook=0
StartupNotify=false
Terminal=false
Hidden=false
EOF
fi
print_success "Installation completed. Reboot and select '$GRUB_MENU_TITLE' from GRUB."
print_info "IMPORTANT: If USB keyboard/mouse does not work in the live mode,"
print_info "please refer to: https://forum.qubes-os.org/t/38868"
print_info "The recommended fix is to use 'usbcore.authorized_default=0' and 'rd.qubes.dom0_usb=<BDF>'"
print_info "instead of the deprecated 'rd.qubes.hide_all_usb' parameter."
}
# --- 卸载功能 ---
uninstall_mode() {
print_info "Starting uninstallation of Qubes OS Ephemeral Overlay Mode..."
# 1. 移除GRUB菜单项
print_info "Removing GRUB custom entries..."
if [[ -f /etc/grub.d/40_custom ]]; then
sed -i '/exec tail -n +3 \$0/,$d' /etc/grub.d/40_custom
if ! grep -q "exec tail -n +3 \$0" /etc/grub.d/40_custom; then
echo '#!/usr/bin/sh' > /etc/grub.d/40_custom
echo 'exec tail -n +3 $0' >> /etc/grub.d/40_custom
fi
chmod 755 /etc/grub.d/40_custom
fi
# 2. 移除Dracut模块
print_info "Removing Dracut modules..."
local modules=(
"/usr/lib/dracut/modules.d/90overlay-crypt"
"/usr/lib/dracut/modules.d/40ram-wipe"
)
for mod in "${modules[@]}"; do
if [[ -d "$mod" ]]; then
rm -rf "$mod"
print_info "Removed: $mod"
fi
done
# 3. 移除Dracut配置文件
sudo chattr -i /boot 2>/dev/null || true
sudo chattr -i /boot/grub2/grub.cfg 2>/dev/null || true
print_info "Removing Dracut configuration files..."
rm -f /etc/dracut.conf.d/30-ram-wipe.conf
# 4. 恢复swap
print_info "Restoring swap in /etc/fstab..."
if [[ -f /etc/fstab.bak ]]; then
mv /etc/fstab.bak /etc/fstab
fi
# 5. 更新Initramfs
print_info "Updating initramfs..."
dracut --verbose --force
# 6. 更新GRUB
print_info "Updating GRUB configuration..."
grub2-mkconfig -o /boot/grub2/grub.cfg
# 7. 删除加密容器文件
if [[ -f /var/lib/overlay-crypt.img ]]; then
print_info "Removing encrypted container file..."
rm -f /var/lib/overlay-crypt.img
fi
# 8. 移除硬化脚本
if [[ -f "$USER_HOME/.config/harden.sh" ]]; then
rm -f "$USER_HOME/.config/harden.sh"
fi
if [[ -f "$USER_HOME/.config/autostart/harden.desktop" ]]; then
rm -f "$USER_HOME/.config/autostart/harden.desktop"
fi
print_success "Uninstallation completed. System restored to default boot behavior."
}
# --- 主逻辑 ---
main() {
check_root
# 获取当前用户HOME
USER_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6 2>/dev/null || echo "$HOME")
case "$1" in
install)
install_mode
;;
remove|uninstall)
uninstall_mode
;;
*)
echo "Usage: $0 {install|remove}"
echo " install - Deploy ephemeral overlay mode for dom0"
echo " remove - Remove all changes and restore original boot"
exit 1
;;
esac
}
main "$@"
#非持久化appvm
参考官方文档以及帖子post
qvm-volume config VMname:root rw False
将会让Vmname的root只读 修改会重定向到volatile层
qvm-pool -s vm-pool -o ephemeral_volatile=True
或者
qvm-volume config VMname:volatile ephemeral True
volatile层会使用临时加密 关闭后清空密钥实现非持久化
那么只剩下private卷(/rw 或者说/home/user下所有的修改没有好方法解决 非持久化)
使用脚本(/rw/config/rc.local)将/home/user或者/home/user/下的敏感目录 使用overlay挂到tmpfs保证不落盘即可实现非持久化
使用注意:使用overlay的目录大小要根据实际设计好 比如.cache .local防止浏览器 app写缓存写爆overlay层
使用方法:传到目标appvm:
sudo cat /home/user/PATH/myrc.local >> /rw/config/rc.local
重启后使用mount df验证是否成功挂载
###参考脚本:
# /rw/config/rc.local 使用 Overlay 覆盖 /home/user 下的目录,并限制 upperdir 大小
USER_HOME="/home/user"
TARGET_DIRS=".cache .config .local"
# 为不同目录设置大小限制(可根据需要调整)
TMPFS_SIZE_CACHE="450M"
TMPFS_SIZE_CONFIG="50M"
TMPFS_SIZE_LOCAL="450M"
# 检查 mountpoint 命令
if ! command -v mountpoint &> /dev/null; then
echo "Warning: mountpoint not found, skipping mount checks."
is_mounted() { return 1; }
else
is_mounted() { mountpoint -q "$1"; }
fi
handle_directory() {
local dir_name="$1"
local target_path="${USER_HOME}/${dir_name}"
# 如果已经挂载,跳过
if is_mounted "$target_path"; then
echo "Skipping $target_path: already mounted"
return
fi
# 如果路径是软链接,跳过
if [ -L "$target_path" ]; then
echo "Skipping $target_path: is a symlink"
return
fi
# 确保目录存在(若不存在则创建空目录作为 lowerdir)
if [ ! -d "$target_path" ]; then
echo "Directory $target_path does not exist, creating empty one."
mkdir -p "$target_path"
chown 1000:1000 "$target_path"
chmod 700 "$target_path"
fi
# 根据目录名决定大小限制
local size_limit
case "$dir_name" in
".cache")
size_limit="$TMPFS_SIZE_CACHE"
;;
".config")
size_limit="$TMPFS_SIZE_CONFIG"
;;
".local")
size_limit="$TMPFS_SIZE_LOCAL"
;;
*)
size_limit="50M"
# 默认值
;;
esac
local safe_name=$(echo "$dir_name" | tr '/' '_')
# 创建一个专用的 tmpfs 挂载点,用于存放 upperdir 和 workdir
local upper_mount="/tmp/overlay_upper_${safe_name}"
mkdir -p "$upper_mount"
# 挂载 tmpfs,限制大小
mount -t tmpfs -o size="$size_limit",mode=700,uid=1000,gid=1000 tmpfs "$upper_mount"
echo "tmpfs mounted on $upper_mount (size=$size_limit)"
# 在 tmpfs 内创建 upper 和 work 子目录
local upperdir="${upper_mount}/upper"
local workdir="${upper_mount}/work"
mkdir -p "$upperdir" "$workdir"
# 挂载 Overlay
mount -t overlay overlay \
-o lowerdir="$target_path",upperdir="$upperdir",workdir="$workdir" \
"$target_path"
echo "Overlay mounted on $target_path (upperdir in $upper_mount, size=$size_limit)"
}
# 处理所有目录
for dir in $TARGET_DIRS; do
handle_directory "$dir"
done
exit 0
#Non-persistent dom0
###Refer to the method of Option 1 of this forum post
Advantages of this program:
- No additional memory is consumed. The dom0 modification itself takes up almost no hard disk.
- All modifications to dom0 will be placed in a temporary encryption overlay, and the key in the ram will be cleared when shutting down to ensure that it cannot be decrypted.
Implement non-persistence of DOM0
Disadvantages:
1.
This mode is only for dom0 non-persistence
There is no non-persistence for various other vm (adding, deleting vm, editing files in appvm, etc. will not restore the original state after restarting)
appvm non-persistent reference part 2
2. After installation, some directories such as /boot will be read-only. Before updating the dom0 kernel related packages, delete this function (remove parameter)
Use ai to write a complete deployment script. After testing, it runs well (
Please modify some parameters according to your own needs. If you do not clone the vm to the varlibqubes pool to run, the appvm will not be persistent. The size of the dom0 overlay does not need to be too large. 1G is enough)
Prerequisites for use: Use lvm/lvm simple configuration when installing qubes os.
Used in dom0 xfce4-terminal
sudo lvs
sudo vgs
Check whether there is output to determine whether this is the installation method.
How to use: Pass to dom0 terminal and run:
sudo ./xx.sh install
sudo ./xx.sh remove
###The deployment script is as follows
#Non-persistent appvm
Refer to official documents and post
qvm-volume config VMname: root rw False
Will make the root of Vmname read-only and modifications will be redirected to the volatile layer
qvm-pool -s vm-pool -o ephemeral_volatile=True
or
qvm-volume config VMname: volatile ephemeral True
The volatile layer will use temporary encryption and clear the key after closing to achieve non-persistence.
Then only the private volume is left (there is no good way to solve all the modifications under /rw or /home/user, non-persistent)
Use a script (/rw/config/rc.local) to hang sensitive directories under /home/user or /home/user/ using overlay to tmpfs to ensure non-persistence without losing disk space.
Note on usage: The size of the directory using overlay should be designed according to the actual situation. For example, .cache.local prevents the browser app from writing cache and exploding the overlay layer.
Use method:pass to target appvm:
sudo cat /home/user/PATH/myrc.local >> /rw/config/rc.local
After restarting, use mount df to verify whether the mount is successful.
###Reference script: