Interresting
In the context of Whonix (sys-whonix) everything is through Tor. You can enforce .onion-only mode (incognito mode via SOCKS). Its not clear to me if its important for Qubes setup, Simplex could use separate Tor circuits for connections from different profiles (transport isolation) only if SOCKS is enabled in the AppVM or Its just for Android?
Relevant point is here:
-
GitHub Issues - Make possible using .onion hosts with SOCKS unset for Tor-routed setups #5034
-
GitHub Issues - [Feature]: Protection Against Correlation Attacks through a “Sync” button #3197
Simplex Documentation:
https://simplex.chat/blog/20220901-simplex-chat-v3.2-incognito-mode.html#incognito-mode :
“We have released support for using SOCKS proxy to access messaging servers via Tor, (…) It means that while your IP address was protected from the server, the whole Tor circuit could have been observed by some actors, and for some communication scenarios it is not desirable.
(…) all servers provided by SimpleX Chat now have dual addresses (one public and one .onion), and you can have your own servers available via two addresses as well (…) you should use the same format for the addresses of your servers.”
https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation :
“Even when you create different chat profiles you are still connecting to your contacts via the same device. Transport isolation, by default, makes connections belonging to the same profile use different TCP sessions, so while the server sees the same IP address, it doesn’t see it as the same client connection. If you are connecting via Tor using SOCKS proxy (e.g. Orbot app on Android) not only the app will use different TCP sessions, it will also use separate Tor circuits for connections from different profiles, preventing the servers and network observers seeing this as traffic coming from the same device.”
Simplex 6.3.3 Default Configuration:
About the script, Im almost there… Entries is commig, work-in-progress
#!/bin/bash
#######################################################################
# File Name : whonix-simplex-dvm.sh
# Description : This script creates a SimpleX qube and template based
# in Whonix DisposableVM template just with persistent
# identity across disposables. It downloads and verify
# the hash of the last SimpleX AppImage and uses sys-
# whonix NetVM. For ease of use the script aggregates
# shortcuts to application menu.
# Dependencies : curl
# Usage : • Transfer this script from appvm to dom0 with:
# [user@dom0 ~]$ qvm-run --pass-io appvm 'cat ~/whonix-simplex-dvm.sh' > ~/whonix-simplex-dvm.sh
# • Make the script executable with:
# [user@dom0 ~]$ chmod +x ~/whonix-simplex-dvm.sh
# • Run the script with:
# [user@dom0 ~]$ bash ~/whonix-simplex-dvm.sh
# • To update, run the script in whonix-simplex-template:
# bash ~/update-whonix-simplex-dvm.sh
# Author : Me and the bois
# License : Free of charge, no warranty
#######################################################################
# Safety check
set -eu
# Define Variables
APP_NAME="simplex-chat.AppImage"
APP_NAME_MENU="SimpleX Chat"
BASE_TEMPLATE="whonix-workstation-17"
DISP_TEMPLATE_APPVM="whonix-simplex-template"
DISPVM_INSTANCE="whonix-simplex-dvm"
NET_VM="sys-whonix"
DOWNLOAD_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"
SIMPLEX_PERSISTENT_DIR="/rw/bind-dirs/home/user/.config/simplex"
SIMPLEX_CONFIG_DIR="$HOME/.config/simplex"
# Verify base template exists
if ! qvm-check "$BASE_TEMPLATE" >/dev/null 2>&1; then
echo -e "\nError: Base template $BASE_TEMPLATE not found!" >&2
exit 1
fi
# Update base template using qubesctl
echo -e "\nUpdating base template..."
sudo qubesctl --show-output --skip-dom0 --targets="$BASE_TEMPLATE" state.sls update.qubes-vm
# Create new AppVM based on Whonix template
echo -e "\nCreating $DISP_TEMPLATE_APPVM based on $BASE_TEMPLATE..."
qvm-create --class AppVM --property template="$BASE_TEMPLATE" --property label=gray "$DISP_TEMPLATE_APPVM"
echo -e "\nShutting down $DISP_TEMPLATE_APPVM to save changes..."
qvm-shutdown --wait "$DISP_TEMPLATE_APPVM"
# Configure it as DisposableVM template
echo -e "\nConfiguring $DISP_TEMPLATE_APPVM as DisposableVM template..."
qvm-prefs "$DISP_TEMPLATE_APPVM" template_for_dispvms True
qvm-prefs "$DISP_TEMPLATE_APPVM" netvm "$NET_VM"
qvm-features "$DISP_TEMPLATE_APPVM" appmenus-dispvm 1
# Install dependencies for new template
echo -e "\nInstalling dependencies..."
qvm-run -u root "$DISP_TEMPLATE_APPVM" 'apt update -y && apt install -y curl'
# Setup persistent storage using bind-dirs
echo -e "\nConfiguring persistent storage with bind-dirs..."
qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p '$SIMPLEX_PERSISTENT_DIR' && \
chown -R user:user '/rw/bind-dirs/home/user' && \
mkdir -p /rw/config/qubes-bind-dirs.d && \
echo '/home/user/.config/simplex' > /rw/config/qubes-bind-dirs.d/50_simplex.conf"
# Download function
download_appimage() {
echo -e "\nFetching release info from GitHub..."
# Get release JSON with --pass-io for reliable transfer
RELEASE_JSON=$(qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "curl -sL '$DOWNLOAD_URL'")
# Extract download URL (more robust parsing)
DOWNLOAD_URL=$(echo -e "\n$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' | \
grep -i 'simplex-desktop.*x86_64.*\.AppImage' | \
cut -d '"' -f 4 | head -n 1)
# Fallback to any AppImage if specific one not found
if [[ -z "$DOWNLOAD_URL" ]]; then
DOWNLOAD_URL=$(echo -e "\n$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' | \
grep -i '\.AppImage"' | cut -d '"' -f 4 | head -n 1)
fi
if [[ -z "$DOWNLOAD_URL" ]]; then
echo -e "\nError: Could not find AppImage download URL in:" >&2
echo -e "\n$RELEASE_JSON" >&2
exit 1
fi
echo -e "\nDownloading AppImage from: $DOWNLOAD_URL"
if ! qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "curl -L -o '$HOME/$APP_NAME' '$DOWNLOAD_URL'"; then
echo -e "\nDownload failed!" >&2
exit 1
fi
qvm-run "$DISP_TEMPLATE_APPVM" "chmod +x '$HOME/$APP_NAME'"
}
download_appimage
# Hash verification function
verify_hash() {
echo -e "\nExtracting expected hash from release info..."
# Get the release body text that contains the hashes
local release_body=$(echo "$RELEASE_JSON" | grep -zoP '"body":\s*".*?"' | cut -d'"' -f4 | tr -d '\0')
# Extract ONLY the AppImage hash from the release body
local expected_hash=$(echo "$release_body" | grep -o "SHA2-256(simplex-desktop-x86_64.AppImage)= [a-f0-9]*" | cut -d' ' -f2)
if [[ -z "$expected_hash" ]]; then
echo "Error: Could not extract AppImage hash from release info!" >&2
exit 1
fi
echo -e "\nCalculating actual hash of downloaded file..."
local actual_hash=$(qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "sha256sum '$HOME/$APP_NAME' | cut -d' ' -f1")
echo -e "\nVerifying hashes..."
echo "Expected: $expected_hash"
echo "Actual: $actual_hash"
if [[ "$expected_hash" != "$actual_hash" ]]; then
echo "ERROR: Hash verification failed!" >&2
exit 1
fi
echo "Hash verification passed"
}
verify_hash
# Setting up entries with correct variable expansion, heredoc parsing and permissions
setup_desktop_entry() {
echo -e "\nSetting up desktop entry and app menu integration..."
# Define paths
local DESKTOP_FILENAME="qubes-dispvm-simplex-chat.desktop"
local DESKTOP_FILE_PATH="/usr/local/share/applications/$DESKTOP_FILENAME"
local MENU_FILE_PATH="/rw/config/xdg/menus/applications-merged/qubes-dispvm.menu"
local MENU_DIR="/rw/config/xdg/menus/applications-merged"
local APPS_DIR="/usr/local/share/applications"
# Create applications directory
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p '$APPS_DIR' && chmod 755 '$APPS_DIR'"; then
echo "ERROR: Failed to create applications directory" >&2
exit 1
fi
# Create menu directory in persistent storage
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p '$MENU_DIR' && chmod 755 '$MENU_DIR'"; then
echo "ERROR: Failed to create menu directory" >&2
exit 1
fi
# Configure bind-dirs using a different approach
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p /rw/config/qubes-bind-dirs.d"; then
echo "ERROR: Failed to create qubes-bind-dirs.d" >&2
exit 1
fi
# Create the bind-dirs config file using cat with a here-document
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "cat > /rw/config/qubes-bind-dirs.d/50_menus.conf" <<'BIND_EOF'
binds+=( '/etc/xdg/menus' )
BIND_EOF
then
echo "ERROR: Failed to configure bind-dirs" >&2
exit 1
fi
# Create desktop file
local DESKTOP_CONTENT="[Desktop Entry]
Version=1.0
Type=Application
Name=SimpleX Chat (DispVM)
GenericName=Secure Messenger
Comment=Private and secure messaging with SimpleX
Exec=qvm-run --dispvm=$DISP_TEMPLATE_APPVM \\\"\\\$HOME/$APP_NAME\\\"
Icon=internet
Terminal=false
Categories=Network;InstantMessaging;X-Qubes-VM;
StartupNotify=true
X-Qubes-VMName=$DISPVM_INSTANCE"
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "echo '$DESKTOP_CONTENT' > '$DESKTOP_FILE_PATH'"; then
echo "ERROR: Failed to create desktop file" >&2
exit 1
fi
# Set desktop file permissions
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "chown root:root '$DESKTOP_FILE_PATH' && chmod 644 '$DESKTOP_FILE_PATH'"; then
echo "ERROR: Failed to set desktop file permissions" >&2
exit 1
fi
# Create menu file
local MENU_CONTENT='<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
<Menu>
<Name>Applications</Name>
<Menu>
<Name>DisposableVMs</Name>
<Directory>qubes-dispvm.directory</Directory>
<Include>
<Filename>qubes-dispvm-simplex-chat.desktop</Filename>
</Include>
</Menu>
</Menu>'
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "echo '$MENU_CONTENT' > '$MENU_FILE_PATH'"; then
echo "ERROR: Failed to create menu file" >&2
exit 1
fi
# Create symlink - simplified approach
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p /etc/xdg/menus/applications-merged"; then
echo "ERROR: Failed to create applications-merged directory" >&2
exit 1
fi
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "ln -sf '$MENU_FILE_PATH' '/etc/xdg/menus/applications-merged/qubes-dispvm.menu'"; then
echo "ERROR: Failed to create menu symlink" >&2
exit 1
fi
# Update desktop database
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "update-desktop-database '$APPS_DIR'"; then
echo "ERROR: Failed to update desktop database" >&2
exit 1
fi
# Verification
echo -e "\nVerification:"
qvm-run -p "$DISP_TEMPLATE_APPVM" "ls -l '$DESKTOP_FILE_PATH' '$MENU_FILE_PATH'"
qvm-run -p "$DISP_TEMPLATE_APPVM" "ls -l /etc/xdg/menus/applications-merged/"
qvm-run -p "$DISP_TEMPLATE_APPVM" "cat /rw/config/qubes-bind-dirs.d/50_menus.conf"
echo -e "\nDesktop entry and app menu setup complete in template!"
}
setup_desktop_entry
create_updater_script() {
echo -e "\nCreating updater in template...update only in the template!"
# Generate updater script content
local UPDATER_CONTENT=$(cat <<'UP_EOF'
#!/bin/bash
# Update only in the template!
# Variables
APP_NAME="simplex-chat.AppImage"
DOWNLOAD_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"
UP_EOF
)
# Add functions
UPDATER_CONTENT+="\n\n$(declare -f download_appimage | sed 's/^/ /')"
UPDATER_CONTENT+="\n\n$(declare -f verify_hash | sed 's/^/ /')"
UPDATER_CONTENT+="\n\n# Main execution\ndownload_appimage\nverify_hash\n"
UPDATER_CONTENT+='echo -e "\nSimpleX Chat update completed successfully!"'
# Transfer to template with proper permissions
echo "$UPDATER_CONTENT" | qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "
cat > \$HOME/update-simplex.sh &&
chown user:user \$HOME/update-simplex.sh &&
chmod 700 \$HOME/update-simplex.sh
"
echo "Updater script created in $DISP_TEMPLATE_APPVM"
}
create_updater_script
# Finalize
echo -e "\nShutting down $BASE_TEMPLATE..."
qvm-shutdown --wait "$BASE_TEMPLATE"
# Start template to sync menus
echo -e "\nStarting $DISP_TEMPLATE_APPVM to sync menus..."
qvm-start "$DISP_TEMPLATE_APPVM" && sleep 5
# Sync menus while template is running
echo -e "\nSyncing application menus to dom0..."
qvm-sync-appmenus "$DISP_TEMPLATE_APPVM"
# Shutdown template to create disposable
echo -e "\nShutting down $DISP_TEMPLATE_APPVM to create $DISPVM_INSTANCE..."
qvm-shutdown --wait "$DISP_TEMPLATE_APPVM"
# Create disposable VM
echo -e "\nCreating disposable VM..."
qvm-create --property template="$DISP_TEMPLATE_APPVM" --class DispVM --property label=red "$DISPVM_INSTANCE"
qvm-features "$DISPVM_INSTANCE" appmenus-dispvm ''
# Final menu update targeting the disposable VM
echo -e "\nUpdating application menus in dom0..."
qvm-appmenus --update --force "$DISPVM_INSTANCE"
echo -e "\nInstallation complete!"


