#!/bin/sh # Copyright (c) Tailscale Inc & AUTHORS # Modifications Copyright (c) 2025, Orb Forge Inc. # Modified by Orb Forge Inc. to install Orb Sensor # SPDX-License-Identifier: BSD-3-Clause # # This script detects the current operating system, and installs # Orb Sensor according to that OS's conventions. set -eu # All the code is wrapped in a main function that gets called at the # bottom of the file, so that a truncated partial download doesn't end # up executing half a script. main() { # Step 1: detect the current linux distro, version, and packaging system. # # We rely on a combination of 'uname' and /etc/os-release to find # an OS name and version, and from there work out what # installation method we should be using. # # The end result of this step is that the following three # variables are populated, if detection was successful. OS="" VERSION="" PACKAGETYPE="" ORB_USER="" APT_KEY_TYPE="" # Only for apt-based distros APT_SYSTEMCTL_START=false # Only needs to be true for Kali TRACK="${TRACK:-stable}" DRY_MODE="${DRY_MODE:-0}" case "$TRACK" in stable) ;; *) echo "unsupported track $TRACK" exit 1 ;; esac if [ -f /etc/os-release ]; then # /etc/os-release populates a number of shell variables. We care about the following: # - ID: the short name of the OS (e.g. "debian", "freebsd") # - VERSION_ID: the numeric release version for the OS, if any (e.g. "18.04") # - VERSION_CODENAME: the codename of the OS release, if any (e.g. "buster") # - UBUNTU_CODENAME: if it exists, use instead of VERSION_CODENAME . /etc/os-release case "$ID" in ubuntu|pop|neon|zorin|tuxedo) OS="ubuntu" if [ "${UBUNTU_CODENAME:-}" != "" ]; then VERSION="$UBUNTU_CODENAME" else VERSION="$VERSION_CODENAME" fi PACKAGETYPE="apt" ORB_USER="orb" # Third-party keyrings became the preferred method of # installation in Ubuntu 20.04. if expr "$VERSION_ID" : "2.*" >/dev/null; then APT_KEY_TYPE="keyring" else APT_KEY_TYPE="legacy" fi ;; debian) OS="$ID" VERSION="$VERSION_CODENAME" PACKAGETYPE="apt" ORB_USER="orb" # Third-party keyrings became the preferred method of # installation in Debian 11 (Bullseye). if [ -z "${VERSION_ID:-}" ]; then # rolling release. If you haven't kept current, that's on you. APT_KEY_TYPE="keyring" # Parrot Security is a special case that uses ID=debian elif [ "$NAME" = "Parrot Security" ]; then # All versions new enough to have this behaviour prefer keyring # and their VERSION_ID is not consistent with Debian. APT_KEY_TYPE="keyring" # They don't specify the Debian version they're based off in os-release # but Parrot 6 is based on Debian 12 Bookworm. VERSION=bookworm elif [ "$VERSION_ID" -lt 11 ]; then APT_KEY_TYPE="legacy" else APT_KEY_TYPE="keyring" fi ;; linuxmint) if [ "${UBUNTU_CODENAME:-}" != "" ]; then OS="ubuntu" VERSION="$UBUNTU_CODENAME" elif [ "${DEBIAN_CODENAME:-}" != "" ]; then OS="debian" VERSION="$DEBIAN_CODENAME" else OS="ubuntu" VERSION="$VERSION_CODENAME" fi PACKAGETYPE="apt" ORB_USER="orb" if [ "$VERSION_ID" -lt 5 ]; then APT_KEY_TYPE="legacy" else APT_KEY_TYPE="keyring" fi ;; elementary) OS="ubuntu" VERSION="$UBUNTU_CODENAME" PACKAGETYPE="apt" ORB_USER="orb" if [ "$VERSION_ID" -lt 6 ]; then APT_KEY_TYPE="legacy" else APT_KEY_TYPE="keyring" fi ;; parrot|mendel) OS="debian" PACKAGETYPE="apt" ORB_USER="orb" if [ "$VERSION_ID" -lt 5 ]; then VERSION="buster" APT_KEY_TYPE="legacy" else VERSION="bullseye" APT_KEY_TYPE="keyring" fi ;; galliumos) OS="ubuntu" PACKAGETYPE="apt" ORB_USER="orb" VERSION="bionic" APT_KEY_TYPE="legacy" ;; pureos|kaisen) OS="debian" PACKAGETYPE="apt" ORB_USER="orb" VERSION="bullseye" APT_KEY_TYPE="keyring" ;; raspbian) OS="debian" VERSION="$VERSION_CODENAME" PACKAGETYPE="apt" ORB_USER="orb" # Third-party keyrings became the preferred method of # installation in Raspbian 11 (Bullseye). if [ "$VERSION_ID" -lt 11 ]; then APT_KEY_TYPE="legacy" else APT_KEY_TYPE="keyring" fi ;; kali) OS="debian" PACKAGETYPE="apt" ORB_USER="orb" YEAR="$(echo "$VERSION_ID" | cut -f1 -d.)" APT_SYSTEMCTL_START=true # Third-party keyrings became the preferred method of # installation in Debian 11 (Bullseye), which Kali switched # to in roughly 2021.x releases if [ "$YEAR" -lt 2021 ]; then # Kali VERSION_ID is "kali-rolling", which isn't distinguishing VERSION="buster" APT_KEY_TYPE="legacy" else VERSION="bullseye" APT_KEY_TYPE="keyring" fi ;; Deepin|deepin) # https://github.com/tailscale/tailscale/issues/7862 OS="debian" PACKAGETYPE="apt" ORB_USER="orb" if [ "$VERSION_ID" -lt 20 ]; then APT_KEY_TYPE="legacy" VERSION="buster" else APT_KEY_TYPE="keyring" VERSION="bullseye" fi ;; pika) PACKAGETYPE="apt" ORB_USER="orb" # All versions of PikaOS are new enough to prefer keyring APT_KEY_TYPE="keyring" # Older versions of PikaOS are based on Ubuntu rather than Debian if [ "$VERSION_ID" -lt 4 ]; then OS="ubuntu" VERSION="$UBUNTU_CODENAME" else OS="debian" VERSION="$DEBIAN_CODENAME" fi ;; sparky) OS="debian" PACKAGETYPE="apt" ORB_USER="orb" VERSION="$DEBIAN_CODENAME" APT_KEY_TYPE="keyring" ;; centos) OS="$ID" VERSION="$VERSION_ID" PACKAGETYPE="dnf" ORB_USER="orb" if [ "$VERSION" = "7" ]; then PACKAGETYPE="yum" fi ;; ol) OS="oracle" VERSION="$(echo "$VERSION_ID" | cut -f1 -d.)" PACKAGETYPE="dnf" ORB_USER="orb" if [ "$VERSION" = "7" ]; then PACKAGETYPE="yum" fi ;; rhel) OS="$ID" VERSION="$(echo "$VERSION_ID" | cut -f1 -d.)" PACKAGETYPE="dnf" ORB_USER="orb" if [ "$VERSION" = "7" ]; then PACKAGETYPE="yum" fi ;; fedora) OS="$ID" VERSION="" PACKAGETYPE="dnf" ORB_USER="orb" ;; rocky|almalinux|nobara|openmandriva|sangoma|risios|cloudlinux|alinux|fedora-asahi-remix|bazzite) OS="fedora" VERSION="" PACKAGETYPE="dnf" ORB_USER="orb" ;; amzn) OS="amazon-linux" VERSION="$VERSION_ID" PACKAGETYPE="yum" ORB_USER="orb" ;; xenenterprise) OS="centos" VERSION="$(echo "$VERSION_ID" | cut -f1 -d.)" PACKAGETYPE="yum" ORB_USER="orb" ;; opensuse-leap|sles) OS="opensuse" VERSION="leap/$VERSION_ID" PACKAGETYPE="zypper" ORB_USER="orb" ;; opensuse-tumbleweed) OS="opensuse" VERSION="tumbleweed" PACKAGETYPE="zypper" ORB_USER="orb" ;; sle-micro-rancher) OS="opensuse" VERSION="leap/15.4" PACKAGETYPE="zypper" ORB_USER="orb" ;; arch|archarm|endeavouros|blendos|garuda|archcraft|cachyos) OS="arch" VERSION="" # rolling release PACKAGETYPE="pacman" ORB_USER="orb" ;; manjaro|manjaro-arm|biglinux) OS="arch" VERSION="" # rolling release PACKAGETYPE="pacman" ORB_USER="orb" ;; alpine) OS="$ID" VERSION="$VERSION_ID" PACKAGETYPE="apk" ORB_USER="orb" ;; postmarketos) OS="alpine" VERSION="$VERSION_ID" PACKAGETYPE="apk" ORB_USER="orb" ;; nixos) OS="$ID" VERSION="$VERSION_ID" ;; void) OS="$ID" VERSION="" # rolling release PACKAGETYPE="xbps" ;; gentoo) OS="$ID" VERSION="" # rolling release PACKAGETYPE="emerge" ;; freebsd) OS="$ID" VERSION="$(echo "$VERSION_ID" | cut -f1 -d.)" PACKAGETYPE="pkg" ;; osmc) OS="debian" PACKAGETYPE="apt" VERSION="bullseye" APT_KEY_TYPE="keyring" ;; photon) OS="photon" VERSION="$(echo "$VERSION_ID" | cut -f1 -d.)" PACKAGETYPE="tdnf" ;; openwrt|turrisos) OS="openwrt" VERSION="$(echo "$VERSION_ID" | cut -f1 -d.)" PACKAGETYPE="opkg" ORB_USER="root" ;; # TODO: wsl? # TODO: synology? qnap? esac fi # If we failed to detect something through os-release, consult # uname and try to infer things from that. if [ -z "$OS" ]; then if type uname >/dev/null 2>&1; then case "$(uname)" in FreeBSD) # FreeBSD before 12.2 doesn't have # /etc/os-release, so we wouldn't have found it in # the os-release probing above. OS="freebsd" VERSION="$(freebsd-version | cut -f1 -d.)" PACKAGETYPE="pkg" ;; OpenBSD) OS="openbsd" VERSION="$(uname -r)" PACKAGETYPE="" ;; Darwin) OS="macos" VERSION="$(sw_vers -productVersion | cut -f1-2 -d.)" PACKAGETYPE="appstore" ;; Linux) OS="other-linux" VERSION="" PACKAGETYPE="" if [ -f /opt/etc/entware_release ]; then OS="entware" VERSION="$(cat /opt/etc/entware_release | grep -i VERSION_ID | cut -f2 -d= | tr -d '"')" PACKAGETYPE="opkg" ORB_USER="root" fi ;; esac fi fi # Ideally we want to use curl, but on some installs we # only have wget. Detect and use what's available. CURL= if type curl >/dev/null; then CURL="curl -fsSL" elif type wget >/dev/null; then CURL="wget -q -O-" fi if [ -z "$CURL" ]; then echo "The installer needs either curl or wget to download files." echo "Please install either curl or wget to proceed." exit 1 fi TEST_URL="https://pkgs.orb.net/" RC=0 TEST_OUT=$($CURL "$TEST_URL" 2>&1) || RC=$? if [ $RC != 0 ]; then echo "The installer cannot reach $TEST_URL" echo "Please make sure that your machine has internet access." echo "Test output:" echo $TEST_OUT exit 1 fi # Step 2: having detected an OS we support, is it one of the # versions we support? OS_UNSUPPORTED= case "$OS" in ubuntu|debian|raspbian|centos|oracle|rhel|amazon-linux|opensuse|photon) # Check with the package server whether a given version is supported. URL="https://pkgs.orb.net/$TRACK/$OS/installer-supported" $CURL "$URL" 2> /dev/null | grep -q OK || OS_UNSUPPORTED=1 ;; fedora) # All versions supported, no version checking required. # support not added yet ;; arch) # Rolling release, no version checking needed. echo "Your operating system will be upgraded as part of the installation of orb. Are you sure you want to proceed? (y/n) default: y" read -r INSTALL_ORB /dev/null 2>&1; then echo "UNAME=$(uname -a)" else echo "UNAME=" fi echo if [ -f /etc/os-release ]; then cat /etc/os-release else echo "No /etc/os-release" fi exit 1 fi # Step 3: work out if we can run privileged commands, and if so, # how. CAN_ROOT= SUDO= if [ "$(id -u)" = 0 ]; then CAN_ROOT=1 SUDO="" elif type sudo >/dev/null; then CAN_ROOT=1 SUDO="sudo" elif type doas >/dev/null; then CAN_ROOT=1 SUDO="doas" fi if [ "$CAN_ROOT" != "1" ]; then echo "This installer needs to run commands as root." echo "We tried looking for 'sudo' and 'doas', but couldn't find them." echo "Either re-run this script as root, or set up sudo/doas." exit 1 fi # dry run prints commands instead of running them if [ "$DRY_MODE" = "1" ]; then SUDO="echo" CURL="echo $CURL" fi # Step 4: run the installation. OSVERSION="$OS" [ "$VERSION" != "" ] && OSVERSION="$OSVERSION $VERSION" echo "Installing Orb for $OSVERSION, using method $PACKAGETYPE" case "$PACKAGETYPE" in apt) export DEBIAN_FRONTEND=noninteractive if [ "$APT_KEY_TYPE" = "legacy" ] && ! type gpg >/dev/null; then $SUDO apt-get update $SUDO apt-get install -y gnupg fi set -x $SUDO mkdir -p --mode=0755 /usr/share/keyrings case "$APT_KEY_TYPE" in legacy) $CURL "https://pkgs.orb.net/$TRACK/$OS/orbforge.asc" | $SUDO apt-key add - $CURL "https://pkgs.orb.net/$TRACK/$OS/$OS.list" | $SUDO tee /etc/apt/sources.list.d/orb.list $SUDO chmod 0644 /etc/apt/sources.list.d/orb.list ;; keyring) $CURL "https://pkgs.orb.net/$TRACK/$OS/orbforge.noarmor.gpg" | $SUDO tee /usr/share/keyrings/orbforge-keyring.gpg >/dev/null $SUDO chmod 0644 /usr/share/keyrings/orbforge-keyring.gpg $CURL "https://pkgs.orb.net/$TRACK/$OS/$OS.orbforge-keyring.list" | $SUDO tee /etc/apt/sources.list.d/orb.list $SUDO chmod 0644 /etc/apt/sources.list.d/orb.list ;; esac $SUDO apt-get update $SUDO apt-get install -y orb if [ "$APT_SYSTEMCTL_START" = "true" ]; then $SUDO systemctl enable --now orb $SUDO systemctl start orb fi set +x ;; yum) set -x $SUDO yum install yum-utils -y $SUDO yum-config-manager -y --add-repo "https://pkgs.orb.net/$TRACK/rpm/orb.repo" $SUDO yum install orb -y $SUDO systemctl enable --now orb set +x ;; dnf) # DNF 5 has a different argument format; determine which one we have. DNF_VERSION="3" if dnf --version | grep -q '^dnf5 version'; then DNF_VERSION="5" fi # The 'config-manager' plugin wasn't implemented when # DNF5 was released; detect that and use the old # version if necessary. if [ "$DNF_VERSION" = "5" ]; then set -x $SUDO dnf install -y 'dnf-command(config-manager)' && DNF_HAVE_CONFIG_MANAGER=1 || DNF_HAVE_CONFIG_MANAGER=0 set +x if [ "$DNF_HAVE_CONFIG_MANAGER" != "1" ]; then if type dnf-3 >/dev/null; then DNF_VERSION="3" else echo "dnf 5 detected, but 'dnf-command(config-manager)' not available and dnf-3 not found" exit 1 fi fi fi set -x if [ "$DNF_VERSION" = "3" ]; then $SUDO dnf install -y 'dnf-command(config-manager)' $SUDO dnf config-manager --add-repo "https://pkgs.orb.net/$TRACK/rpm/orb.repo" elif [ "$DNF_VERSION" = "5" ]; then # Already installed config-manager, above. $SUDO dnf config-manager addrepo --from-repofile="https://pkgs.orb.net/$TRACK/rpm/orb.repo" else echo "unexpected: unknown dnf version $DNF_VERSION" exit 1 fi $SUDO dnf install -y orb $SUDO systemctl enable --now orb set +x ;; tdnf) set -x curl -fsSL "https://pkgs.orb.net/$TRACK/rpm/orb.repo" > /etc/yum.repos.d/orb.repo $SUDO tdnf install -y orb $SUDO systemctl enable --now orb set +x ;; zypper) set -x $SUDO rpm --import "https://pkgs.orb.net/$TRACK/rpm-opensuse/key.gpg" $SUDO zypper --non-interactive ar -g -r "https://pkgs.orb.net/$TRACK/rpm-opensuse/orb.repo" $SUDO zypper --non-interactive --gpg-auto-import-keys refresh $SUDO zypper --non-interactive install orb $SUDO systemctl enable --now orb set +x ;; pacman) set -x $CURL https://pkgs.orb.net/stable/rpm/key.gpg | $SUDO pacman-key -a - $SUDO pacman-key --lsign-key AC24C3F82F82BADB6868AE99C1968008BFF84DF5 if grep -q "\[orb\]" /etc/pacman.conf; then echo "orb repo already exists in /etc/pacman.conf" else echo -e "[orb]\nServer = https://pkgs.orb.net/stable/arch/\$arch" | $SUDO tee -a /etc/pacman.conf fi $SUDO pacman -Syu --needed --noconfirm orb $SUDO systemctl enable --now orb set +x ;; pkg) set -x $SUDO pkg install --yes orb $SUDO service orb enable $SUDO service orb start set +x ;; apk) set -x # ideally only add the repo if it doesn't exist if grep -q "pkgs\.orb\.net" /etc/apk/repositories; then echo "orb repo already exists in /etc/apk/repositories" else echo "https://pkgs.orb.net/stable/alpine" | $SUDO tee -a /etc/apk/repositories fi $CURL "https://pkgs.orb.net/stable/alpine/orb.pub" | $SUDO tee /etc/apk/keys/packages@orb.net.rsa.pub $SUDO apk update $SUDO apk add orb set +x ;; xbps) set -x # $SUDO xbps-install orb -y set +x ;; emerge) set -x # $SUDO emerge --ask=n net/orb set +x ;; opkg) # Check if opkg is available if ! type opkg >/dev/null 2>&1; then echo "The opkg package manager couldn't be found on your system." echo "This is required for installing Orb on OpenWRT-based systems." echo "You can try the script again, ensuring opkg is in your PATH. Otherwise you can try the manual instructions: " echo "https://orb.net/docs/setup-sensor/linux/openwrt" exit 1 fi ARCHITECTURE=$(opkg info busybox | awk '$1 == "Architecture:" {print $2; exit}') if [ -z "$ARCHITECTURE" ]; then echo "Could not determine architecture from opkg." exit 1 fi # find opkg configs # Check common opkg config path locations OPKG_CONF_PATHS="/etc/opkg/customfeeds.conf /opt/etc/opkg/customfeeds.conf /etc/opkg.conf /opt/etc/opkg.conf" OPKG_CONF_FOUND=0 for OPKG_CONF_PATH in $OPKG_CONF_PATHS; do if [ -f "$OPKG_CONF_PATH" ]; then echo "Found opkg config at $OPKG_CONF_PATH" OPKG_CONF_FOUND=1 break fi done OPKG_KEYS_PATHS="/etc/opkg/keys /opt/etc/opkg/keys" OPKG_KEYS_FOUND=0 for OPKG_KEYS_PATH in $OPKG_KEYS_PATHS; do if [ -d "$OPKG_KEYS_PATH" ]; then echo "Found opkg keys directory at $OPKG_KEYS_PATH" OPKG_KEYS_FOUND=1 break fi done if [ $OPKG_CONF_FOUND -eq 0 ]; then echo "Could not find opkg configuration file." echo "Please ensure opkg is correctly configured on your system." echo "You can try the manual instructions at: https://orb.net/docs/setup-sensor/linux/openwrt" exit 1 fi set -x if grep -q "orb_packages" $OPKG_CONF_PATH; then echo "orb_packages repo already exists in $OPKG_CONF_PATH" else echo "src/gz orb_packages https://pkgs.orb.net/stable/openwrt/$ARCHITECTURE" | $SUDO tee -a $OPKG_CONF_PATH fi if [ $OPKG_KEYS_FOUND -eq 1 ]; then echo "Found opkg keys directory at $OPKG_KEYS_PATH" $CURL https://pkgs.orb.net/stable/openwrt/key.pub | $SUDO tee $OPKG_KEYS_PATH/744a82bfef3c5690 else echo "Could not find opkg keys directory. Your installation might not verify signatures." echo "If your installation requires signature verification, you can setup the key manually." echo "You can try the manual instructions at: https://orb.net/docs/setup-sensor/linux/openwrt" fi $SUDO opkg update $SUDO opkg install orb set +x if [ "$OS" = "entware" ]; then # entware doesn't start the app anymore /opt/etc/init.d/S90orb start || true fi ;; appstore) set -x open "https://apps.apple.com/us/app/orb-net/id6477840170" set +x ;; *) echo "unexpected: unknown package type $PACKAGETYPE" exit 1 ;; esac # find if orb successfully installed if ! type orb >/dev/null 2>&1; then echo "Can't find the orb binary. Orb installation might have failed." exit 1 fi # See if orb is running and capture the PID ORB_PID=$(pidof orb 2>/dev/null) if [ -z "$ORB_PID" ]; then echo "Orb is not running. Please check the installation." exit 1 else echo "Orb Sensor installed successfully!" # echo "Orb is running with PID: $ORB_PID" fi # how to link case "$ORB_USER" in root) echo "Orb Sensor is running as root." echo "You can link your Orb Sensor to your Orb account using the following command:" echo "$SUDO orb link" echo "Executing:" $SUDO orb link ;; "") # skip ;; *) echo "Orb Sensor is running as $ORB_USER." echo "You can link your Orb Sensor to your Orb account using the following command:" LINKEXEC=1 if [ "$SUDO" != "" ]; then LINKSUDO="$SUDO" elif type sudo >/dev/null; then LINKSUDO="sudo" elif type doas >/dev/null; then LINKSUDO="doas" else LINKSUDO="sudo" LINKEXEC=0 fi echo "$LINKSUDO -u $ORB_USER orb link" if [ "$LINKEXEC" = "1" ]; then echo "Executing:" $LINKSUDO -u $ORB_USER orb link fi ;; esac # Check if the OS supports auto-updates AUTO_UPDATE_SUPPORTED=0 case "$OS" in ubuntu|debian|alpine|openwrt|fedora|redhat|centos|oracle) AUTO_UPDATE_SUPPORTED=1 ;; *) AUTO_UPDATE_SUPPORTED=0 ;; esac if [ "$AUTO_UPDATE_SUPPORTED" = "1" ]; then echo echo "Do you want to enable automated updates? (y/n) default: yes" read -r ENABLE_UPDATES