Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions apps/desktop/launcher/tronbrowser
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,45 @@ ver_gt() {
exit 1 }'
}

# The bundled engine must be able to START here, not merely exist: a shared
# library can be missing, and on Ubuntu 23.10+ AppArmor blocks the unprivileged
# user namespaces Chromium's sandbox needs unless install.sh's profile is in
# place — either way Chromium aborts in ZygoteHostImpl::Init ("No usable
# sandbox!"), which must never be what a click on the app icon does. So probe
# with a throwaway headless run once per engine version and remember success
# beside the engine; a failure is re-probed on every launch (a second or so,
# only while it is broken) and falls through to the system candidates.
engine_usable() { # engine_dir
[ -x "$1/chrome" ] || return 1
_ev="$(cat "$1/VERSION" 2>/dev/null)"
if [ -n "$_ev" ] && [ "$(cat "$1/.usable" 2>/dev/null)" = "$_ev" ]; then return 0; fi
_pd="$(mktemp -d 2>/dev/null || echo "/tmp/tron-engine-probe.$$")"
if timeout 25 "$1/chrome" --headless=new --disable-gpu --no-first-run --user-data-dir="$_pd" \
--dump-dom about:blank >/dev/null 2>&1; then
rm -rf "$_pd"
printf '%s\n' "$_ev" > "$1/.usable" 2>/dev/null
return 0
fi
rm -rf "$_pd"
echo "TronBrowser: the bundled engine ($1/chrome) cannot start on this machine — using a system or Flatpak Ungoogled Chromium instead." >&2
if [ "$(cat /proc/sys/kernel/apparmor_restrict_unprivileged_userns 2>/dev/null)" = "1" ]; then
echo " This distro restricts unprivileged user namespaces, which the engine's sandbox needs. Run 'tron upgrade': it installs the AppArmor profile that allows it (asks for sudo once)." >&2
fi
return 1
}

if [ -n "${TRONBROWSER_BROWSER:-}" ]; then
BROWSER="$TRONBROWSER_BROWSER"
elif [ -x "$DIR/chrome" ]; then
BROWSER="$DIR/chrome" # future: bundled native fork binary
elif [ -x "$DIR/engine/chrome" ] && "$DIR/engine/chrome" --version >/dev/null 2>&1; then
elif engine_usable "$DIR/engine"; then
# TronBrowser's own engine: the portable ungoogled-chromium the installer
# fetches next to this shim (install.sh ensure_engine), pinned per release.
# Preferred over anything the distro or Flathub provides because it is the
# one build we have verified end to end — in particular it honours the
# per-user NSS trust store, which the Flathub ungoogled-chromium does not
# (a Moshpit name's certificate sat in the exact database that build opened
# and it still refused it), so https on pit names works here with no flags.
# The --version probe skips it when a shared library is missing, falling
# through to the system candidates rather than failing to start.
BROWSER="$DIR/engine/chrome"
else
# Ungoogled Chromium ONLY. Never regular Chromium/Chrome, never snap (snap
Expand Down
59 changes: 58 additions & 1 deletion apps/web/public/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -732,13 +732,69 @@
rm -rf "$tmp"; return 1
}

# Ubuntu 23.10+ (kernel.apparmor_restrict_unprivileged_userns=1) blocks the
# unprivileged user namespaces Chromium's sandbox needs unless an AppArmor
# profile grants them to the binary. Ubuntu ships such profiles for its own
# browsers (/etc/apparmor.d/chrome); a third-party engine brings its own, and
# without it the engine aborts at start with "No usable sandbox!". Chromium's
# docs/security/apparmor-userns-restrictions.md is the reference. One sudo,
# written once per engine path, survives upgrades. The launcher probes the
# engine before using it, so a missing profile means a fallback, not a crash.
ENGINE_PROFILE="${TRONBROWSER_ENGINE_PROFILE:-/etc/apparmor.d/tronbrowser-engine}"

