#!/bin/sh # ArgusNOC Linux probe installer (POSIX sh). Debian 12/13, Ubuntu 22.04/24.04, amd64/arm64. # # curl -sL https://get.argusnoc.net | sh # curl -sL https://get.argusnoc.net | sh -s -- --name waw1 --country PL --city Warsaw --email noc@example.com # curl -sL https://get.argusnoc.net | sh -s -- --uninstall [--purge] # # Downloads the latest release from the center, verifies size + sha256 + ed25519 signature, # unpacks it into /opt/argus-probe and hands over to `argus-probe install` (questions, enroll, # firewall, systemd). Running it again on an installed host updates the probe in place. set -eu INSTALLER_VERSION="2.0" BASE="/opt/argus-probe" CENTER="${ARGUS_CENTER:-https://api.argusnoc.net}" PUBKEY_HEX="1316481bf58c80c886a391d9d5bc03813076da30878dde2ebf1ac604e51c9b2a" WANT_VERSION="latest" UNINSTALL=0 PURGE=0 PASS="" log() { printf '%s\n' "$*"; } die() { printf 'error: %s\n' "$*" >&2; exit 1; } # ---- arguments (everything except --version/--uninstall/--purge is passed to argus-probe install) while [ $# -gt 0 ]; do case "$1" in --version) [ $# -ge 2 ] || die "--version needs a value"; WANT_VERSION="$2"; shift 2 ;; --version=*) WANT_VERSION="${1#--version=}"; shift ;; --center) [ $# -ge 2 ] || die "--center needs a value"; CENTER="${2%/}"; PASS="$PASS --center $2"; shift 2 ;; --center=*) CENTER="${1#--center=}"; CENTER="${CENTER%/}"; PASS="$PASS $1"; shift ;; --uninstall) UNINSTALL=1; shift ;; --purge) PURGE=1; shift ;; -h|--help) sed -n '2,10p' "$0" 2>/dev/null || true log "flags: --name NAME --country CC --city CITY --email MAIL [--org KEY] [--center URL]" log " [--ssh-port N] [--extra-inbound 80,443] [--sources ip1,ip2] [--primary IP] [--force-firewall]" log " [--version X.Y.Z] [--uninstall [--purge]]" exit 0 ;; *) PASS="$PASS $1"; shift ;; esac done [ "$(id -u)" = "0" ] || die "run as root (sudo sh install.sh ...)" # ---- uninstall if [ "$UNINSTALL" = "1" ]; then if [ -x "$BASE/argus-probe" ]; then if [ "$PURGE" = "1" ]; then exec "$BASE/argus-probe" uninstall --purge; else exec "$BASE/argus-probe" uninstall; fi fi log "argus-probe binary not found — removing leftovers" systemctl disable --now argus-probe argus-agentd 2>/dev/null || true rm -f /etc/systemd/system/argus-probe.service /etc/systemd/system/argus-agentd.service systemctl daemon-reload 2>/dev/null || true rm -f /etc/sysctl.d/90-argus.conf /etc/security/limits.d/argus.conf if [ -f /etc/nftables.conf ] && grep -q "generated by argus-probe" /etc/nftables.conf 2>/dev/null; then if [ -f "$BASE/data/nftables.conf.bak" ]; then cp "$BASE/data/nftables.conf.bak" /etc/nftables.conf; nft -f /etc/nftables.conf || true else nft flush ruleset 2>/dev/null || true; rm -f /etc/nftables.conf; systemctl disable nftables 2>/dev/null || true; fi fi if [ "$PURGE" = "1" ]; then rm -rf /var/lib/argus-probe.bak; else mkdir -p /var/lib/argus-probe.bak && chmod 700 /var/lib/argus-probe.bak cp -f "$BASE/secrets.env" /var/lib/argus-probe.bak/ 2>/dev/null || true cp -f "$BASE/config.yaml" /var/lib/argus-probe.bak/ 2>/dev/null || true cp -f "$BASE/data/state.json" /var/lib/argus-probe.bak/ 2>/dev/null || true fi rm -rf "$BASE" log "✅ ArgusNOC probe removed" exit 0 fi # ---- platform ARCH="$(uname -m)" case "$ARCH" in x86_64) BIN="argus-probe" ;; aarch64|arm64) BIN="argus-probe.arm64" ;; *) die "unsupported architecture $ARCH (amd64 and arm64 only)" ;; esac OS_ID=""; OS_VER="" if [ -f /etc/os-release ]; then OS_ID="$(. /etc/os-release; printf '%s' "${ID:-}")" OS_VER="$(. /etc/os-release; printf '%s' "${VERSION_ID:-}")" fi case "$OS_ID/$OS_VER" in debian/12|debian/13|ubuntu/22.04|ubuntu/24.04) ;; *) log "⚠ $OS_ID $OS_VER is not a tested platform (Debian 12/13, Ubuntu 22.04/24.04) — continuing" ;; esac command -v systemctl >/dev/null 2>&1 || die "systemd is required" # ---- packages log "• installing packages (curl ca-certificates nftables mtr-tiny iperf3 openssl)" if command -v apt-get >/dev/null 2>&1; then export DEBIAN_FRONTEND=noninteractive if command -v debconf-set-selections >/dev/null 2>&1; then echo "iperf3 iperf3/start_daemon boolean false" | debconf-set-selections 2>/dev/null || true fi apt-get update -qq >/dev/null 2>&1 || log "⚠ apt-get update failed — trying to continue" apt-get install -y -qq curl ca-certificates nftables mtr-tiny iperf3 openssl tar gzip >/dev/null 2>&1 \ || apt-get install -y -qq curl ca-certificates nftables mtr-tiny openssl tar gzip >/dev/null 2>&1 \ || log "⚠ apt-get install failed — make sure curl, nftables, mtr-tiny and openssl are present" systemctl disable --now iperf3 >/dev/null 2>&1 || true else log "⚠ apt-get not found — install curl, nftables, mtr-tiny, iperf3 and openssl manually" fi for t in curl tar sha256sum openssl; do command -v "$t" >/dev/null 2>&1 || die "$t is required"; done # ---- release metadata META_URL="$CENTER/probe/releases/meta/$WANT_VERSION?os=linux" log "• fetching release metadata: $META_URL" META="$(curl -fsSL --retry 3 --max-time 30 -H 'Cache-Control: no-cache' -H "User-Agent: argus-probe-installer/$INSTALLER_VERSION" "$META_URL")" \ || die "cannot fetch release metadata from $CENTER" META="$(printf '%s' "$META" | tr -d '\n' | sed 's/"notes"[[:space:]]*:[[:space:]]*"\([^"\\]*\(\\.[^"\\]*\)*\)"//')" jstr() { printf '%s' "$META" | sed -n 's/.*"'"$1"'"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1; } jnum() { printf '%s' "$META" | sed -n 's/.*"'"$1"'"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' | head -n 1; } VERSION="$(jstr version)"; URL="$(jstr url)"; API_URL="$(jstr api_url)"; SHA="$(jstr sha256)"; SIG="$(jstr signature)" SIZE="$(jnum size)"; MIN_INST="$(jstr min_installer)" [ -n "$VERSION" ] && [ -n "$URL$API_URL" ] && [ -n "$SHA" ] || die "incomplete release metadata: $META" if [ -n "$MIN_INST" ]; then lowest="$(printf '%s\n%s\n' "$MIN_INST" "$INSTALLER_VERSION" | sort -V | head -n 1)" [ "$lowest" = "$MIN_INST" ] || die "this installer ($INSTALLER_VERSION) is older than required ($MIN_INST) — re-run: curl -sL https://get.argusnoc.net | sh" fi CUR=""; [ -f "$BASE/VERSION" ] && CUR="$(cat "$BASE/VERSION" 2>/dev/null || true)" if [ -n "$CUR" ]; then log "• installed: $CUR, release: $VERSION"; else log "• release: $VERSION"; fi # ---- download (resumable), verify TMP="$(mktemp -d /tmp/argus-probe.XXXXXX)" trap 'rm -rf "$TMP"' EXIT PKG="$TMP/argus-probe_v${VERSION}_linux.tar.gz" dl() { curl -fL --retry 3 --retry-delay 3 --max-time 900 -C - -o "$PKG" -H 'Cache-Control: no-cache' -H "User-Agent: argus-probe-installer/$INSTALLER_VERSION" "$1"; } log "• downloading $URL" if ! dl "$URL" 2>/dev/null; then [ -n "$API_URL" ] || die "download failed" log "• CDN download failed — trying $API_URL" dl "$API_URL" 2>/dev/null || die "download failed" fi if [ -n "$SIZE" ]; then got="$(wc -c < "$PKG" | tr -d ' ')" [ "$got" = "$SIZE" ] || die "size mismatch: $got != $SIZE" fi got_sha="$(sha256sum "$PKG" | cut -d' ' -f1)" [ "$got_sha" = "$(printf '%s' "$SHA" | tr 'A-F' 'a-f')" ] || die "sha256 mismatch" # ed25519 over ":" with the embedded public key (openssl ≥ 1.1.1) [ -n "$SIG" ] || die "release $VERSION is not signed — refusing to install" hex2bin() { h="$(cat | tr -d ' \n')" while [ -n "$h" ]; do b="${h%"${h#??}"}"; h="${h#??}" printf "\\$(printf '%03o' "$((0x$b))")" done } { printf '%s' "302a300506032b6570032100$PUBKEY_HEX" | hex2bin | base64; } | { printf -- '-----BEGIN PUBLIC KEY-----\n'; cat; printf -- '-----END PUBLIC KEY-----\n'; } > "$TMP/pub.pem" printf '%s' "$SIG" | hex2bin > "$TMP/sig.bin" printf '%s:%s' "$VERSION" "$got_sha" > "$TMP/msg" if ! openssl pkeyutl -verify -pubin -inkey "$TMP/pub.pem" -rawin -in "$TMP/msg" -sigfile "$TMP/sig.bin" >/dev/null 2>&1; then die "signature verification FAILED for $VERSION — refusing to install" fi log "• package verified (sha256 + signature)" # ---- unpack into /opt/argus-probe (config.yaml, secrets.env, data/, logs/ are never replaced) mkdir -p "$TMP/x" && tar -xzf "$PKG" -C "$TMP/x" [ -d "$TMP/x/argus-probe" ] || die "unexpected package layout" [ -f "$TMP/x/argus-probe/$BIN" ] || die "package has no $BIN" mkdir -p "$BASE/data" "$BASE/logs" for it in argus-probe argus-probe.arm64 install.sh VERSION CHANGELOG.md README.md systemd sysctl; do rm -rf "$BASE/$it" [ -e "$TMP/x/argus-probe/$it" ] && cp -a "$TMP/x/argus-probe/$it" "$BASE/$it" done if [ "$BIN" != "argus-probe" ]; then mv -f "$BASE/$BIN" "$BASE/argus-probe"; else rm -f "$BASE/argus-probe.arm64"; fi [ -f "$BASE/config.yaml" ] || cp "$TMP/x/argus-probe/config.yaml" "$BASE/config.yaml" chmod 755 "$BASE/argus-probe" "$BASE/install.sh" chmod 700 "$BASE/data" 2>/dev/null || true log "• unpacked $VERSION into $BASE" # ---- hand over # shellcheck disable=SC2086 exec "$BASE/argus-probe" install $PASS