#!/usr/bin/env bash # SPDX-License-Identifier: CC0-1.0 # Dedicated to the public domain under CC0 1.0 Universal. # See https://creativecommons.org/publicdomain/zero/1.0/ # This script is not affiliated with or endorsed by The Tor Project. set -Eeuo pipefail IFS=$'\n\t' # ------------------------------- logging -------------------------------------- # Plain ASCII, no icons or emojis. c_green() { printf '\033[0;32m%s\033[0m\n' "$*"; } c_yellow() { printf '\033[1;33m%s\033[0m\n' "$*"; } c_red() { printf '\033[0;31m%s\033[0m\n' "$*"; } log() { c_green "INFO: $*"; } warn() { c_yellow "WARN: $*"; } die() { c_red "ERROR: $*"; exit 1; } # Print a helpful line number on failure. trap 'die "Error on or near line $LINENO. Exiting."' ERR # ------------------------------- root check ----------------------------------- # We must be root to modify APT sources and install packages. if [[ ${EUID:-0} -ne 0 ]]; then if command -v sudo >/dev/null 2>&1; then exec sudo -E -- "$0" "$@" else die "Please run as root (or install sudo)." fi fi # Make APT non-interactive and a bit more resilient. export DEBIAN_FRONTEND=noninteractive APT_FLAGS=(-y -o Dpkg::Use-Pty=0 -o Acquire::Retries=3 -o Acquire::http::No-Cache=true) # --------------------------- detect codename ---------------------------------- # We need the Debian/Ubuntu codename (e.g., bookworm, bullseye, noble, jammy). OS_ID='' CODENAME='' if [[ -r /etc/os-release ]]; then # shellcheck disable=SC1091 . /etc/os-release OS_ID="${ID:-}" CODENAME="${VERSION_CODENAME:-}" fi if [[ -z ${CODENAME} ]]; then if command -v lsb_release >/dev/null 2>&1; then CODENAME="$(lsb_release -cs 2>/dev/null || true)" fi fi [[ -z ${CODENAME} ]] && die "Could not determine distro codename. Aborting." log "Distro ID: ${OS_ID:-unknown}, Codename: ${CODENAME}" # ------------------------------ prerequisites --------------------------------- # Minimal tools to fetch keys and manage repos. log "Ensuring prerequisites..." apt-get update "${APT_FLAGS[@]}" apt-get install "${APT_FLAGS[@]}" --no-install-recommends \ ca-certificates gnupg curl lsb-release # ------------------------ configure Tor Project repo -------------------------- # This section is the whole point: always use the Tor Project APT repository. log "Configuring Tor Project APT repository..." # Create a dedicated system keyring directory if needed. install -d -m 0755 /usr/share/keyrings # Download the Tor Project archive signing key to a temporary file. # Official key fingerprint: # A3C4 F0F9 79CA A22C DBA8 F512 EE8C BC9E 886D DD89 KEY_URL="https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc" TMP_KEY="$(mktemp)" curl -fsSL "$KEY_URL" -o "$TMP_KEY" # Verify the fingerprint before trusting the key. FPR_EXPECTED="A3C4 F0F9 79CA A22C DBA8 F512 EE8C BC9E 886D DD89" FPR_SEEN="$(gpg --show-keys --with-colons "$TMP_KEY" 2>/dev/null | awk -F: '/^fpr:/ {print $10; exit}' | sed 's/.\{4\}/& /g' | sed 's/ $//')" if [[ "$FPR_SEEN" != "$FPR_EXPECTED" ]]; then rm -f "$TMP_KEY" die "Tor Project key fingerprint mismatch. Expected '$FPR_EXPECTED' but saw '$FPR_SEEN'." fi # Install the verified key into a dedicated keyring file. gpg --dearmor < "$TMP_KEY" > /usr/share/keyrings/torproject-org-archive.gpg rm -f "$TMP_KEY" # Write the source list entry, pinned to the keyring. # Note: if the Tor Project does not publish packages for your codename or arch, # APT operations will fail, which is intentional for this script. SRC_LINE="deb [signed-by=/usr/share/keyrings/torproject-org-archive.gpg] https://deb.torproject.org/torproject.org ${CODENAME} main" SRC_FILE="/etc/apt/sources.list.d/torproject-org.list" printf '%s\n' "$SRC_LINE" > "$SRC_FILE" # Refresh APT indices from the Tor Project repo. apt-get update "${APT_FLAGS[@]}" # ------------------------------- installation --------------------------------- # Install Tor from the Tor Project repository, plus their keyring package # to keep keys maintained via APT. log "Installing tor and deb.torproject.org-keyring from the Tor Project repo..." apt-get install "${APT_FLAGS[@]}" --no-install-recommends tor deb.torproject.org-keyring # ------------------------------- post-install --------------------------------- # Show version and attempt to enable and start the service on systemd systems. if command -v tor >/dev/null 2>&1; then log "Tor installed. Version:" tor --version || true if command -v systemctl >/dev/null 2>&1; then # Try common service names. Ignore errors if a given unit does not exist. systemctl enable --now tor.service >/dev/null 2>&1 || true systemctl enable --now tor@default.service >/dev/null 2>&1 || true # Print a simple status hint if neither service is active. if ! systemctl is-active --quiet tor.service 2>/dev/null \ && ! systemctl is-active --quiet tor@default.service 2>/dev/null; then warn "Tor service is not active. You can start it with:" warn " systemctl start tor.service" warn " or" warn " systemctl start tor@default.service" fi else warn "Systemd not detected. Use your init system to manage the Tor daemon." fi else die "Tor did not install correctly." fi