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
96 changes: 96 additions & 0 deletions apps/desktop/launcher/tronbrowser
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,102 @@ if [ "$TOR" != "1" ] && [ -z "$MAC_APP" ] && [ -f "$DIR/tron-pwa" ]; then
fi
fi

# --- Moshpit names over HTTPS ----------------------------------------------
# Chromium does not read /etc/ssl/certs. On Linux the only user-added trust it
# honours lives in the per-user NSS database at ~/.pki/nssdb — a path keyed off
# $HOME, not off --user-data-dir, so no TronBrowser profile can carry it and no
# amount of reinstalling puts it there.
#
# Moshpit installs its certificates into the OpenSSL store instead. That is why
# `curl https://chovy.hacker` returns the page while the same URL here fails
# with ERR_CERT_AUTHORITY_INVALID: the two are reading different stores. It
# reads as a TronBrowser bug, and there is nothing the user can do about it from
# inside the browser, so mirror whatever Moshpit installed into the store
# Chromium actually reads.
#
# Two certificate shapes, and the trust flag differs:
# * the Moshpit Local CA (CA:TRUE) — an anchor, "C,,". This is what a machine
# behind the pinned-TLS proxy is served.
# * a per-name origin leaf (CA:FALSE, self-signed, SAN = the name) — "P,,",
# a trusted peer. Importing one as "C,," fails: Chromium will not anchor a
# CA:FALSE certificate. Reading basicConstraints beats guessing from the
# filename, because getting this backwards fails exactly like doing nothing.
#
# Runs on every start rather than once at install. Names get pointed and trusted
# long after the browser is installed, and re-importing one already in the
# database is skipped by nickname, so the steady state costs a `certutil -L`.
# Set TRONBROWSER_NO_MOSHPIT_TRUST=1 to opt out entirely.
#
# The Local CA keeps the nickname `moshcode dns enable` gives it, so a database
# it already wrote is recognised as done rather than imported a second time.
moshpit_nickname() { # cert_file
case "$1" in
*/moshpit-local-ca.crt|*/.moshpit/ca/ca.crt) printf 'Moshpit Local CA' ;;
*)
_base="$(basename "$1" .crt)"
printf 'moshpit %s' "${_base#moshpit-}"
;;
esac
}

sync_moshpit_trust() {
if [ "$(uname -s)" != "Linux" ]; then return 0; fi
if [ "${TRONBROWSER_NO_MOSHPIT_TRUST:-0}" = "1" ]; then return 0; fi

_nssdb="$HOME/.pki/nssdb"
_ready=0

# Each word below is a literal path or a glob result, so this stays
# whitespace-safe; an unmatched glob arrives as the pattern itself and fails
# the -f test.
for _cert in /usr/local/share/ca-certificates/moshpit-*.crt \
/etc/ca-certificates/trust-source/anchors/moshpit-*.crt \
/etc/pki/ca-trust/source/anchors/moshpit-*.crt \
"$HOME/.moshpit/ca/ca.crt"; do
if [ ! -f "$_cert" ]; then continue; fi

# Deferred until we know there is something to import: a machine that never
# heard of Moshpit should not be told to go install anything.
if [ "$_ready" = "0" ]; then
if ! command -v certutil >/dev/null 2>&1; then
echo "TronBrowser: Moshpit certificates are installed, but 'certutil' is not." >&2
echo " Chromium reads its own trust store, so .hacker/.rank names fail TLS here." >&2
echo " Debian/Ubuntu: sudo apt install libnss3-tools Fedora: sudo dnf install nss-tools" >&2
echo " Arch: sudo pacman -S nss Then start TronBrowser again." >&2
return 0
fi
if [ ! -f "$_nssdb/cert9.db" ]; then
mkdir -p "$_nssdb" 2>/dev/null || return 0
certutil -d "sql:$_nssdb" -N --empty-password >/dev/null 2>&1 || return 0
fi
_ready=1
fi

_nick="$(moshpit_nickname "$_cert")"
if certutil -d "sql:$_nssdb" -L -n "$_nick" >/dev/null 2>&1; then continue; fi

_flag="P,,"
if command -v openssl >/dev/null 2>&1; then
if openssl x509 -noout -text -in "$_cert" 2>/dev/null | grep -q 'CA:TRUE'; then
_flag="C,,"
fi
else
# No openssl: the Local CA is the only anchor Moshpit ships, and it is the
# one file we can identify by name alone.
case "$_nick" in "Moshpit Local CA") _flag="C,," ;; esac
fi

if certutil -d "sql:$_nssdb" -A -t "$_flag" -n "$_nick" -i "$_cert" >/dev/null 2>&1; then
echo "TronBrowser: trusted $_nick for Moshpit HTTPS." >&2
fi
done
return 0
}

# Best-effort and never fatal: a trust store we could not write must not stop
# the browser from opening.
sync_moshpit_trust || true

