wren-linux / docs / extras / whonix.md (as a page)
# Whonix (`extras/whonix.sh`)
## SYNOPSIS
```
wrenctl install whonix download (if needed), check and install both VMs (asks about a bridge)
wrenctl install whonix --bridges installed already: choose the Gateway's bridge again
wrenctl install whonix --fetch only download and check it, for later
wrenctl remove whonix remove the VMs (asks before deleting their disks)
wrenctl manual whonix print the manual steps below
whonix start both VMs and open both, each on an empty workspace (or Super+d)
whonix gateway start the Gateway and open it (Tor settings, bridges)
whonix stop shut both down, right away and cleanly: save your work inside first
whonix stop --force the same, and one still running after 60 seconds is switched off
whonix live the same as whonix, but nothing done in the Workstation is kept after it shuts down
whonix update update both VMs and Tor Browser, keeping your bookmarks; they power off when done
```
Whonix runs Tor in one virtual machine, the Gateway, and your programs in another, the Workstation,
whose only network is the Gateway, so even a hacked browser in the Workstation can't learn your real
address.
Everything runs as your user under KVM (libvirt's `qemu:///session`), with no root daemon or host
network setup. The download (about 1.9 GB) is checked against Whonix's signing key, pinned in
`pins.sh` (`WHONIX_FPR`), and kept in `offline/whonix`.
Before the first boot, Wren Linux writes into the images, without mounting them (with qemu-img and
debugfs): in both, a rule for acpid so that the power button, which `whonix stop` presses, shuts the
VM down cleanly (left to Whonix's desktop, it opens a dialog); a bridge for the Gateway if you need
one (obfs4 or webtunnel lines you paste, or Snowflake), so its very first connection uses it, or Tor
kept off until you turn it on (`--bridges` chooses again later); and in the Workstation, Tor Browser
at Safest with JavaScript off (`data/tor-browser-safest.js`), the level locked by the browser's own
policy file (`data/tor-browser-policies-safest.json`: the slider won't move, about:config is
blocked), and the Feather wallet for Monero:
Feather's own build for Tails and Whonix, which has no Tor of its own and uses the Gateway's,
checked against the hash list signed by Feather's key (`FEATHER_FPR`), as the Feather extra checks
it.
Electrum, for Bitcoin, comes with Whonix. If Feather can't be downloaded or checked, Whonix is
installed without it.
For wallets, Whonix is the safest choice Wren Linux has: a wallet in the Workstation can't reach
this computer's files or its real address, even if it is hacked.
The Gateway's way out is a network card of Wren Linux's, joined to a passt that `whonix` starts
before the Gateway.
Whonix's own file gives it libvirt's, and libvirt can't give passt any option: with passt's defaults
the Gateway's address for your router leads to this computer's own loopback instead (every port of
it: Tor's port, the VMs' consoles, a local server), and nothing answers DNS, so Snowflake never gets
past "broker failure" at 10%.
Wren Linux's passt has Whonix's fixed addresses (the Gateway 10.0.2.15, its gateway 10.0.2.2), IPv4
only, no DHCP, nothing of this computer mapped in (`--no-map-gw`), and one address, 10.0.2.3, where
only DNS goes on, to the resolver this computer uses (Unbound with secure DNS on, so encrypted).
The Gateway's own DNS stays off, as Whonix ships it, except for a bridge that has to look names up
(Snowflake, webtunnel): then its `/etc/resolv.conf.whonix` gets `nameserver 10.0.2.3`. webtunnel is
experimental here: Whonix ships no program for it, so Wren Linux installs lyrebird on this computer
from the Tor Project's repository (its key pinned, `TORPROJECT_FPR`, and an apt pin that lets
nothing else come from there), where its updates install themselves, and copies it into the Gateway
as `/usr/bin/lyrebird`; Tor's AppArmor profile in Whonix 18 already lets Tor run it.
`whonix update` updates everything in one go, over Tor, with both VMs shut down first. It starts
the Gateway, which updates and powers off; then starts it as usual, for the Workstation to update
its packages and Tor Browser. You see it in their windows, and both end up off.
Nothing starts by itself: it runs when you run it. A week after the last update that worked, the bar
shows `whonix update` (a click runs it), and from then on `whonix stop` asks whether to update before
shutting down. Only an update that worked starts the week again.
How the VMs know: `whonix update` starts each with a note in its firmware (an SMBIOS OEM string, for a
service written into the images at install, `data/whonix-update.service`). The VM's saved settings
aren't changed, so with no update running a VM boots as ever, and the service does nothing.
Whonix's `update-torbrowser` is a downloader rather than an updater: it deletes the browser's folder
and puts a new one there, which would lose your bookmarks and downloads. So
`data/whonix-update-browser`, running as the user and never as root, moves the old folder aside, runs
it, and gives the new one your downloads as they are and your bookmarks rebuilt, nothing else of the
old profile: a hacked page can write to anything in a profile (settings, add-ons, a changed
database), and a profile carried over would keep that. The bookmarks are read from a copy of the old
database and written again as a plain `bookmarks.html` of checked web addresses and titles
(`javascript:`, `data:` and `place:` entries, tags and anything with a quote or markup in the
address are left out); Firefox imports it at its first start. Once the bookmarks are safely out,
the rest of the old profile (history, cookies, settings, whatever a hacked page wrote) is deleted;
only if the bookmarks couldn't be read is the old profile kept, in
`~/.tb/left-from-browser-VERSION/profile`, out of the folder that runs, so they can still be got at.
**Lost on purpose:** per-site NoScript choices, saved onion keys and any other setting. The new
browser is put at the Safest level with JavaScript off by the update itself, written into its own
profile (`user.js`, from the settings Wren Linux put in the image), and the level is locked by the
browser's policy file (`policies.json`, likewise), not left to Whonix's start-up; if those files are
missing from the image, the update says so plainly and you set Safest by hand.
It runs it only for a newer version: with
no one to ask, it would install an older one too, which could be an attack. If anything fails, the old
folder goes back whole, and a power cut is put right by the next update. Anything else in the old
folder that the new one doesn't have, such as a page you saved beside Downloads, is kept in
`~/.tb/left-from-browser-VERSION`. A download cut off (`update-torbrowser` status 17, which a
bridge's connection can cause) is tried again, up to four times in all, with `update-torbrowser
--resume`; the flag is Whonix's own advice and has been tried only with a pretend `update-torbrowser`.
Any other failure ends the update and puts the old browser back.
The maintainer's config files replace ones that were changed (`--force-confnew`, as Whonix's own
`apt-get-noninteractive` does): a security fix in a config file reaches you, where keeping the old one
would have kept it out without a word. A file you edited by hand in the VM is replaced too; dpkg keeps
yours beside it as `.dpkg-old`. Put your own settings in `/usr/local/etc`, where Whonix looks for them and
an update leaves them alone. The one packaged file Wren Linux edits, the Gateway's
`/etc/resolv.conf.whonix` (its nameserver line, for a bridge given as a name), is put back after the
upgrade, if it was on before; if that ever fails, Tor can't resolve a bridge's name and doesn't
connect, which is the safe way to fail (`wrenctl install whonix --bridges` sets it again).
Installed before this? Running `wrenctl install whonix` again (with the Gateway shut down) gives it
the new card; `--bridges` then sets the DNS for the bridge you choose.
## LIMITS
- A bug in KVM or QEMU could reach this computer from a VM.
- What you type into a site still identifies you if it's about you; Tor hides the address, not the
person.
- `whonix stop` doesn't ask inside the VMs: what isn't saved there is lost. The same goes for the
power button of a VM's own window. Tried here on Whonix 18.2.4.2, without a Tor connection or
programs open: both VMs were off in 5 seconds, their disks clean.
- VMs installed before this have no such rule: `whonix stop` opens their shutdown dialog and leaves
them running until you choose Shutdown there.
`--bridges` gives the Gateway the rule, but nothing gives it to a Workstation that is already
installed: back up what you keep in it, then `wrenctl remove whonix` (saying yes to deleting the
disks) and `wrenctl install whonix`.
- `whonix live` forgets what is done in the Workstation, not the Gateway: one that forgot would pick
new Tor guards each time, which makes you easier to find. Until it shuts down, what changes is
in a file beside its disk. That is deleted, not wiped, and a power cut leaves it until the next
start, so only your disk encryption protects it: it isn't memory-only, like Tails.
- `whonix stop --force` switches off a VM still running after 60 seconds, as if its power button
were held, which can leave its disk needing repair at the next boot.
- A VM still starting doesn't hear its power button (a Workstation here, for about its first 70
seconds), so `whonix stop` presses it again every 5 seconds: right after a start it takes longer.
- Wren Linux doesn't update Feather in the Workstation after the install: Feather says when a newer
version is out, and its guide for Whonix (docs.featherwallet.org) shows how to replace it. Whonix
updates Electrum with its other packages.
- Feather inside the Workstation hasn't been tried in a real Whonix VM yet: the files are put in
place and checked, but not run there.
- The Workstation's Tor Browser Safest level is locked by a policy file (the slider won't move,
about:config is blocked). That the policy locks the level was shown on a real Tor Browser 16.0.1
(headless); that Whonix's start-up re-applies it at each browser start (`50_wren.conf`) is checked
by the suite. It hasn't been watched on a real Whonix Workstation's screen.
- `whonix update` was tried on Whonix 18.2.4.2, installed by this script, with real libvirt, each VM
at its 2 GB, in a VM with 5.4 GB of memory and no screen, and no bridge. Both VMs updated over Tor
(24 minutes, with a new kernel). Tor Browser went from 15.0.25 to 16.0, and on a profile made
beforehand a bookmark, a setting, a download and a page saved beside Downloads all survived; Safest
stayed on. (That was with the first version of the update, which carried the whole profile over.
The bookmarks-only version has been tried with a real Tor Browser 16 importing the file it writes,
from a real Firefox database, but not through a real Whonix update: that Safest and JavaScript off
stay on in the new browser has been tried only with a pretend update.) Everything the Gateway sent went to Tor relays in its own list, with no DNS. An ordinary
start afterwards stayed up and ran no update, and the saved settings hadn't changed. The windows
opened and closed in a real sway with no screen, and closing the terminal didn't stop the update.
Tried on a real computer, with a real screen and a webtunnel bridge: a first update whose Tor Browser
download was cut off put the old browser back whole, with a bookmark and a download; the next one
took Tor Browser from 15 to 16 with both still there and Safest on, and `whonix stop` then asked nothing.
Not tried: a real power cut (only stopped by force in a test with pretend programs), a bad signature
on a real download.
- An update doesn't clean a VM that is already hacked: something running as root inside it could
fake the result, or undo the update. What a VM says only decides whether the reminder stays.
- `whonix update` connects to Tor, as Whonix does: your network sees that as it sees any start of
the Gateway, through your bridge if you set one. With Tor held off until you choose a bridge, the
update fails after about 20 minutes, without connecting.
- Whonix installed before `whonix update` can't use it, and shows no reminder: nothing inside the VMs
could answer. Back up what you keep in them, then `wrenctl remove whonix` (saying yes to deleting the
disks) and `wrenctl install whonix`. Until then, update inside the VMs.
- Feather and webtunnel's lyrebird aren't updated by it: they aren't from apt. Feather says when a
newer version is out, and `whonix` says when lyrebird is.
- Tor Browser's own updater also runs while you browse, and keeps your things too. `whonix update`
is for when it hasn't had the chance.
- An update stopped from outside (this computer shut down, `whonix stop --force`) can leave packages half
installed. The next `whonix update` finishes them first, if the VM still boots.
- With Snowflake, the Gateway looks up its broker and STUN servers through this computer's resolver
(encrypted, with secure DNS on); the resolver sees those names, and your network sees you reach
them, as with Snowflake anywhere. Whonix's page on Gateway DNS knows of no risk in having it on.
- Started any other way than `whonix` (virt-manager, `virsh start`), the Gateway has no network: its
passt isn't running.
- webtunnel's copy of lyrebird in the Gateway isn't updated by Whonix. `whonix` says when the one on
this computer is newer; then `whonix stop` and `wrenctl install whonix --bridges` copy it in
again. Until you do, the Gateway runs the older one.
- A disk switched off without shutting down (as `whonix stop --force` can do) isn't edited:
`--bridges` asks you to start the Gateway once and shut it down from inside first, so its journal
is replayed.
- The Gateway's own network was tried here with Whonix 18.2.4.2 under passt (its outside card is
eth0, Tor reaches its relays), and Snowflake and a real webtunnel bridge through it connected on a
real install.
## MANUAL INSTALL
```
# KVM must be on: this must list /dev/kvm (if not, turn on VT-x / AMD-V in the firmware settings)
ls -l /dev/kvm
# KVM, libvirt and virt-manager (from whonix.org/wiki/KVM, for Debian trixie),
# and the tools to unpack and edit the images
sudo apt install --no-install-recommends qemu-system-x86 qemu-system-gui qemu-system-modules-spice qemu-utils \
libvirt-daemon libvirt-daemon-driver-qemu libvirt-daemon-driver-storage libvirt-daemon-driver-nodedev \
libvirt-clients virt-manager gir1.2-spiceclientgtk-3.0 passt ovmf swtpm swtpm-tools \
xz-utils e2fsprogs fdisk acl
# Your user may use KVM and libvirt (in full from your next login)
sudo adduser "$(id -un)" kvm
sudo adduser "$(id -un)" libvirt
# No core dumps, and no limits libvirt can't set as a normal user
mkdir -p ~/.config/libvirt
printf 'max_core = 0\nmax_processes = 0\nmax_files = 0\n' >~/.config/libvirt/qemu.conf
# The newest version: the highest number on whonix.org's KVM page
wget -qO- https://www.whonix.org/wiki/KVM | grep -o 'download/libvirt/[0-9.]*/Whonix-LXQt-[0-9.]*\.Intel_AMD64' | sort -uV | tail -1
V=18.2.4.2
F=Whonix-LXQt-$V.Intel_AMD64.qcow2.libvirt.xz
mkdir -p ~/whonix-download && cd ~/whonix-download
# The image (about 1.9 GB), its signature and the signing key
wget -c https://www.whonix.org/download/libvirt/$V/$F https://www.whonix.org/download/libvirt/$V/$F.asc
wget -O derivative.asc @WHONIX_KEY_URL@
# The key, in a keyring of its own. This must show the fingerprint @WHONIX_FPR@
mkdir -m 700 gpg
gpg --homedir gpg --import derivative.asc
gpg --homedir gpg --fingerprint @WHONIX_FPR@
# The image is signed by it: this must say "Good signature" with
# "Primary key fingerprint" @WHONIX_FPR@. Stop here if not.
gpg --homedir gpg --verify $F.asc $F
# Unpack it, and read Whonix's license and disclaimer before going on
tar -xSf $F
less WHONIX_BINARY_LICENSE_AGREEMENT WHONIX_DISCLAIMER
mv Whonix-Gateway-*.qcow2 gw.qcow2 && mv Whonix-Workstation-*.qcow2 ws.qcow2
# Files to put into the images before their first boot:
# the Workstation's Tor Browser at Safest, JavaScript off, the level locked by its policy file
cp @WREN_DIR@/data/tor-browser-safest.js user.js
cp @WREN_DIR@/data/whonix-torbrowser.conf 50_wren.conf
cp @WREN_DIR@/data/tor-browser-policies-safest.json policies.json
# Feather, its build for Tails and Whonix (the newest version: the tag in the
# "Location:" line, e.g. 2.9.1), checked as extras/feather-wallet.sh did
wget --max-redirect=0 -S -O /dev/null https://github.com/feather-wallet/feather/releases/latest 2>&1 | grep -i location
VER=2.9.1
wget https://featherwallet.org/files/releases/hashes-$VER.txt https://featherwallet.org/files/releases/linux-appimage-a/feather-$VER-a.AppImage
mkdir -m 700 fgpg
wget -O feather.asc @FEATHER_KEY_URL@
gpg --homedir fgpg --import feather.asc
# This must say "Good signature" with "Primary key fingerprint"
# @FEATHER_FPR@. Stop here if not.
gpg --homedir fgpg --output sums.txt --decrypt hashes-$VER.txt
# This must print "OK"
awk -v n="feather-$VER-a.AppImage" '$2 == n || $2 == "*" n { print $1 " " n }' sums.txt | sha256sum -c
mv feather-$VER-a.AppImage feather.AppImage
cp @WREN_DIR@/data/whonix-feather @WREN_DIR@/data/whonix-feather.desktop .
# A bridge for the Gateway, if you need one, in 50_user.conf: obfs4 lines
# from bridges.torproject.org (for Snowflake, put "ClientTransportPlugin
# snowflake exec /usr/bin/snowflake-client" and the Snowflake "Bridge" lines
# from data/snowflake-bridges.txt instead)
printf '%s\n' 'UseBridges 1' 'ClientTransportPlugin obfs4 exec /usr/bin/obfs4proxy' \
'Bridge obfs4 1.2.3.4:443 FINGERPRINT cert=... iat-mode=0' >50_user.conf
# Only for Snowflake: the Gateway's own DNS, at 10.0.2.3 (passt hands it to this
# computer's resolver). Take Whonix's file out of the image first: copy it from
# "debugfs -R 'cat /etc/resolv.conf.whonix'" (see put below), then
# sed -i 's/^#nameserver 10\.0\.2\.3$/nameserver 10.0.2.3/' resolv.conf.whonix
# and put it back as /etc/resolv.conf.whonix (for webtunnel too)
# Only for webtunnel: lyrebird, from the Tor Project's repository (as
# extras/apt-tor.sh adds it: key @TORPROJECT_FPR@, pinned to lyrebird only),
# and in 50_user.conf "ClientTransportPlugin webtunnel exec /usr/bin/lyrebird"
# with your "Bridge webtunnel ..." lines; then, with put below:
# put gw.qcow2 /usr/bin/lyrebird /usr/bin/lyrebird 755
# put IMAGE FILE DEST [MODE]: write FILE into the image's root file system as
# DEST (root's, mode 0644 or MODE), checked, without mounting it
put() {
qemu-img convert -O raw "$1" "$1.raw"
start=$(sfdisk -d "$1.raw" | sed -n 's/.*start= *\([0-9]*\),.*name="primary".*/\1/p')
fs="$1.raw?offset=$((start * 512))"
dir=$(dirname "$3")
debugfs -w -R "mkdir $dir" "$fs"
debugfs -w -f - "$fs" <<EOF
sif $dir uid 0
sif $dir gid 0
sif $dir mode 040755
rm $3
write $2 $3
sif $3 uid 0
sif $3 gid 0
sif $3 mode 0100${4:-644}
EOF
# It must be there, as written, and the file system must check out;
# if not, the image is left as it was
if ! debugfs -R "cat $3" "$fs" | cmp -s - "$2" || ! e2fsck -fn "$fs"; then
echo "Couldn't write $3 into $1; it is unchanged." >&2
rm "$1.raw"
return 1
fi
# (a write that fails, on a full disk, must never take the image's place)
qemu-img convert -O qcow2 -o cluster_size=2M,extended_l2=on "$1.raw" "$1.new" ||
{ echo "Couldn't write $1 back; it is unchanged." >&2; rm -f "$1.raw" "$1.new"; return 1; }
rm "$1.raw" && mv "$1.new" "$1"
}
put ws.qcow2 @WREN_DIR@/data/whonix-power-button /etc/acpi/events/wren-power-button
put gw.qcow2 @WREN_DIR@/data/whonix-power-button /etc/acpi/events/wren-power-button
put ws.qcow2 user.js /usr/local/etc/torbrowser.d/user.js
put ws.qcow2 50_wren.conf /usr/local/etc/torbrowser.d/50_wren.conf
put ws.qcow2 policies.json /usr/local/etc/torbrowser.d/policies.json
put ws.qcow2 feather.AppImage /usr/local/lib/feather/feather.AppImage 755
put ws.qcow2 whonix-feather /usr/local/bin/feather 755
put ws.qcow2 whonix-feather.desktop /usr/local/share/applications/feather.desktop
put gw.qcow2 50_user.conf /usr/local/etc/torrc.d/50_user.conf
# What whonix update runs, in both (the service does nothing without the host's note)
for i in gw.qcow2 ws.qcow2; do
put $i @WREN_DIR@/data/whonix-update /usr/local/libexec/wren-update 755
put $i @WREN_DIR@/data/whonix-update.service /etc/systemd/system/wren-update.service
put $i @WREN_DIR@/data/whonix-update-wants.conf /etc/systemd/system/multi-user.target.d/wren-update.conf
done
put ws.qcow2 @WREN_DIR@/data/whonix-update-browser /usr/local/libexec/wren-update-browser 755
# (each must end without "Couldn't write"; stop here if one does)
# The VMs, as your user: the storage pool, the disks, the definitions
mkdir -p ~/.local/share/libvirt/images
virsh -c qemu:///session pool-define-as default dir --target ~/.local/share/libvirt/images
virsh -c qemu:///session pool-autostart default
virsh -c qemu:///session pool-start default
mv gw.qcow2 ~/.local/share/libvirt/images/Whonix-Gateway.qcow2
mv ws.qcow2 ~/.local/share/libvirt/images/Whonix-Workstation.qcow2
virsh -c qemu:///session pool-refresh default
# The Gateway's outside card: in Whonix-Gateway.xml, delete the block
# <interface type='user'> ... </interface>, add xmlns:qemu to its first line, so
# it reads <domain xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0' type='kvm'>,
# and put this just before </domain> (with your home folder in the path; slot
# 0x1e must be free on bus 0x00: virsh dumpxml shows the slots in use)
# <qemu:commandline>
# <qemu:arg value='-netdev'/>
# <qemu:arg value='{"type": "stream", "id": "wren-gw-netdev", "server": false, "reconnect-ms": 1000, "addr": {"type": "unix", "path": "/home/YOU/.local/state/wren/whonix-gateway-net.sock"}}'/>
# <qemu:arg value='-device'/>
# <qemu:arg value='{"driver": "virtio-net-pci", "netdev": "wren-gw-netdev", "id": "wren-gw-net", "mac": "@GW_NET_MAC@", "bus": "pcie.0", "addr": "0x1e"}'/>
# </qemu:commandline>
virsh -c qemu:///session define Whonix-Gateway.xml
virsh -c qemu:///session define Whonix-Workstation.xml
# Its network, before every start of the Gateway (the whonix command does this)
mkdir -p ~/.local/state/wren && chmod 700 ~/.local/state/wren
passt --quiet --one-off --socket ~/.local/state/wren/whonix-gateway-net.sock \
--pid ~/.local/state/wren/whonix-gateway-net.pid --ipv4-only --address 10.0.2.15 \
--netmask 24 --gateway 10.0.2.2 --no-map-gw --dns-forward 10.0.2.3 --no-dhcp
# The whonix command
install -D -m 0755 @WREN_DIR@/bin/whonix ~/.local/bin/whonix
# The VMs' windows don't keep sway's keys (Super+1 and the like still switch workspaces)
printf '%s\n' 'for_window [app_id="^virt-manager$"] shortcuts_inhibitor disable' >~/.config/sway/config.d/whonix-keys.conf
swaymsg reload
# When the last update worked: the install counts (whonix update needs this file)
touch ~/.local/state/wren/whonix-updated
cd ~ && rm -rf ~/whonix-download
whonix
```
To undo:
```
# Switch the VMs off and forget them
virsh -c qemu:///session destroy Whonix-Workstation
virsh -c qemu:///session destroy Whonix-Gateway
virsh -c qemu:///session undefine Whonix-Workstation
virsh -c qemu:///session undefine Whonix-Gateway
# Their disks, and everything saved inside the VMs
rm ~/.local/share/libvirt/images/Whonix-Gateway.qcow2 ~/.local/share/libvirt/images/Whonix-Workstation.qcow2
# The command
rm ~/.local/bin/whonix ~/.local/state/wren/whonix-updated ~/.config/sway/config.d/whonix-keys.conf
# KVM, libvirt and virt-manager, marked "automatic": autoremove takes them out
# only if nothing (mockvps-kvm, claude-vm, the onion site) needs them
sudo apt-mark auto qemu-system-x86 qemu-system-gui qemu-system-modules-spice qemu-utils libvirt-daemon \
libvirt-daemon-driver-qemu libvirt-daemon-driver-storage libvirt-daemon-driver-nodedev libvirt-clients \
virt-manager gir1.2-spiceclientgtk-3.0 passt ovmf swtpm swtpm-tools
sudo apt autoremove --purge
```