#!/usr/bin/env bash # HGVSupport remote-access installer — Linux (Ubuntu/Debian/Fedora and most desktops). # # Public, non-secret. Served unauthenticated from https://get.hgvsupport.ddns.net/install.sh so a # staff member can run: curl -fsSL https://get.hgvsupport.ddns.net/install.sh | bash # # What it does, in order: # 1. installs the Tailscale VPN client (official install script) # 2. installs the Linphone softphone (flatpak, then snap, then apt — first that works) # 3. joins the HGVSupport tailnet: tailscale up --login-server=… --accept-routes # (a browser opens to the sign-in portal — username + password + 6-digit authenticator code) # 4. writes the local Linphone config from the extension + phone (SIP) password YOU paste from # your welcome email. The password is typed hidden and is NEVER printed or uploaded anywhere. # # Fails closed: any failed step stops the script (set -euo pipefail). Safe to re-run — an already # joined VPN and an already installed app are left as-is; the phone config is re-written from your # input and any previous config is backed up first. # # NON-SECRET constants (safe to be public — these are addresses, not credentials): LOGIN_SERVER="https://headscale.hgvsupport.ddns.net" # the HGVSupport VPN control server SIP_SERVER="192.168.3.151" # the desk-phone (PBX) server, reached over the VPN set -euo pipefail log() { printf '\n\033[1;36m[HGV]\033[0m %s\n' "$*"; } warn() { printf '\n\033[1;33m[HGV]\033[0m %s\n' "$*" >&2; } die() { printf '\n\033[1;31m[HGV ERROR]\033[0m %s\n' "$*" >&2; exit 1; } have() { command -v "$1" >/dev/null 2>&1; } [ "$(uname -s)" = "Linux" ] || die "This installer is for Linux. Use install-macos.sh on a Mac or install.ps1 on Windows." # ----- privilege check (installing packages + 'tailscale up' need root) --------------------------- if [ "$(id -u)" -eq 0 ]; then SUDO=""; else have sudo || die "This installer needs administrator rights, but 'sudo' is not available. Run it as an administrator." SUDO="sudo" log "You may be asked for your computer password (for installing software)." $SUDO -v || die "Administrator password check failed — cannot continue." fi # ----- 1. Tailscale ------------------------------------------------------------------------------ install_tailscale() { if have tailscale; then log "Tailscale is already installed ($(tailscale version 2>/dev/null | head -1))."; return; fi log "Installing Tailscale…" curl -fsSL https://tailscale.com/install.sh | $SUDO sh || die "Tailscale install failed. Reply to your welcome email for help." have tailscale || die "Tailscale did not install correctly." } # ----- 2. Linphone ------------------------------------------------------------------------------- # LINPHONE_VARIANT records HOW it is installed so we write config to the matching location. LINPHONE_VARIANT="" install_linphone() { if have linphone || flatpak info org.linphone.Linphone >/dev/null 2>&1; then LINPHONE_VARIANT="$(flatpak info org.linphone.Linphone >/dev/null 2>&1 && echo flatpak || echo native)" log "Linphone is already installed (${LINPHONE_VARIANT})."; return fi log "Installing the Linphone phone app…" if have flatpak; then flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo >/dev/null 2>&1 || true if flatpak install -y --user flathub org.linphone.Linphone >/dev/null 2>&1; then LINPHONE_VARIANT="flatpak"; fi fi if [ -z "$LINPHONE_VARIANT" ] && have snap; then $SUDO snap install linphone >/dev/null 2>&1 && LINPHONE_VARIANT="snap" || true fi if [ -z "$LINPHONE_VARIANT" ] && have apt-get; then $SUDO apt-get update -y >/dev/null 2>&1 || true $SUDO apt-get install -y linphone-desktop >/dev/null 2>&1 && LINPHONE_VARIANT="native" \ || { $SUDO apt-get install -y linphone >/dev/null 2>&1 && LINPHONE_VARIANT="native"; } || true fi [ -n "$LINPHONE_VARIANT" ] || die "Could not install Linphone automatically. Reply to your welcome email and IT will help you install it." log "Linphone installed (${LINPHONE_VARIANT})." } # ----- 3. join the tailnet ----------------------------------------------------------------------- join_tailnet() { if tailscale status --json 2>/dev/null | grep -q "\"ControlURL\":[[:space:]]*\"${LOGIN_SERVER}\""; then log "This computer is already connected to the HGVSupport VPN."; return fi log "Connecting to the HGVSupport VPN. A browser window will open — sign in with your username," log "password, and the 6-digit code from your authenticator app. (If no browser opens, copy the" log "link the next line prints into any browser.)" $SUDO tailscale up --login-server="$LOGIN_SERVER" --accept-routes \ || die "Could not connect to the VPN. Make sure you finished the browser sign-in, then re-run this installer." log "VPN connected. Your address: $(tailscale ip -4 2>/dev/null | head -1 || echo '(pending)')" } # ----- 4. configure Linphone from the welcome-email values --------------------------------------- # Prompt path (v1): the user copies extension + SIP password from the welcome email. The password is # read hidden and written straight into the local Linphone config (0600). It is never echoed/uploaded. configure_linphone() { local ext secret rc_dir rc_file ext="${HGV_EXT:-}"; secret="${HGV_SIP_SECRET:-}" if [ -z "$ext" ]; then printf '\n[HGV] Enter your EXTENSION number (from your welcome email): ' read -r ext "$rc_file" < reg_identity=sip:$ext@$SIP_SERVER reg_expires=600 reg_sendregister=1 publish=0 EOF chmod 600 "$rc_file" unset secret log "Linphone is configured for extension $ext. If Linphone was already open, close and reopen it." } install_tailscale install_linphone join_tailnet configure_linphone log "All done. Open Linphone — it should show 'Connected/Registered'. Reply to your welcome email if you need help."