# --- GPU backend -----------------------------------------------------------
# A crashing GPU process does not look like a crash. The window stays up and the
# page keeps whatever it had already rasterized — a logo, a header — while
Expand Down
57 changes: 54 additions & 3 deletions apps/web/public/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,51 @@ ensure_tor() {
return 1
}

# Chromium takes user-added trust only from the per-user NSS database at
# ~/.pki/nssdb, never from /etc/ssl/certs. The launcher mirrors Moshpit's
# certificates into it on every start so `.hacker` / `.rank` names load over
# HTTPS — but writing an NSS database needs `certutil`, and that is the one part
# of the job that needs a package installed. Doing it here is the difference
# between the launcher fixing the problem and the launcher printing a command
# for the user to run, which is the manual step this is meant to remove.
#
# Only on a machine that actually has Moshpit certificates: a package nobody
# needs is not ours to install. Best-effort throughout — the launcher explains
# what to do if this cannot get there.
ensure_certutil() {
# macOS Chromium reads the system keychain, which `moshcode dns enable`
# already writes. There is no NSS database in the picture at all.
[ "$(uname -s)" = "Linux" ] || return 0
command -v certutil >/dev/null 2>&1 && return 0

_have_moshpit=0
for _c in /usr/local/share/ca-certificates/moshpit-*.crt \
/etc/ca-certificates/trust-source/anchors/moshpit-*.crt \
/etc/pki/ca-trust/source/anchors/moshpit-*.crt \
"$HOME/.moshpit/ca/ca.crt"; do
if [ -f "$_c" ]; then _have_moshpit=1; break; fi
done
[ "$_have_moshpit" = "1" ] || return 0

info "Setting up certutil (so Moshpit names load over HTTPS)…"
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" -eq 0 ] || [ -n "$SUDO" ]; then
# The tool is the same everywhere; only the package carrying it differs.
if command -v apt-get >/dev/null 2>&1; then $SUDO apt-get update -y >/dev/null 2>&1; $SUDO apt-get install -y libnss3-tools >/dev/null 2>&1 || true
elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y nss-tools >/dev/null 2>&1 || true
elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y nss-tools >/dev/null 2>&1 || true
elif command -v pacman >/dev/null 2>&1; then $SUDO pacman -Sy --noconfirm nss >/dev/null 2>&1 || true
elif command -v zypper >/dev/null 2>&1; then $SUDO zypper --non-interactive install mozilla-nss-tools >/dev/null 2>&1 || true
elif command -v apk >/dev/null 2>&1; then $SUDO apk add nss-tools >/dev/null 2>&1 || true
fi
command -v certutil >/dev/null 2>&1 && return 0
fi
warn "Couldn't install certutil automatically. Moshpit names (.hacker, .rank) will fail TLS in TronBrowser until it is installed (e.g. 'sudo apt install libnss3-tools' / 'sudo pacman -S nss')."
return 1
}

do_install() {
need uname
asset="$(detect_asset)"
Expand Down Expand Up @@ -692,7 +737,11 @@ DESKTOP
command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$apps_dir" 2>/dev/null || true

ensure_browser
ensure_tor # so the in-browser 🧅 Tor toggle works out of the box
# `|| true` because this script runs under `set -eu`: both of these return 1
# when they could not install their tool, and a missing nice-to-have must not
# abort an install that has already put the browser on disk.
ensure_tor || true # so the in-browser 🧅 Tor toggle works out of the box
ensure_certutil || true # so Moshpit names load over HTTPS on first launch
brand_macos_icon "$(dirname "$bin")/tronbrowser.png"

info "Installed TronBrowser $tag to $APP_DIR"
Expand Down Expand Up @@ -791,8 +840,9 @@ do_upgrade() {
[ -n "$latest" ] || err "could not resolve the latest release of $REPO"
if [ "$current" = "$latest" ] && [ "${TB_FORCE:-0}" != "1" ]; then
info "TronBrowser is already up to date ($current)."
ensure_browser # still make sure Ungoogled Chromium is installed
ensure_tor # and that Tor is available for the toggle
ensure_browser # still make sure Ungoogled Chromium is installed
ensure_tor || true # and that Tor is available for the toggle
ensure_certutil || true # and that Moshpit trust can be written
brand_macos_icon "$(find "$APP_DIR" -maxdepth 3 -name tronbrowser.png 2>/dev/null | head -n1)" # re-apply icon (Chromium updates reset it)
info "Re-install anyway with: TB_FORCE=1 tron upgrade"
return
Expand Down Expand Up @@ -851,6 +901,7 @@ case "$cmd" in
esac ;;
remove|uninstall) do_remove ;;
ensure-tor) ensure_tor ;;
ensure-certutil) ensure_certutil ;;
version|--version|-v) do_version ;;
help|--help|-h) usage ;;
*) err "unknown command: $cmd (try 'help')" ;;
Expand Down
Loading