wren-linux / extras / tor-browser.sh
#!/bin/bash
# Tor Browser as its own locked-down user, tor-browser, so a hacked page doesn't reach your
# files. It has no password or login, and its own home (/home/tor-browser), where the browser
# lives and updates itself through Tor.
# - It opens inside cage, a tiny Wayland compositor shown as one window in your sway, with a
# private X server: Tor Browser runs on X11 on purpose (less fingerprintable), and on X11 any
# program can read every window and key. A restricted socket (bin/wayland-restrict) keeps it
# from your screen.
# - A firewall rule (data/tor-browser-firewall.nft) keeps it off this computer and your local
# network. It doesn't force the browser through Tor: browser and Tor are one user, so a
# hacked browser could go straight out and show your address. Whonix is what stops that.
# - Nothing crosses by itself. A line of text goes through browser-sandbox clip (Super+Ctrl+b),
# which shows it to you first; files through ~/Browsers/tor-browser.
# - Debian's torbrowser-launcher is installed for the AppArmor profiles it brings, which confine
# the browser and its Tor. The browser itself is the Tor Project's, checked against their key
# (prep/fetch-tor-browser.sh): contrib is turned on for the launcher, and --remove turns it off
# again if this script was what turned it on.
# - Before its first start this writes a user.js into its profile: the Safest security level,
# JavaScript off, and a bridge if you need one (it asks first, then offers obfs4, webtunnel,
# Snowflake or meek), so its very first connection goes through it. Firefox applies user.js at
# every start, so these come back after a restart even if changed in the browser; run this
# again to change them.
# - The Safest level is locked by Tor Browser's own policy file (Browser/distribution/
# policies.json, from data/tor-browser-policies-safest.json): the shield's dialog and
# about:config can't change it, and the browser's updater leaves the file in place. The lock
# stops slips and pages, not code running as the tor-browser user. The javascript.* prefs
# can't be locked that way, so user.js still turns JavaScript off (and sets the same level).
# /usr/bin/torbrowser-launcher, by its full path, still runs as you. A copy you already had there
# is left alone, and --remove asks about it.
# Usage: bash tor-browser.sh [--remove | --manual]
# summary: Tor Browser as its own user, with a bridge if needed
# security: most
# category: web
# status: file:/usr/local/bin/tor-browser apt:wren-tor-browser
# files: lib/browser-sandbox.sh bin/browser-sandbox bin/browser-session data/tor-browser-firewall.nft data/tor-browser-safest.js data/tor-browser-policies-safest.json prep/fetch-tor-browser.sh bin/no-abstract-sockets bin/wren-sandbox bin/wayland-restrict bin/sandbox-net-check
. "$(dirname -- "${BASH_SOURCE[0]}")/../lib.sh"
. "$WREN_DIR/lib/appimage.sh"
. "$WREN_DIR/lib/browser-sandbox.sh"
if [ "${1:-}" = --manual ]; then print_manual; exit; fi
need_user
MARK=/etc/apt/wren-enabled-contrib
CLASSIC=/etc/apt/sources.list
# Debian's own sources: classic lines in sources.list, deb822 stanzas in
# *.sources files that point at debian.org
deb822_files() {
grep -ls 'debian\.org' /etc/apt/sources.list.d/*.sources 2>/dev/null || true
}
enable_contrib() {
local changed=false f
if [ -f "$CLASSIC" ] && grep -Eq '^deb(-src)?[[:space:]].*debian.*[[:space:]]main([[:space:]]|$)' "$CLASSIC" &&
grep -E '^deb(-src)?[[:space:]].*debian' "$CLASSIC" | grep -vq '[[:space:]]contrib\b'; then
sudo sed -i -E '/^deb(-src)?[[:space:]].*debian/ { /[[:space:]]contrib([[:space:]]|$)/! s/[[:space:]]main([[:space:]]|$)/ main contrib\1/ }' "$CLASSIC"
changed=true
fi
for f in $(deb822_files); do
if grep -q '^Components:' "$f" && grep '^Components:' "$f" | grep -vq '\bcontrib\b'; then
sudo sed -i -E '/^Components:/ { /\bcontrib\b/! s/$/ contrib/ }' "$f"
changed=true
fi
done
if $changed; then
sudo touch "$MARK"
note "Turned on Debian's contrib section."
else
note "contrib is already on."
fi
}
disable_contrib() {
local f
[ -e "$MARK" ] || { note "contrib was on before this script; leaving it."; return 0; }
if [ -f "$CLASSIC" ]; then sudo sed -i -E 's/ main contrib([[:space:]]|$)/ main\1/' "$CLASSIC"; fi
for f in $(deb822_files); do sudo sed -i -E '/^Components:/ s/ contrib\b//' "$f"; done
sudo rm -f "$MARK"
sudo apt-get update >/dev/null
note "Turned Debian's contrib section off again."
}
NAME=tor-browser
# Where it lives. (Under /home, in the folder the launcher would use: that is where its AppArmor
# profile attaches. It isn't confined anywhere else.)
TB_BASE=/home/$NAME/.local/share/torbrowser/tbb/$(uname -m)
TBB=$TB_BASE/tor-browser
PROFILE=$TBB/Browser/TorBrowser/Data/Browser/profile.default
USERJS=$PROFILE/user.js
# A copy from before it had a user of its own, which ran as you
OLD_USERJS=$HOME/.local/share/torbrowser/tbb/$(uname -m)/tor-browser/Browser/TorBrowser/Data/Browser/profile.default/user.js
POLICIES_SRC=$WREN_DIR/data/tor-browser-policies-safest.json
TB_PREFS_SRC=$WREN_DIR/data/tor-browser-safest.js
TB_LEVEL_NOTES=("Tor Browser starts at the Safest level with JavaScript off. The level is locked:"
"the shield's dialog and about:config can't change it (Tor Browser's own policy file).")
OFFLINE="$OFFLINE_DIR/torbrowser"
if [ "${1:-}" = --remove ]; then
browser_remove "$NAME"
sudo rm -f /usr/local/bin/torbrowser-launcher
remove_wayland_restrict
remove_no_abstract
remove_sandbox_lib
remove_net_check
# The AppArmor fixes are shared with tor-browser-unsafe: only undo them if it is gone too
# (browser_remove above already took this one's command out of /usr/local/bin)
if ! tor_browser_sibling "$NAME"; then
unfix_apparmor_browser
unfix_apparmor_dns
apt_purge torbrowser-launcher
else
note "tor-browser-unsafe is still installed, so Tor Browser's shared pieces stay."
fi
disable_contrib
# The copy from before it had a user of its own
if [ -d "$HOME/.local/share/torbrowser" ] &&
ask "Also delete the older Tor Browser that ran as you, and its bookmarks (~/.local/share/torbrowser)?" n; then
rm -rf "$HOME/.local/share/torbrowser" "$HOME/.cache/torbrowser" "$HOME/.config/torbrowser"
note "Deleted."
elif [ -f "$OLD_USERJS" ]; then
sed -i "\|^$JS_BEGIN\$|,\|^$JS_END\$|d" "$OLD_USERJS"
note "Took the Wren Linux settings out of $OLD_USERJS; that copy keeps their last values."
fi
exit
fi
say "Debian contrib section"
enable_contrib
# A fresh update, so apt sees the contrib packages
unset WREN_APT_UPDATED
say "Installing what it needs"
# torbrowser-launcher: the AppArmor profiles; cage, xwayland: its window and private X server;
# xclip, wl-clipboard: browser-sandbox clip; acl: sharing the socket and the folder; nftables,
# python3: the firewall and its check, wayland-restrict; the pipewire ones: its sound server, and
# pw-cat, the player of yours that reads its one pipe; xz-utils: unpacking it
need_xwayland
apt_install torbrowser-launcher xz-utils cage xclip wl-clipboard acl nftables python3 \
pipewire-bin pipewire-pulse wireplumber
fix_apparmor_dns
say "The $NAME user"
browser_user "$NAME" "Wren Linux sandboxed Tor Browser"
fix_apparmor_browser
browser_files "$NAME"
say "The commands"
install_bin wayland-restrict
install_no_abstract
install_sandbox_lib
install_net_check
browser_commands "$NAME"
# By the name you may know it from: /usr/local/bin comes first
sudo ln -sfn browser-sandbox /usr/local/bin/torbrowser-launcher
browser_allow "$NAME"
say "Keeping $NAME off this computer and your network"
browser_firewall "$NAME"
browser_clip_key
say "Tor Browser"
install_browser
say "Tor Browser's security level and bridges"
prepare_profile
if [ -d "$HOME/.local/share/torbrowser/tbb" ]; then
note "You also have a Tor Browser that runs as you, in ~/.local/share/torbrowser: left alone, and no"
note "longer started by name. To keep its bookmarks, export them to a file there, and import that"
note "from ~/Browsers/tor-browser."
fi
note "Run it with: tor-browser (or Super+d)"
note "Text in or out: Super+Ctrl+b, which shows it first. Files: ~/Browsers/tor-browser."