Final version.
⚠️ BWRAP DISCLAIMER ⚠️
Disclaimer: When configuring bwrap, I am not responsible for any misuse of the sandbox rules, the rules provided in the script are only examples. Bwrap is not a simple tool, as many bubblewrap arguments map 1:1 to low-level system calls and kernel mechanisms, but it is a powerful tool for defeating some exploitation routes in a specific security policy.
Everything passed to the sandboxed can be used to break out of it, for example a dbus-socket, which could be used to execute something via systemd. Also, as mentioned in a thread in Privacy Guides, a sandboxed webbrowser could be less able to enforce a security sandbox within itself.
Remember that Qubes already uses compartmentalization and amnesic systems as a security layer.
Simplex
whonix-simplex-named-disposable.sh
#!/bin/bash
#######################################################################
# File Name : whonix-simplex-named-disposable.sh
# Description : This script creates a named disposable SimpleX qube
# based on Whonix Workstation template. It downloads
# and verify the hash of the last SimpleX AppImage and
# uses sys-whonix as NetVM.
# Dependencies : curl
# Usage : • Transfer this script from appvm to dom0 with:
# [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-simplex-named-disposable.sh' > ~/whonix-simplex-named-disposable.sh
# • Make the script executable with:
# [user@dom0 ~]$ chmod +x ~/whonix-simplex-named-disposable.sh
# • Run the script with:
# [user@dom0 ~]$ bash ~/whonix-simplex-named-disposable.sh
# • To update, run the script in whonix-simplex-dvm (disposable template):
# bash ~/update-simplex-chat.sh
# Author : Me and the bois
# License : Free of charge, no warranty
# Last edited : 2025-10-11
#######################################################################
# Safety check
set -eu
# Define Variables
APP_NAME="simplex-chat"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-simplex-template"
DISP_TEMPLATE="whonix-simplex-template-dvm"
NAMED_DISP_VM="disp-whonix-simplex"
NET_VM="sys-whonix"
SOURCE_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"
# Step 1: Install and update the Qubes Template
echo -e "\n[1/7] Checking for Qubes template..."
if ! qvm-check "$BASE_TEMPLATE" 2>/dev/null; then
echo "Installing $BASE_TEMPLATE..."
sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi
# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true
# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm
# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true
# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
qvm-prefs "$CUSTOM_TEMPLATE" label black
# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "locale-gen en_US.UTF-8"
# Shutdown
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
if ! qvm-check "$DISP_TEMPLATE" 2>/dev/null; then
qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True
else
echo "DVM template $DISP_TEMPLATE already exists, skipping creation."
fi
# Step 5: Download and verify in disposable template
echo -e "\n[5/7] Configuring $APP_NAME in disposable template..."
# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
qvm-start "$DISP_TEMPLATE" && sleep 3
fi
# Fetch latest release information
echo "Fetching latest release information..."
RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$SOURCE_URL'"); then
# Check for rate limit error
if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
echo -e "\nERROR: Failed to fetch release info from API (rate limited)" >&2
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
exit 1
fi
fi
# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | \
grep -o '"browser_download_url": *"[^"]*"' | \
grep -i "simplex-desktop-x86_64\.AppImage" | \
cut -d '"' -f 4 | \
head -n 1)
if [[ -z "$DOWNLOAD_URL" ]]; then
echo "ERROR: Could not find download URL" >&2
echo "Available browser_download_url entries:" >&2
echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' >&2
exit 1
fi
echo "Downloading from: $DOWNLOAD_URL"
# Download
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$HOME/$APP_NAME.AppImage' '$DOWNLOAD_URL'"
# Hash verification
echo "Verifying integrity..."
# Extract the hash from API
ASSET_NAME=$(basename "$DOWNLOAD_URL")
EXPECTED_HASH=$(echo "$RELEASE_JSON" | \
grep -A 30 "\"name\":.*\"$ASSET_NAME\"" | \
grep '"digest"' | \
head -n 1 | \
sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')
if [[ -z "$EXPECTED_HASH" ]]; then
echo "ERROR: Could not extract hash from release info!" >&2
echo "Looking for asset: $ASSET_NAME" >&2
echo "Available assets:" >&2
echo "$RELEASE_JSON" | grep -A 5 -B 5 "\"name\":.*\"$ASSET_NAME\"" >&2
qvm-run -p "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
exit 1
fi
echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$HOME/$APP_NAME.AppImage' | cut -d' ' -f1")
echo "Found hash: $ACTUAL_HASH"
if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
echo "ERROR: Hash verification failed!" >&2
echo "Hashes do not match!" >&2
qvm-run "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
exit 1
fi
echo "Hash verification passed"
# Adjust user permissions
qvm-run -u root "$DISP_TEMPLATE" "chown user:user '$HOME/$APP_NAME.AppImage'"
qvm-run "$DISP_TEMPLATE" "chmod +x '$HOME/$APP_NAME.AppImage'"
# Create desktop entry
echo "Creating desktop entry..."
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=Private and secure open-source messenger
Exec=/home/user/$APP_NAME.AppImage
Icon=starred
Terminal=false
Categories=Network;Chat;
StartupNotify=true"
# Write desktop entry to the custom template
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
sleep 3
if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
mkdir -p /usr/share/applications && \
cat > /usr/share/applications/$APP_NAME.desktop <&2
exit 1
fi
# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
exit 1
fi
if [[ -z "\$dl_url" ]]; then
echo "Error: No download URL found" >&2
echo "Available browser_download_url entries:" >&2
echo "\$release" | grep -o '"browser_download_url": *"[^"]*"' >&2
exit 1
fi
echo "Downloading update from: \$dl_url"
curl --tlsv1.2 --http1.1 -L -o "\$HOME/\$APP_NAME.AppImage" "\$dl_url"
# Hash verification
echo "Verifying integrity..."
asset_name=\$(basename "\$dl_url")
# Extract the hash from API
expected_hash=\$(echo "\$release" | \\
grep -A 30 "\"name\": *\"\$asset_name\"" | \\
grep '"digest"' | \\
head -n 1 | \\
sed -n 's/.*"digest": *"sha256:\\([a-f0-9]*\\)".*/\\1/p')
if [[ -z "\$expected_hash" ]]; then
echo "ERROR: Could not extract hash from release info!" >&2
echo "Looking for asset: \$asset_name" >&2
echo "Available assets:" >&2
echo "\$release" | grep -A 5 -B 5 "\"name\": *\"\$asset_name\"" >&2
rm -f "\$HOME/\$APP_NAME.AppImage"
exit 1
fi
echo "Expected hash: \$expected_hash"
actual_hash=\$(sha256sum "\$HOME/\$APP_NAME.AppImage" | cut -d' ' -f1)
echo "Found hash: \$actual_hash"
if [[ "\$expected_hash" != "\$actual_hash" ]]; then
echo "ERROR: Hash verification failed!" >&2
echo "Hashes do not match!" >&2
rm -f "\$HOME/\$APP_NAME.AppImage"
exit 1
fi
echo "Hash verification passed"
# Adjust permissions
chmod +x "\$HOME/\$APP_NAME.AppImage"
echo "Update complete and verified"
EOF
qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh
qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"
# Step 6: Create named disposable VM instance
echo -e "\n[6/7] Creating named disposable VM instance..."
if ! qvm-check "$NAMED_DISP_VM" 2>/dev/null; then
qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
--property netvm="$NET_VM" \
--property include_in_backups=False \
"$NAMED_DISP_VM"
qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1
else
echo "Named disposable VM $NAMED_DISP_VM already exists, skipping creation."
fi
# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop xfce4-terminal.desktop"
# Finalize
echo -e "\nFinish!"
whonix-simplex-bwrap-named-disposable.sh
#!/bin/bash
#######################################################################
# File Name : whonix-simplex-bwrap-named-disposable.sh
# Description : This script creates a named disposable SimpleX qube
# based on Whonix Workstation template. It downloads
# and verify the hash of the last SimpleX AppImage and
# uses sys-whonix as NetVM. It runs the application
# using bubblewrap (bwrap) to allow unprivileged
# users to use container features to defining its own
# security model, and choosing appropriate bubblewrap
# command-line arguments.
# Dependencies : curl, bubblewrap
# Usage : • Transfer this script from appvm to dom0 with:
# [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-simplex-named-disposable.sh' > ~/whonix-simplex-named-disposable.sh
# • Make the script executable with:
# [user@dom0 ~]$ chmod +x ~/whonix-simplex-named-disposable.sh
# • Run the script with:
# [user@dom0 ~]$ bash ~/whonix-simplex-named-disposable.sh
# • To update, run the script in whonix-simplex-dvm (disposable template):
# bash ~/update-simplex-chat.sh
# Author : Me and the bois
# License : Free of charge, no warranty
# Last edited : 2025-10-11
#######################################################################
# Safety check
set -eu
# Define Variables
APP_NAME="simplex-chat"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-simplex-template"
DISP_TEMPLATE="whonix-simplex-template-dvm"
NAMED_DISP_VM="disp-whonix-simplex"
NET_VM="sys-whonix"
SOURCE_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"
# Define bwrap rules
get_bwrap_cmd() {
local DISPLAY_VALUE="${DISPLAY:-:0}"
local USER_ID=$(id -u)
local DOWNLOAD_DIR="${XDG_DOWNLOAD_DIR:-$HOME/Downloads}"
cat </dev/null; then
echo "Installing $BASE_TEMPLATE..."
sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi
# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true
# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm
# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true
# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
qvm-prefs "$CUSTOM_TEMPLATE" label black
# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "locale-gen en_US.UTF-8"
# Shutdown
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
if ! qvm-check "$DISP_TEMPLATE" 2>/dev/null; then
qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True
else
echo "DVM template $DISP_TEMPLATE already exists, skipping creation."
fi
# Step 5: Download and verify in disposable template (we need to use userspace in dvm)
echo -e "\n[5/7] Configuring disposable template..."
# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
qvm-start "$DISP_TEMPLATE" && sleep 3
fi
# Fetch latest release information
echo "Fetching latest release information..."
RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$SOURCE_URL'"); then
# Check for rate limit error
if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
echo -e "\nERROR: Failed to fetch release info from API (rate limited)" >&2
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
exit 1
fi
fi
# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | \
grep -o '"browser_download_url": *"[^"]*"' | \
grep -i "simplex-desktop-x86_64\.AppImage" | \
cut -d '"' -f 4 | \
head -n 1)
if [[ -z "$DOWNLOAD_URL" ]]; then
echo "ERROR: Could not find download URL" >&2
echo "Available browser_download_url entries:" >&2
echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' >&2
exit 1
fi
echo "Downloading from: $DOWNLOAD_URL"
# Download
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$HOME/$APP_NAME.AppImage' '$DOWNLOAD_URL'"
# Hash verification
echo "Verifying integrity..."
# Extract the hash from API
ASSET_NAME=$(basename "$DOWNLOAD_URL")
EXPECTED_HASH=$(echo "$RELEASE_JSON" | \
grep -A 30 "\"name\":.*\"$ASSET_NAME\"" | \
grep '"digest"' | \
head -n 1 | \
sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')
if [[ -z "$EXPECTED_HASH" ]]; then
echo "ERROR: Could not extract hash from release info!" >&2
echo "Looking for asset: $ASSET_NAME" >&2
echo "Available assets:" >&2
echo "$RELEASE_JSON" | grep -A 5 -B 5 "\"name\":.*\"$ASSET_NAME\"" >&2
qvm-run -p "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
exit 1
fi
echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$HOME/$APP_NAME.AppImage' | cut -d' ' -f1")
echo "Found hash: $ACTUAL_HASH"
if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
echo "ERROR: Hash verification failed!" >&2
echo "Hashes do not match!" >&2
qvm-run "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
exit 1
fi
echo "Hash verification passed"
# Adjust permissions
qvm-run -u root "$DISP_TEMPLATE" "chown user:user '$HOME/$APP_NAME.AppImage'"
qvm-run "$DISP_TEMPLATE" "chmod +x '$HOME/$APP_NAME.AppImage'"
# Create desktop entry using bwrap rules
echo "Creating desktop entry..."
# Generate the bwrap command
BWRAP_CMD=$(get_bwrap_cmd)
# Create desktop entry
echo "Creating desktop entry..."
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=Private and secure open-source messenger
Exec=$BWRAP_CMD
Icon=starred
Terminal=false
Categories=Network;Chat;
StartupNotify=true"
# Write desktop entry to the custom template
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
sleep 3
if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
mkdir -p /usr/share/applications && \
cat > /usr/share/applications/$APP_NAME.desktop <&2
exit 1
fi
# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
exit 1
fi
if [[ -z "\$dl_url" ]]; then
echo "Error: No download URL found" >&2
echo "Available browser_download_url entries:" >&2
echo "\$release" | grep -o '"browser_download_url": *"[^"]*"' >&2
exit 1
fi
echo "Downloading update from: \$dl_url"
curl --tlsv1.2 --http1.1 -L -o "\$HOME/\$APP_NAME.AppImage" "\$dl_url"
# Hash verification
echo "Verifying integrity..."
asset_name=\$(basename "\$dl_url")
# Extract the hash from API
expected_hash=\$(echo "\$release" | \\
grep -A 30 "\"name\": *\"\$asset_name\"" | \\
grep '"digest"' | \\
head -n 1 | \\
sed -n 's/.*"digest": *"sha256:\\([a-f0-9]*\\)".*/\\1/p')
if [[ -z "\$expected_hash" ]]; then
echo "ERROR: Could not extract hash from release info!" >&2
echo "Looking for asset: \$asset_name" >&2
echo "Available assets:" >&2
echo "\$release" | grep -A 5 -B 5 "\"name\": *\"\$asset_name\"" >&2
rm -f "\$HOME/\$APP_NAME.AppImage"
exit 1
fi
echo "Expected hash: \$expected_hash"
actual_hash=\$(sha256sum "\$HOME/\$APP_NAME.AppImage" | cut -d' ' -f1)
echo "Found hash: \$actual_hash"
if [[ "\$expected_hash" != "\$actual_hash" ]]; then
echo "ERROR: Hash verification failed!" >&2
echo "Hashes do not match!" >&2
rm -f "\$HOME/\$APP_NAME.AppImage"
exit 1
fi
echo "Hash verification passed"
# Adjust permissions
chmod +x "\$HOME/\$APP_NAME.AppImage"
echo "Update complete and verified"
EOF
qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh
qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"
# Step 6: Create named disposable VM instance
echo -e "\n[6/7] Creating named disposable VM instance..."
if ! qvm-check "$NAMED_DISP_VM" 2>/dev/null; then
qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
--property netvm="$NET_VM" \
--property include_in_backups=False \
"$NAMED_DISP_VM"
qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1
else
echo "Named disposable VM $NAMED_DISP_VM already exists, skipping creation."
fi
# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop xfce4-terminal.desktop"
# Finalize
echo -e "\nFinish!"
Issues: FUSE in Simplex bwrap setup
This version whonix-simplex-bwrap-named-disposable.sh requires FUSE permissions to be manually enabled in the bwrap rules. Look this:
Telegram
I took this opportunity to set up for Telegram binary as well.
whonix-telegram-named-disposable.sh
#!/bin/bash
#######################################################################
# File Name : whonix-telegram-named-disposable.sh
# Description : This script creates a named disposable Telegram qube
# based on Whonix Workstation template. It downloads
# and verify the hash of the latest Telegram binary
# and uses sys-whonix as NetVM. Note that Telegram have
# a native binary for updates.
# Dependencies : curl
# Usage : • Transfer this script from appvm to dom0 with:
# [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-telegram-named-disposable.sh' > ~/whonix-telegram-named-disposable.sh
# • Make the script executable with:
# [user@dom0 ~]$ chmod +x ~/whonix-telegram-named-disposable.sh
# • Run the script with:
# [user@dom0 ~]$ bash ~/whonix-telegram-named-disposable.sh
# • To update, run the script in whonix-telegram-dvm (disposable template):
# bash ~/update-telegram.sh
# Author : Me and the bois
# License : Free of charge, no warranty
# Last edited : 2025-10-11
#######################################################################
# Safety check
set -eu
# Define Variables
APP_NAME="Telegram" # Absolute reference
APP_PATH="/home/user/telegram-desktop"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-telegram-template"
DISP_TEMPLATE="whonix-telegram-template-dvm"
NAMED_DISP_VM="disp-whonix-telegram"
NET_VM="sys-whonix"
SOURCE_URL="https://api.github.com/repos/telegramdesktop/tdesktop/releases/latest"
# Step 1: Install and update the Qubes Template
echo -e "\n[1/7] Checking for Qubes template..."
if ! qvm-check "$BASE_TEMPLATE" 2>/dev/null; then
echo "Installing $BASE_TEMPLATE..."
sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi
# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true
# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm
# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true
# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
qvm-prefs "$CUSTOM_TEMPLATE" label black
# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "locale-gen en_US.UTF-8"
# Install dependencies (change as needed)
echo "Installing dependencies..."
qvm-run -p -u root "$CUSTOM_TEMPLATE" "apt-get update && apt-get install -y \
qt5-image-formats-plugins \
libabsl20220623 \
libavcodec59 \
libavformat59 \
libavutil57 \
libglib2.0-0 \
libglibmm-2.68-1 \
libhunspell-1.7-0 \
libjpeg62-turbo \
libkf5coreaddons5 \
liblz4-1 \
libminizip1 \
libopenal1 \
libopus0 \
libqrcodegencpp1 \
libqt5core5a \
libqt5gui5 \
libqt5network5 \
libqt5qml5 \
libqt5quickwidgets5 \
libqt5svg5 \
libqt5waylandcompositor5 \
libqt5widgets5 \
librlottie0-1 \
libsigc++-3.0-0 \
libsrtp2-1 \
libssl3 \
libswresample4 \
libswscale6 \
libvpx7 \
libwayland-client0 \
libxcb-keysyms1 \
libxcb-record0 \
libxcb-screensaver0 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
libxxhash0 \
zlib1g"
# Shutdown
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
if ! qvm-check "$DISP_TEMPLATE" 2>/dev/null; then
qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True
else
echo "DVM template $DISP_TEMPLATE already exists, skipping creation."
fi
# Step 5: Download and verify in disposable template
echo -e "\n[5/7] Configuring disposable template..."
# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
qvm-start "$DISP_TEMPLATE" && sleep 3
fi
# Fetch latest release information
echo "Fetching latest release information..."
RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$SOURCE_URL'"); then
# Check for rate limit error
if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
echo -e "\nERROR: Failed to fetch release info from API (rate limited)" >&2
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
exit 1
fi
fi
# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | \
grep -o '"browser_download_url": *"[^"]*"' | \
grep "tsetup\..*\.tar\.xz" | \
cut -d '"' -f 4 | \
head -n 1)
if [[ -z "$DOWNLOAD_URL" ]]; then
echo "ERROR: Could not find download URL" >&2
echo "Available browser_download_url entries:" >&2
echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' >&2
exit 1
fi
echo "Downloading from: $DOWNLOAD_URL"
# Download
qvm-run -p "$DISP_TEMPLATE" "mkdir -p '$APP_PATH'"
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$APP_PATH/$APP_NAME.tar.xz' '$DOWNLOAD_URL'"
# Hash verification
echo "Verifying integrity..."
# Extract the hash from API
ASSET_NAME=$(basename "$DOWNLOAD_URL")
EXPECTED_HASH=$(echo "$RELEASE_JSON" | \
grep -A 30 "\"name\":.*\"$ASSET_NAME\"" | \
grep '"digest"' | \
head -n 1 | \
sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')
if [[ -z "$EXPECTED_HASH" ]]; then
echo "ERROR: Could not extract hash from release info!" >&2
echo "Looking for asset: $ASSET_NAME" >&2
echo "Available assets:" >&2
echo "$RELEASE_JSON" | grep -o '"name": *"[^"]*"' | head -10 >&2
qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
exit 1
fi
echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$APP_PATH/$APP_NAME.tar.xz' | cut -d' ' -f1")
echo "Found hash: $ACTUAL_HASH"
if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
echo "ERROR: Hash verification failed!" >&2
echo "Hashes do not match!" >&2
qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
exit 1
fi
echo "Hash verification passed"
# Extract and install
echo "Extracting files..."
qvm-run -p "$DISP_TEMPLATE" "tar -xf '$APP_PATH/$APP_NAME.tar.xz' -C '$APP_PATH/' --strip-components=1"
qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
# Telegram hidden feature for TDATA portable account storage in the same directory of the app binary
qvm-run -p "$DISP_TEMPLATE" "mkdir -p $APP_PATH/TelegramForcePortable"
# Adjust user permissions
qvm-run -p -u root "$DISP_TEMPLATE" "chown -R user:user '$APP_PATH'"
qvm-run -p "$DISP_TEMPLATE" "chmod +x '$APP_PATH/$APP_NAME'"
qvm-run -p "$DISP_TEMPLATE" "chmod +x '$APP_PATH/Updater'" # Absolute reference
# Create desktop entry
echo "Creating desktop entry..."
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=New era of messaging
Exec=$APP_PATH -- %u
Icon=org.telegram.desktop
Terminal=false
Categories=Chat;Network;InstantMessaging;Qt;
StartupNotify=true
MimeType=x-scheme-handler/tg;x-scheme-handler/tonsite;"
# Write desktop entry to the custom template
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
sleep 3
if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
mkdir -p /usr/share/applications && \
cat > /usr/share/applications/$APP_NAME.desktop <&2
exit 1
fi
# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
exit 1
fi
if [[ -z "\$dl_url" ]]; then
echo "Error: No download URL found" >&2
echo "Available browser_download_url entries:" >&2
echo "\$release" | grep -o '"browser_download_url": *"[^"]*"' >&2
exit 1
fi
echo "Downloading update from: \$dl_url"
curl --tlsv1.2 --http1.1 -L -o "\$APP_PATH/\$APP_NAME.tar.xz" "\$dl_url"
# Hash verification
echo "Verifying integrity..."
asset_name=\$(basename "\$dl_url")
# Extract the hash from API
expected_hash=\$(echo "\$release" | \\
grep -A 30 "\"name\": *\"\$asset_name\"" | \\
grep '"digest"' | \\
head -n 1 | \\
sed -n 's/.*"digest": *"sha256:\\([a-f0-9]*\\)".*/\\1/p')
if [[ -z "\$expected_hash" ]]; then
echo "ERROR: Could not extract hash from release info!" >&2
echo "Looking for asset: \$asset_name" >&2
echo "Available assets:" >&2
echo "\$release" | grep -o '"name": *"[^"]*"' | head -10 >&2
rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
exit 1
fi
echo "Expected hash: \$expected_hash"
actual_hash=\$(sha256sum "\$APP_PATH/\$APP_NAME.tar.xz" | cut -d' ' -f1)
echo "Found hash: \$actual_hash"
if [[ "\$expected_hash" != "\$actual_hash" ]]; then
echo "ERROR: Hash verification failed!" >&2
echo "Hashes do not match!" >&2
rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
exit 1
fi
echo "Hash verification passed"
# Clean, extract and update
echo "Extracting update..."
rm -f "\$APP_PATH/\$APP_NAME" "\$APP_PATH/Updater"
tar -xf "\$APP_PATH/\$APP_NAME.tar.xz" -C "\$APP_PATH/" --strip-components=1
rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
# Adjust permissions
chmod +x "\$APP_PATH/\$APP_NAME"
chmod +x "\$APP_PATH/Updater"
echo "Update complete and verified"
EOF
qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run -p "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh
qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"
# Step 6: Create named disposable VM instance
echo -e "\n[6/7] Creating named disposable VM instance..."
if ! qvm-check "$NAMED_DISP_VM" 2>/dev/null; then
qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
--property netvm="$NET_VM" \
--property include_in_backups=False \
"$NAMED_DISP_VM"
qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1
else
echo "Named disposable VM $NAMED_DISP_VM already exists, skipping creation."
fi
# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop xfce4-terminal.desktop"
# Finalize
echo -e "\nFinish!"
whonix-telegram-bwrap-named-disposable.sh
#!/bin/bash
#######################################################################
# File Name : whonix-telegram-bwrap-named-disposable.sh
# Description : This script creates a named disposable Telegram qube
# based on Whonix Workstation template. It downloads
# and verify the hash of the latest Telegram binary
# and uses sys-whonix as NetVM. It runs the application
# using bubblewrap (bwrap) to allow unprivileged
# users to use container features to defining its own
# security model, and choosing appropriate bubblewrap
# command-line arguments. Note that Telegram have a
# native binary for updates and a hidden feature for
# TDATA portable account storage in the same directory
# of the app binary.
# Dependencies : curl, bubblewrap
# Usage : • Transfer this script from appvm to dom0 with:
# [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-telegram-bwrap-named-disposable.sh' > ~/whonix-telegram-bwrap-named-disposable.sh
# • Make the script executable with:
# [user@dom0 ~]$ chmod +x ~/whonix-telegram-bwrap-named-disposable.sh
# • Run the script with:
# [user@dom0 ~]$ bash ~/whonix-telegram-bwrap-named-disposable.sh
# • To update, run the script in whonix-telegram-dvm (disposable template):
# bash ~/update-telegram.sh
# Author : Me and the bois
# License : Free of charge, no warranty
# Last edited : 2025-10-11
#######################################################################
# Safety check
set -eu
# Define Variables
APP_NAME="Telegram" # Absolute reference
APP_PATH="/home/user/telegram-desktop"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-telegram-template"
DISP_TEMPLATE="whonix-telegram-template-dvm"
NAMED_DISP_VM="disp-whonix-telegram"
NET_VM="sys-whonix"
SOURCE_URL="https://api.github.com/repos/telegramdesktop/tdesktop/releases/latest"
# Define bwrap rules
get_bwrap_cmd() {
local DISPLAY_VALUE="${DISPLAY:-:0}"
local USER_ID=$(id -u)
local DOWNLOAD_DIR="${XDG_DOWNLOAD_DIR:-$HOME/Downloads}"
cat </dev/null; then
echo "Installing $BASE_TEMPLATE..."
sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi
# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true
# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm
# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true
# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
if ! qvm-check "$CUSTOM_TEMPLATE" 2>/dev/null; then
qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
qvm-prefs "$CUSTOM_TEMPLATE" label black
else
echo "Custom template $CUSTOM_TEMPLATE already exists, skipping clone."
fi
# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "locale-gen en_US.UTF-8"
# Install dependencies (change as needed)
echo "Installing dependencies..."
qvm-run -p -u root "$CUSTOM_TEMPLATE" "apt-get update && apt-get install -y \
qt5-image-formats-plugins \
libabsl20220623 \
libavcodec59 \
libavformat59 \
libavutil57 \
libglib2.0-0 \
libglibmm-2.68-1 \
libhunspell-1.7-0 \
libjpeg62-turbo \
libkf5coreaddons5 \
liblz4-1 \
libminizip1 \
libopenal1 \
libopus0 \
libqrcodegencpp1 \
libqt5core5a \
libqt5gui5 \
libqt5network5 \
libqt5qml5 \
libqt5quickwidgets5 \
libqt5svg5 \
libqt5waylandcompositor5 \
libqt5widgets5 \
librlottie0-1 \
libsigc++-3.0-0 \
libsrtp2-1 \
libssl3 \
libswresample4 \
libswscale6 \
libvpx7 \
libwayland-client0 \
libxcb-keysyms1 \
libxcb-record0 \
libxcb-screensaver0 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
libxxhash0 \
zlib1g \
bubblewrap"
# Shutdown
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
if ! qvm-check "$DISP_TEMPLATE" 2>/dev/null; then
qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True
else
echo "DVM template $DISP_TEMPLATE already exists, skipping creation."
fi
# Step 5: Download and verify in disposable template
echo -e "\n[5/7] Configuring disposable template..."
# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
qvm-start "$DISP_TEMPLATE" && sleep 3
fi
# Fetch latest release information
echo "Fetching latest release information..."
RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$SOURCE_URL'"); then
# Check for rate limit error
if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
echo -e "\nERROR: Failed to fetch release info from API (rate limited)" >&2
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
exit 1
fi
fi
# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | \
grep -o '"browser_download_url": *"[^"]*"' | \
grep "tsetup\..*\.tar\.xz" | \
cut -d '"' -f 4 | \
head -n 1)
if [[ -z "$DOWNLOAD_URL" ]]; then
echo "ERROR: Could not find download URL" >&2
echo "Available browser_download_url entries:" >&2
echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' >&2
exit 1
fi
echo "Downloading from: $DOWNLOAD_URL"
# Download
qvm-run -p "$DISP_TEMPLATE" "mkdir -p '$APP_PATH'"
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$APP_PATH/$APP_NAME.tar.xz' '$DOWNLOAD_URL'"
# Hash verification
echo "Verifying integrity..."
# Extract the hash from API
ASSET_NAME=$(basename "$DOWNLOAD_URL")
EXPECTED_HASH=$(echo "$RELEASE_JSON" | \
grep -A 30 "\"name\":.*\"$ASSET_NAME\"" | \
grep '"digest"' | \
head -n 1 | \
sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')
if [[ -z "$EXPECTED_HASH" ]]; then
echo "ERROR: Could not extract hash from release info!" >&2
echo "Looking for asset: $ASSET_NAME" >&2
echo "Available assets:" >&2
echo "$RELEASE_JSON" | grep -o '"name": *"[^"]*"' | head -10 >&2
qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
exit 1
fi
echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$APP_PATH/$APP_NAME.tar.xz' | cut -d' ' -f1")
echo "Found hash: $ACTUAL_HASH"
if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
echo "ERROR: Hash verification failed!" >&2
echo "Hashes do not match!" >&2
qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
exit 1
fi
echo "Hash verification passed"
# Extract and install
echo "Extracting files..."
qvm-run -p "$DISP_TEMPLATE" "tar -xf '$APP_PATH/$APP_NAME.tar.xz' -C '$APP_PATH/' --strip-components=1"
qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
# Telegram hidden feature for TDATA portable account storage in the same directory of the app binary
qvm-run -p "$DISP_TEMPLATE" "mkdir -p $APP_PATH/TelegramForcePortable"
# Adjust user permissions
qvm-run -p -u root "$DISP_TEMPLATE" "chown -R user:user '$APP_PATH'"
qvm-run -p "$DISP_TEMPLATE" "chmod +x '$APP_PATH/$APP_NAME'"
qvm-run -p "$DISP_TEMPLATE" "chmod +x '$APP_PATH/Updater'" # Absolute reference
# Create desktop entry using bwrap rules
echo "Creating desktop entry..."
# Generate the bwrap command
BWRAP_CMD=$(get_bwrap_cmd)
# Create desktop entry
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=New era of messaging
Exec=$BWRAP_CMD
Icon=org.telegram.desktop
Terminal=false
Categories=Chat;Network;InstantMessaging;Qt;
StartupNotify=true
MimeType=x-scheme-handler/tg;x-scheme-handler/tonsite;"
# Write desktop entry to the custom template
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
sleep 3
if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
mkdir -p /usr/share/applications && \
cat > /usr/share/applications/$APP_NAME.desktop <&2
exit 1
fi
# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
exit 1
fi
if [[ -z "\$dl_url" ]]; then
echo "Error: No download URL found" >&2
echo "Available browser_download_url entries:" >&2
echo "\$release" | grep -o '"browser_download_url": *"[^"]*"' >&2
exit 1
fi
echo "Downloading update from: \$dl_url"
curl --tlsv1.2 --http1.1 -L -o "\$APP_PATH/\$APP_NAME.tar.xz" "\$dl_url"
# Hash verification
echo "Verifying integrity..."
asset_name=\$(basename "\$dl_url")
# Extract the hash from API
expected_hash=\$(echo "\$release" | \\
grep -A 30 "\"name\": *\"\$asset_name\"" | \\
grep '"digest"' | \\
head -n 1 | \\
sed -n 's/.*"digest": *"sha256:\\([a-f0-9]*\\)".*/\\1/p')
if [[ -z "\$expected_hash" ]]; then
echo "ERROR: Could not extract hash from release info!" >&2
echo "Looking for asset: \$asset_name" >&2
echo "Available assets:" >&2
echo "\$release" | grep -o '"name": *"[^"]*"' | head -10 >&2
rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
exit 1
fi
echo "Expected hash: \$expected_hash"
actual_hash=\$(sha256sum "\$APP_PATH/\$APP_NAME.tar.xz" | cut -d' ' -f1)
echo "Found hash: \$actual_hash"
if [[ "\$expected_hash" != "\$actual_hash" ]]; then
echo "ERROR: Hash verification failed!" >&2
echo "Hashes do not match!" >&2
rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
exit 1
fi
echo "Hash verification passed"
# Clean, extract and update
echo "Extracting update..."
rm -f "\$APP_PATH/\$APP_NAME" "\$APP_PATH/Updater"
tar -xf "\$APP_PATH/\$APP_NAME.tar.xz" -C "\$APP_PATH/" --strip-components=1
rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
# Adjust permissions
chmod +x "\$APP_PATH/\$APP_NAME"
chmod +x "\$APP_PATH/Updater"
echo "Update complete and verified"
EOF
qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run -p "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh
qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"
# Step 6: Create named disposable VM instance
echo -e "\n[6/7] Creating named disposable VM instance..."
if ! qvm-check "$NAMED_DISP_VM" 2>/dev/null; then
qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
--property netvm="$NET_VM" \
--property include_in_backups=False \
"$NAMED_DISP_VM"
qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1
else
echo "Named disposable VM $NAMED_DISP_VM already exists, skipping creation."
fi
# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop xfce4-terminal.desktop"
# Finalize
echo -e "\nFinish!"
Issues: icons in Telegram setup
There is no icon because we dont created it inside the folder in the user space like: /home/user/.local/share/icons/hicolor/256x256/apps/org.telegram.desktop.png.
[7/7] Configuring menu items...
(...)
whonix-telegram-template: Creating Telegram
whonix-telegram-template: Failed to get icon for Telegram: No icon received
(...)
There is no desktop entry inside the user space until the first start of the Telegram binary:/home/user/.local/share/applications/org.telegram.desktop._XXXXXXXXXXXXXX.desktop.
Note that we created the desktop entry in the root space of the custom template: /usr/share/applications/Telegram.desktop.
The issue: Telegram could overwrite the desktop entry (with the bwrap rules) and also create an werid name to track the updater binary, see this reports:
- GitHub Issues - Locally-written Launch Script Unexpectedly Allows Sandboxed App to Overwrite Its Own .desktop File #695
- GitHub Issues - Telegram Replaces Launcher Desktop File with Weirdly Named One on Linux #25718
About the list of Telegram binary dependencies:
# Less strict
apt show telegram-desktop
apt-cache depends telegram-desktop
# More strict
dpkg -i telegram-desktop_4.6.5+ds-2+b1_amd64.deb
About the Telegram TDATA setup:
You could safely use Telegram account in TDATA format using the TelegramForcePortable folder. You could purchase TDATA phone number to get a login code then ensure to close all existing sessions on the account and set a 2FA password.
References
Desktop Entry and Others References
- Creating Desktop Shortcuts for AppImages
- Issues and Confusion Executing AppImages from Custom Desktop Shortcuts
- GitHub Issues - Locally-written Launch Script Unexpectedly Allows Sandboxed App to Overwrite Its Own .desktop File #695
- GitHub Issues - Telegram Replaces Launcher Desktop File with Weirdly Named One on Linux #25718
- Freedesktop.org - Desktop Entry Specification
Qubes Default Disposable References
- Whonix Workstation vs Whonix Gateway Discussion
- Understanding Persistent and Disposable VMs
- Clicking URLs in Disposable VMs
Bubblewrap References
- Flatpak Sandbox Permissions Documentation
- Working with the Flatpak Sandbox
- Is the Flatpak Sandbox Actually Weak? - Privacy Guides Community
- Does Flatpak Weaken Chromium/Firefox's Sandbox? - Privacy Guides Community
- Arch Linux Bubblewrap Wiki
- Freedesktop.org - XDG Base Directory Specification
- Understand what it means to share a Linux namespace through $ podman pod create --share namespace
FUSE References
- FUSE in Rootless, Unprivileged Podman - StackExchange
- Mounting FUSE Filesystems as Unprivileged User - Bojan Nikolic (12/19/2024)
- Using FUSE without Root on Linux - Zameer Manji (8/4/2022)
Telegram References
- Authorize Telegram Accounts via "Tdata" on MacOS
- Telegram HVM DomU - Qubes OS Forum
- Telegram Desktop Flathub Repository
- Whonix - Telegram Wiki
Telegram default Icon:
Scripts not rendering correctly in the forum!
The bwrap function caused a problem with the HTML rendering, part of the code simply disappeared.
The complete scripts, just change it to .sh :
whonix-simplex-bwrap-named-disposable.log (11.1 KB)
whonix-simplex-named-disposable.log (9.6 KB)
whonix-telegram-bwrap-named-disposable.log (13.2 KB)
whonix-telegram-named-disposable.log (11.4 KB)
