Introduce make lint-shell / fmt-shell with shellcheck and shfmt (-i 2 -ci -bn), normalize first-party scripts, and document the new targets. Keep them out of make lint / fmt. Signed-off-by: Ludovic Vielle <ludovic@probo.com>
366 lines
9.7 KiB
Bash
Executable File
366 lines
9.7 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# probo-agent installer for Darwin, Linux, and FreeBSD.
|
|
#
|
|
# Downloads the matching GitHub Release binary, verifies its sha256
|
|
# checksum (embedded in this script at release time), installs to
|
|
# /usr/local/bin, then enrolls the device.
|
|
#
|
|
# Usage:
|
|
#
|
|
# # Interactive — curl install.sh from the target probo-agent/v* release
|
|
# curl -fsSL "https://github.com/getprobo/probo/releases/download/probo-agent/vX.Y.Z/install.sh" | sudo sh
|
|
#
|
|
# # Unattended / MDM
|
|
# curl -fsSL "…/install.sh" | sudo \
|
|
# PROBO_SERVER_URL=https://us.probo.com \
|
|
# PROBO_ENROLLMENT_TOKEN='…' sh
|
|
#
|
|
# # Mirror the release assets (must match the embedded release tag)
|
|
# PROBO_AGENT_RELEASE_BASE="https://release-base/probo-agent/vX.Y.Z" \
|
|
# curl -fsSL "…/install.sh" | sudo sh
|
|
#
|
|
# # Explicit flags
|
|
# curl -fsSL "…/install.sh" | sudo sh -s -- \
|
|
# --server https://us.probo.com \
|
|
# --enrollment-token '…'
|
|
#
|
|
# Environment variables:
|
|
# PROBO_AGENT_RELEASE_BASE Release download base URL (default: embedded tag)
|
|
# PROBO_AGENT_RELEASE_TAG Override embedded release tag (local dev)
|
|
# PROBO_AGENT_SKIP_CHECKSUM_VERIFY Set to true to skip SHA-256 verification (local dev)
|
|
# PROBO_AGENT_STATE_DIR Agent state directory passed as --dir (default: /var/lib/probo-agent)
|
|
# PROBO_SERVER_URL Probo server base URL
|
|
# PROBO_ENROLLMENT_TOKEN One-shot enrollment token
|
|
# PROBO_NO_AUTO_UPDATE Set to true to pass --no-auto-update
|
|
#
|
|
# Never pass the enrollment token in the curl URL.
|
|
|
|
set -eu
|
|
# pipefail is a bash/ksh extension; enable when available.
|
|
# shellcheck disable=SC3040
|
|
(set -o pipefail 2>/dev/null) && set -o pipefail
|
|
|
|
BINARY_PATH="/usr/local/bin/probo-agent"
|
|
GITHUB_RELEASES_URL="https://github.com/getprobo/probo/releases/download"
|
|
|
|
# Injected at release time by .github/workflows/release-probo-agent.yaml
|
|
RELEASE_TAG="__PROBO_AGENT_RELEASE_TAG__"
|
|
if [ -n "${PROBO_AGENT_RELEASE_TAG:-}" ]; then
|
|
RELEASE_TAG="$PROBO_AGENT_RELEASE_TAG"
|
|
fi
|
|
|
|
RELEASE_BASE="${PROBO_AGENT_RELEASE_BASE:-}"
|
|
SERVER_URL="${PROBO_SERVER_URL:-}"
|
|
ENROLLMENT_TOKEN="${PROBO_ENROLLMENT_TOKEN:-}"
|
|
STATE_DIR="${PROBO_AGENT_STATE_DIR:-}"
|
|
NO_AUTO_UPDATE="${PROBO_NO_AUTO_UPDATE:-}"
|
|
SKIP_SERVICE=false
|
|
|
|
die() {
|
|
echo "error: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
can_prompt() {
|
|
[ -t 0 ] && return 0
|
|
[ -r /dev/tty ] && [ -w /dev/tty ]
|
|
}
|
|
|
|
read_user() {
|
|
if [ -t 0 ]; then
|
|
IFS= read -r "$1"
|
|
else
|
|
IFS= read -r "$1" </dev/tty
|
|
fi
|
|
}
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
probo-agent installer for Darwin, Linux, and FreeBSD.
|
|
|
|
Usage:
|
|
curl -fsSL "…/install.sh" | sudo sh
|
|
curl -fsSL "…/install.sh" | sudo sh -s -- --server URL --enrollment-token TOKEN
|
|
|
|
Environment variables:
|
|
PROBO_AGENT_RELEASE_BASE Release download base URL (default: embedded tag)
|
|
PROBO_AGENT_RELEASE_TAG Override embedded release tag (local dev)
|
|
PROBO_AGENT_SKIP_CHECKSUM_VERIFY Set to true to skip SHA-256 verification (local dev)
|
|
PROBO_AGENT_STATE_DIR Agent state directory (--dir; default /var/lib/probo-agent)
|
|
PROBO_SERVER_URL Probo server base URL
|
|
PROBO_ENROLLMENT_TOKEN One-shot enrollment token
|
|
PROBO_NO_AUTO_UPDATE Set to true to disable auto-update
|
|
EOF
|
|
}
|
|
|
|
embedded_checksums() {
|
|
cat <<'EOF'
|
|
# __PROBO_AGENT_CHECKSUMS_BEGIN__
|
|
# __PROBO_AGENT_CHECKSUMS_END__
|
|
EOF
|
|
}
|
|
|
|
resolve_embedded_release() {
|
|
if [ "$RELEASE_TAG" = "__PROBO_AGENT_RELEASE_TAG__" ]; then
|
|
die "this install.sh was not published by a probo-agent release; curl install.sh from the target release"
|
|
fi
|
|
|
|
if [ -n "$RELEASE_BASE" ]; then
|
|
RELEASE_BASE="${RELEASE_BASE%/}"
|
|
case "$RELEASE_BASE" in
|
|
*/"$RELEASE_TAG") ;;
|
|
*) die "PROBO_AGENT_RELEASE_BASE must end with release tag ${RELEASE_TAG}" ;;
|
|
esac
|
|
return 0
|
|
fi
|
|
|
|
RELEASE_BASE="${GITHUB_RELEASES_URL}/${RELEASE_TAG}"
|
|
printf 'Using release %s\n' "$RELEASE_TAG"
|
|
}
|
|
|
|
require_cmd() {
|
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
die "required command not found: $1"
|
|
fi
|
|
}
|
|
|
|
detect_platform() {
|
|
os="$(uname -s)"
|
|
arch="$(uname -m)"
|
|
|
|
case "$os" in
|
|
Darwin) os_label="Darwin" ;;
|
|
Linux) os_label="Linux" ;;
|
|
FreeBSD) os_label="Freebsd" ;;
|
|
*) die "unsupported operating system: $os (Darwin, Linux, and FreeBSD only)" ;;
|
|
esac
|
|
|
|
case "$arch" in
|
|
x86_64 | amd64) arch_label="x86_64" ;;
|
|
arm64 | aarch64) arch_label="arm64" ;;
|
|
*) die "unsupported CPU architecture: $arch" ;;
|
|
esac
|
|
|
|
archive_dir="probo-agent_${os_label}_${arch_label}"
|
|
archive_name="${archive_dir}.tar.gz"
|
|
}
|
|
|
|
sha256_file() {
|
|
file="$1"
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
sha256sum "$file" | awk '{print $1}'
|
|
elif command -v shasum >/dev/null 2>&1; then
|
|
shasum -a 256 "$file" | awk '{print $1}'
|
|
elif command -v sha256 >/dev/null 2>&1; then
|
|
sha256 -q "$file"
|
|
else
|
|
die "no sha256 tool found (need sha256sum, shasum, or sha256)"
|
|
fi
|
|
}
|
|
|
|
verify_embedded_checksum() {
|
|
case "${PROBO_AGENT_SKIP_CHECKSUM_VERIFY:-}" in
|
|
1 | true | TRUE | yes | YES) return 0 ;;
|
|
esac
|
|
|
|
archive_file="$1"
|
|
archive_basename="$(basename "$archive_file")"
|
|
|
|
expected="$(
|
|
embedded_checksums | awk -v name="$archive_basename" '
|
|
$0 ~ /^#/ { next }
|
|
$2 == name { print $1; exit }
|
|
'
|
|
)"
|
|
if [ -z "$expected" ]; then
|
|
die "archive ${archive_basename} not found in embedded release checksums"
|
|
fi
|
|
|
|
actual="$(sha256_file "$archive_file")"
|
|
if [ "$expected" != "$actual" ]; then
|
|
die "checksum mismatch for ${archive_file}"
|
|
fi
|
|
}
|
|
|
|
read_secret() {
|
|
prompt_text="$1"
|
|
printf '%s' "$prompt_text"
|
|
if command -v stty >/dev/null 2>&1; then
|
|
if [ -t 0 ]; then
|
|
old_stty="$(stty -g 2>/dev/null || true)"
|
|
stty -echo 2>/dev/null || true
|
|
IFS= read -r REPLY || REPLY=""
|
|
printf '\n'
|
|
if [ -n "${old_stty:-}" ]; then
|
|
stty "$old_stty" 2>/dev/null || stty echo 2>/dev/null || true
|
|
fi
|
|
else
|
|
old_stty="$(stty -g </dev/tty 2>/dev/null || true)"
|
|
stty -echo </dev/tty 2>/dev/null || true
|
|
IFS= read -r REPLY </dev/tty || REPLY=""
|
|
printf '\n' >/dev/tty
|
|
if [ -n "${old_stty:-}" ]; then
|
|
stty "$old_stty" </dev/tty 2>/dev/null || stty echo </dev/tty 2>/dev/null || true
|
|
fi
|
|
fi
|
|
else
|
|
read_user REPLY
|
|
fi
|
|
ENROLLMENT_TOKEN="$REPLY"
|
|
}
|
|
|
|
prompt_server_url() {
|
|
if [ -n "$SERVER_URL" ]; then
|
|
return 0
|
|
fi
|
|
if ! can_prompt; then
|
|
die "PROBO_SERVER_URL is required in non-interactive mode"
|
|
fi
|
|
|
|
printf '\nProbo server URL:\n'
|
|
printf ' 1) https://us.probo.com (United States)\n'
|
|
printf ' 2) https://eu.probo.com (European Union)\n'
|
|
printf ' 3) Enter a custom URL\n'
|
|
printf 'Choice [1]: '
|
|
read_user choice
|
|
|
|
case "${choice:-1}" in
|
|
1 | "") SERVER_URL="https://us.probo.com" ;;
|
|
2) SERVER_URL="https://eu.probo.com" ;;
|
|
3)
|
|
printf 'Server URL: '
|
|
read_user SERVER_URL
|
|
;;
|
|
*) SERVER_URL="$choice" ;;
|
|
esac
|
|
|
|
if [ -z "$SERVER_URL" ]; then
|
|
die "server URL is required"
|
|
fi
|
|
}
|
|
|
|
prompt_enrollment_token() {
|
|
if [ -n "$ENROLLMENT_TOKEN" ]; then
|
|
return 0
|
|
fi
|
|
if ! can_prompt; then
|
|
die "PROBO_ENROLLMENT_TOKEN is required in non-interactive mode"
|
|
fi
|
|
|
|
read_secret "Enrollment token: "
|
|
if [ -z "$ENROLLMENT_TOKEN" ]; then
|
|
die "enrollment token is required"
|
|
fi
|
|
}
|
|
|
|
parse_args() {
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--server)
|
|
[ $# -ge 2 ] || die "--server requires a value"
|
|
SERVER_URL="$2"
|
|
shift 2
|
|
;;
|
|
--enrollment-token)
|
|
[ $# -ge 2 ] || die "--enrollment-token requires a value"
|
|
ENROLLMENT_TOKEN="$2"
|
|
shift 2
|
|
;;
|
|
--no-auto-update)
|
|
NO_AUTO_UPDATE=true
|
|
shift
|
|
;;
|
|
--skip-service)
|
|
SKIP_SERVICE=true
|
|
shift
|
|
;;
|
|
--dir)
|
|
[ $# -ge 2 ] || die "--dir requires a value"
|
|
STATE_DIR="$2"
|
|
shift 2
|
|
;;
|
|
-h | --help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
die "unknown option: $1 (try --help)"
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
run_agent_install() {
|
|
set -- --server "$SERVER_URL" --enrollment-token "$ENROLLMENT_TOKEN"
|
|
if [ -n "$STATE_DIR" ]; then
|
|
set -- "$@" --dir "$STATE_DIR"
|
|
fi
|
|
case "$NO_AUTO_UPDATE" in
|
|
1 | true | TRUE | yes | YES) set -- "$@" --no-auto-update ;;
|
|
esac
|
|
case "$SKIP_SERVICE" in
|
|
1 | true | TRUE | yes | YES) set -- "$@" --skip-service ;;
|
|
esac
|
|
"$BINARY_PATH" install "$@"
|
|
}
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
die "this installer must run as root; re-run with: curl -fsSL \"…/install.sh\" | sudo sh"
|
|
fi
|
|
|
|
require_cmd curl
|
|
require_cmd tar
|
|
require_cmd install
|
|
|
|
detect_platform
|
|
resolve_embedded_release
|
|
|
|
workdir="$(mktemp -d "${TMPDIR:-/tmp}/probo-agent-install.XXXXXX")"
|
|
trap 'rm -rf "$workdir"' EXIT INT HUP TERM
|
|
|
|
printf 'Downloading probo-agent %s …\n' "$archive_name"
|
|
|
|
curl -fsSL "${RELEASE_BASE}/${archive_name}" -o "${workdir}/${archive_name}"
|
|
|
|
verify_embedded_checksum "${workdir}/${archive_name}"
|
|
|
|
tar -xzf "${workdir}/${archive_name}" -C "$workdir"
|
|
if [ ! -f "${workdir}/${archive_dir}/probo-agent" ]; then
|
|
die "archive did not contain probo-agent binary"
|
|
fi
|
|
|
|
install -m 0755 "${workdir}/${archive_dir}/probo-agent" "$BINARY_PATH"
|
|
printf 'Installed %s\n' "$BINARY_PATH"
|
|
|
|
prompt_server_url
|
|
prompt_enrollment_token
|
|
|
|
printf 'Enrolling device …\n'
|
|
if run_agent_install; then
|
|
enroll_ok=true
|
|
else
|
|
enroll_ok=false
|
|
fi
|
|
|
|
if [ "$enroll_ok" = true ]; then
|
|
case "$SKIP_SERVICE" in
|
|
1 | true | TRUE | yes | YES)
|
|
printf 'Device enrolled (service installation skipped).\n'
|
|
;;
|
|
*)
|
|
printf 'Device enrolled and service installed.\n'
|
|
;;
|
|
esac
|
|
else
|
|
printf 'warning: probo-agent install failed; binary is at %s\n' "$BINARY_PATH" >&2
|
|
printf 'Re-run: %s install --server … --enrollment-token …\n' "$BINARY_PATH" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
main "$@"
|