ensure_engine_sandbox() { # engine_dir
[ "$(uname -s)" = "Linux" ] || return 0
[ "$(cat /proc/sys/kernel/apparmor_restrict_unprivileged_userns 2>/dev/null)" = "1" ] || return 0
bin="$1/chrome"
if grep -qs "profile tronbrowser-engine $bin " "$ENGINE_PROFILE"; then return 0; fi
uid="$(id -u 2>/dev/null || echo 0)"
SUDO=""
if [ "$uid" -ne 0 ] && command -v sudo >/dev/null 2>&1 && { [ -t 1 ] || [ -t 2 ]; }; then SUDO="sudo"; fi
if [ "$uid" -ne 0 ] && [ -z "$SUDO" ]; then
warn "This distro restricts unprivileged user namespaces; TronBrowser's engine needs an AppArmor profile to sandbox itself. Re-run this in a terminal (it asks for sudo once): curl -fsSL $INSTALL_URL | sh -s -- ensure-engine"
Comment thread
ralyodio marked this conversation as resolved.
Dismissed
return 1
fi
if ! command -v apparmor_parser >/dev/null 2>&1; then
warn "apparmor_parser not found; cannot install the engine's AppArmor profile."
return 1
fi
info "Allowing the engine to sandbox itself (AppArmor profile at $ENGINE_PROFILE, asks for sudo once)…"
tmpf="$(mktemp)"
cat > "$tmpf" <<PROFILE
# TronBrowser's engine (ungoogled-chromium) needs unprivileged user namespaces
# for its process sandbox; this distro restricts them to profiled binaries.
# Written by tronbrowser.dev/install.sh. Remove with: sudo rm $ENGINE_PROFILE
abi <abi/4.0>,
include <tunables/global>

profile tronbrowser-engine $bin flags=(unconfined) {
userns,

# Site-specific additions and overrides. See local/README for details.
include if exists <local/tronbrowser-engine>
}
PROFILE
if $SUDO install -m 0644 "$tmpf" "$ENGINE_PROFILE" && $SUDO apparmor_parser -r -T -W "$ENGINE_PROFILE"; then
rm -f "$tmpf"
info "Installed the engine's AppArmor profile."
rm -f "$1/.usable" # let the launcher re-probe now that the sandbox should work
return 0
fi
rm -f "$tmpf"
warn "Couldn't install the engine's AppArmor profile; the launcher will use a system or Flatpak Ungoogled Chromium instead."
return 1
}

ensure_engine() {
[ "${TB_NO_ENGINE_INSTALL:-0}" = "1" ] && return 0
[ "$(uname -s)" = "Linux" ] || return 0
endest="$APP_DIR/engine"
_ldir="$(find "$APP_DIR" -maxdepth 3 -type f -name tronbrowser 2>/dev/null | head -n1)"
[ -n "$_ldir" ] && endest="$(dirname "$_ldir")/engine"
if [ -x "$endest/chrome" ] && [ "$(cat "$endest/VERSION" 2>/dev/null)" = "$ENGINE_VERSION" ]; then return 0; fi
if [ -x "$endest/chrome" ] && [ "$(cat "$endest/VERSION" 2>/dev/null)" = "$ENGINE_VERSION" ]; then
ensure_engine_sandbox "$endest" || true
return 0
fi
if ! tar --help 2>/dev/null | grep -q -- '-J\|xz'; then
if ! command -v xz >/dev/null 2>&1; then
warn "Couldn't install TronBrowser's engine: 'tar' here cannot read .xz and 'xz' is not installed (Debian/Ubuntu: sudo apt install xz-utils). Falling back to the system or Flatpak Ungoogled Chromium."
Expand All @@ -748,6 +804,7 @@
info "Setting up TronBrowser's engine (ungoogled-chromium ${ENGINE_VERSION})…"
if download_engine "$endest"; then
info "Installed the engine to $endest"
ensure_engine_sandbox "$endest" || true
return 0
fi
warn "Couldn't install TronBrowser's engine; falling back to the system or Flatpak Ungoogled Chromium (https on Moshpit names may warn there). Retry with: curl -fsSL $INSTALL_URL | sh -s -- ensure-engine"
Expand Down
16 changes: 15 additions & 1 deletion docs/moshpit-pit-toggle.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,21 @@ or Flatpak Chromium (after a `--version` probe, so a machine missing a shared
library falls back rather than failing to start). On that engine the per-name
import above is all that is needed: no flag, no bar, no relaunch. If the pit
is turned on while a Flatpak engine is still running, the sidebar says so and
points at `tron upgrade`. A `--ignore-certificate-errors-spki-list` workaround
points at `tron upgrade`.

Two things the engine needs to actually start. Ubuntu 23.10+ sets
`kernel.apparmor_restrict_unprivileged_userns=1`, which blocks the user
namespaces Chromium's sandbox needs; without help the engine aborts in
`ZygoteHostImpl::Init` ("No usable sandbox!", SIGTRAP), which is what bonita
hit first. Ubuntu's answer for third-party browsers is an AppArmor profile
granting `userns` to the binary path (Chromium's
`docs/security/apparmor-userns-restrictions.md`), so `install.sh` writes
`/etc/apparmor.d/tronbrowser-engine` once, with one `sudo`, and loads it.
And the launcher never takes the engine on faith: `engine_usable` runs a
throwaway headless start once per engine version, remembers success in
`engine/.usable`, and on failure falls through to a system or Flatpak
Chromium with a note saying why. A missing library or a missing profile is a
fallback, never a crash. A `--ignore-certificate-errors-spki-list` workaround
was tried and reverted: it works, but Chromium flags it as an unsupported switch
at every start.

Expand Down
Loading