#!/bin/sh # The FortWatch agent's installer, served at https://fortwatch.ai/agent/install.sh (agent.Dockerfile fills in the # addresses below). Read it, then run it as root: # # curl -fsSL https://fortwatch.ai/agent/install.sh -o fortwatch-install.sh # sudo sh fortwatch-install.sh install, ask for the server's token, start # sudo sh fortwatch-install.sh --uninstall remove everything it installed # sudo sh fortwatch-install.sh --update update an installed agent when a newer one is published # # It downloads the agent for this machine's architecture from the same place, checks it against the published # SHA-256 checksums before installing anything, creates the fortwatch-agent account, writes the settings once, # asks for the token at a prompt (never on the command line), and starts the systemd service. It also installs # fortwatch-agent-update.timer, which runs this installer again with --update every hour, as root, so the agent # follows each new version within the hour; auto_update = off in agent.conf turns that off. # Options: --endpoint URL (where reports go), --no-start (install without the service: containers, tests). # With no terminal, the token is read from standard input: printf '%s' "$TOKEN" | sudo sh fortwatch-install.sh set -eu BASE="https://fortwatch.ai/agent" ENDPOINT="https://api.fortwatch.ai/server-agent/v1/report" VERSION="0.1.10" START=1 ACTION=install while [ $# -gt 0 ]; do case "$1" in --uninstall) ACTION=uninstall ;; --update) ACTION=update ;; --no-start) START=0 ;; --endpoint) ENDPOINT=${2:?--endpoint needs a URL}; shift ;; --base) BASE=${2:?--base needs a URL}; shift ;; *) echo "Unknown option: $1" >&2; exit 2 ;; esac shift done [ "$(id -u)" -eq 0 ] || { echo "Run it as root: sudo sh $0" >&2; exit 1; } if [ "$ACTION" = uninstall ]; then if command -v systemctl >/dev/null 2>&1; then systemctl disable --now fortwatch-agent-update.timer 2>/dev/null || true systemctl disable --now fortwatch-agent 2>/dev/null || true rm -f /etc/systemd/system/fortwatch-agent.service /etc/systemd/system/fortwatch-agent-update.service /etc/systemd/system/fortwatch-agent-update.timer systemctl daemon-reload fi rm -rf /usr/lib/fortwatch-agent rm -f /usr/bin/fortwatch-agent rm -rf /etc/fortwatch-agent if id fortwatch-agent >/dev/null 2>&1; then userdel fortwatch-agent; fi echo "The FortWatch agent is removed. Remove the server in FortWatch too, or its page will say it is silent." exit 0 fi if [ "$ACTION" = update ]; then # An update needs an agent installed with its token, and does nothing when this version already runs. if [ ! -x /usr/bin/fortwatch-agent ] || [ ! -s /etc/fortwatch-agent/token ]; then echo "No installed agent to update: install it first." >&2 exit 1 fi INSTALLED=$(/usr/bin/fortwatch-agent -version 2>/dev/null || true) if [ "$INSTALLED" = "$VERSION" ]; then echo "The FortWatch agent $VERSION is up to date." exit 0 fi echo "Updating the FortWatch agent from ${INSTALLED:-an unknown version} to $VERSION." fi case "$(uname -m)" in x86_64 | amd64) ARCH=amd64 ;; aarch64 | arm64) ARCH=arm64 ;; *) echo "This machine is $(uname -m); the agent runs on amd64 and arm64." >&2; exit 1 ;; esac [ "$(uname -s)" = Linux ] || { echo "The agent runs on Linux." >&2; exit 1; } if [ "$START" = 1 ] && ! command -v systemctl >/dev/null 2>&1; then echo "The agent runs as a systemd service, and this machine has no systemd." >&2 exit 1 fi fetch() { if command -v curl >/dev/null 2>&1; then curl -fsSL "$1" -o "$2" elif command -v wget >/dev/null 2>&1; then wget -q -O "$2" "$1" else echo "Install curl or wget first." >&2; exit 1 fi } TMP=$(mktemp -d) trap 'rm -rf "$TMP"' EXIT echo "Downloading the FortWatch agent $VERSION for $ARCH from $BASE" fetch "$BASE/fortwatch-agent-linux-$ARCH" "$TMP/fortwatch-agent" fetch "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" fetch "$BASE/fortwatch-agent.service" "$TMP/fortwatch-agent.service" # Every file this installs must match its published SHA-256: the program and the service that runs it. verify() { EXPECTED=$(awk -v f="$1" '$2 == f { print $1 }' "$TMP/SHA256SUMS") ACTUAL=$(sha256sum "$2" | cut -d' ' -f1) if [ -z "$EXPECTED" ] || [ "$EXPECTED" != "$ACTUAL" ]; then echo "$1 does not match its published checksum. Nothing was installed." >&2 exit 1 fi } verify "fortwatch-agent-linux-$ARCH" "$TMP/fortwatch-agent" verify fortwatch-agent.service "$TMP/fortwatch-agent.service" if ! id fortwatch-agent >/dev/null 2>&1; then useradd --system --no-create-home --home-dir /nonexistent --shell /usr/sbin/nologin fortwatch-agent fi install -m 0755 -o root -g root "$TMP/fortwatch-agent" /usr/bin/fortwatch-agent install -d -m 0750 -o root -g fortwatch-agent /etc/fortwatch-agent if [ ! -f /etc/fortwatch-agent/agent.conf ]; then umask 027 cat >/etc/fortwatch-agent/agent.conf </dev/null; then /usr/bin/fortwatch-agent -config /etc/fortwatch-agent/agent.conf token /dev/null; then exit 0 fi TMP=$(mktemp -d) trap 'rm -rf "$TMP"' EXIT if command -v curl >/dev/null 2>&1; then curl -fsSL "$BASE/install.sh" -o "$TMP/install.sh" else wget -q -O "$TMP/install.sh" "$BASE/install.sh" fi sh "$TMP/install.sh" --update --base "$BASE" UPDATE } >/usr/lib/fortwatch-agent/update.new chmod 0755 /usr/lib/fortwatch-agent/update.new # A new file, not the same one rewritten: the update that runs this installer is still reading the old one. mv -f /usr/lib/fortwatch-agent/update.new /usr/lib/fortwatch-agent/update cat >/etc/systemd/system/fortwatch-agent-update.service <<'UNIT' [Unit] Description=FortWatch agent update Documentation=https://fortwatch.ai After=network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/bin/sh /usr/lib/fortwatch-agent/update PrivateTmp=yes Nice=10 IOSchedulingClass=idle UNIT cat >/etc/systemd/system/fortwatch-agent-update.timer <<'UNIT' [Unit] Description=FortWatch agent update, hourly [Timer] OnCalendar=hourly RandomizedDelaySec=10min Persistent=true [Install] WantedBy=timers.target UNIT systemctl daemon-reload systemctl enable --now fortwatch-agent-update.timer >/dev/null 2>&1 echo "The FortWatch agent $VERSION is running. Its next report reaches FortWatch within a minute." if grep -Eqi '^[[:space:]]*auto_update[[:space:]]*=[[:space:]]*(off|no|false)' /etc/fortwatch-agent/agent.conf 2>/dev/null; then echo "Automatic updates are off (auto_update = off in /etc/fortwatch-agent/agent.conf)." else echo "It updates itself within the hour of each new version; auto_update = off in /etc/fortwatch-agent/agent.conf turns that off." fi else echo "The FortWatch agent is installed and not started (--no-start)." fi echo "See exactly what it sends: sudo fortwatch-agent -print"