mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-29 06:02:25 +03:00
feat(api): serve the host installer and its downloads
GET /install.sh serves a POSIX script, embedded in the API at build time so the script and the routes that redeem its token ship together. It checks the platform, asks where box images should live, downloads the host agent at a pinned version, verifies it against SHA256SUMS, installs it for the calling user and hands over with the token in the environment rather than argv. GET /install/:component/:version/:asset redirects to a one-minute signed URL on a private S3-compatible bucket, so every download passes through a route that can be logged or switched off. The SigV4 signer is written against Web Crypto and checked against AWS's published example.
This commit is contained in:
Executable
+111
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env sh
|
||||
# Nestri host installer — https://api.nestri.io/install.sh
|
||||
#
|
||||
# This file is the source of what that URL serves, kept in the public
|
||||
# repository so anyone about to pipe it into a shell can read it first.
|
||||
#
|
||||
# curl -fsSL https://api.nestri.io/install.sh | sh -s -- <install-token>
|
||||
#
|
||||
# What it does, in order: check this is 64-bit Linux, ask where box images
|
||||
# should live, download the host agent for this platform, verify it against the
|
||||
# published SHA256SUMS, install it to ~/.local/bin, and hand over to
|
||||
# the agent's own onboarding, which checks the machine, registers it with the
|
||||
# token and starts the agent as a systemd user service. It never asks for sudo: the agent
|
||||
# runs as the user who ran this.
|
||||
#
|
||||
# The token comes from the dashboard's Installation page. It registers one
|
||||
# machine, works once and lapses after an hour.
|
||||
|
||||
set -eu
|
||||
|
||||
API="${NESTRI_API:-https://api.nestri.io}"
|
||||
# Pinned, not "latest", so the script and the binary it installs are a pair
|
||||
# somebody chose. Bump when cutting a release; NESTRI_HOST_VERSION overrides it.
|
||||
DEFAULT_VERSION="0.1.0"
|
||||
VERSION="${NESTRI_HOST_VERSION:-$DEFAULT_VERSION}"
|
||||
BIN_DIR="${NESTRI_BIN_DIR:-$HOME/.local/bin}"
|
||||
|
||||
say() { printf '%s\n' "$*" >&2; }
|
||||
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
TOKEN="${1:-${NESTRI_INSTALL_TOKEN:-}}"
|
||||
[ -n "$TOKEN" ] || die "no install token. Copy the full command from the dashboard's Installation page."
|
||||
|
||||
# --- platform ---------------------------------------------------------------
|
||||
[ "$(uname -s)" = Linux ] || die "a host has to run Linux (with KVM); this is $(uname -s)."
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) target=x86_64-unknown-linux-musl ;;
|
||||
*) die "no host build for $(uname -m) yet." ;;
|
||||
esac
|
||||
[ "$(id -u)" -ne 0 ] || die "run this as the user that will run boxes, not as root."
|
||||
|
||||
# --- fetch ------------------------------------------------------------------
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
get() { curl -fsSL "$1" -o "$2"; }
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
get() { wget -qO "$2" "$1"; }
|
||||
else
|
||||
die "need curl or wget"
|
||||
fi
|
||||
|
||||
# --- where box images go ----------------------------------------------------
|
||||
# Their own device, xfs or ext4, and never `/`: box images are large, and a
|
||||
# filled root filesystem takes the whole machine down with it. The agent's
|
||||
# preflight checks this again; asking here is so the default is a good guess.
|
||||
tty_ok() { [ -e /dev/tty ] && (exec 3</dev/tty) 2>/dev/null; }
|
||||
BOX_STORE="${NESTRI_BOX_STORE:-}"
|
||||
if [ -z "$BOX_STORE" ]; then
|
||||
guess="$(df -P -T -x tmpfs -x devtmpfs -x overlay 2>/dev/null \
|
||||
| awk 'NR>1 && ($2=="xfs"||$2=="ext4") && $7!="/" && $7!~/^\/(boot|efi)/ {print $5, $7}' \
|
||||
| sort -rn | awk 'NR==1 {print $2}')"
|
||||
default="${guess:+$guess/nestri}"
|
||||
if tty_ok; then
|
||||
printf 'Where should box images go? (xfs or ext4, not /) [%s]: ' "${default:-none found}" >&2
|
||||
read -r answer </dev/tty || answer=""
|
||||
BOX_STORE="${answer:-$default}"
|
||||
else
|
||||
BOX_STORE="$default"
|
||||
fi
|
||||
[ -n "$BOX_STORE" ] || die "no xfs or ext4 filesystem besides / was found. Mount one, or set NESTRI_BOX_STORE."
|
||||
fi
|
||||
|
||||
# --- download and verify ----------------------------------------------------
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT INT TERM
|
||||
ASSET="nestri-host-$target"
|
||||
BASE="$API/install/host/$VERSION"
|
||||
|
||||
say "Downloading the host agent $VERSION ($target)…"
|
||||
get "$BASE/$ASSET" "$TMP/$ASSET" || die "download failed: $BASE/$ASSET"
|
||||
|
||||
# A checksum fetched from the same place as the binary is not a security
|
||||
# boundary. It catches a truncated or corrupted download, which is the failure
|
||||
# that actually happens; the download itself is over TLS from our API.
|
||||
get "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" || die "no SHA256SUMS for $VERSION"
|
||||
want="$(grep -F " $ASSET" "$TMP/SHA256SUMS" | cut -d' ' -f1 | head -n1)"
|
||||
[ -n "$want" ] || die "no checksum for $ASSET in SHA256SUMS"
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
have="$(sha256sum "$TMP/$ASSET" | cut -d' ' -f1)"
|
||||
else
|
||||
have="$(shasum -a 256 "$TMP/$ASSET" | cut -d' ' -f1)"
|
||||
fi
|
||||
[ "$have" = "$want" ] || die "checksum mismatch — not installing
|
||||
expected $want
|
||||
got $have"
|
||||
say "Checksum OK."
|
||||
|
||||
mkdir -p "$BIN_DIR"
|
||||
chmod +x "$TMP/$ASSET"
|
||||
mv "$TMP/$ASSET" "$BIN_DIR/nestri-host"
|
||||
say "Installed $BIN_DIR/nestri-host"
|
||||
say ""
|
||||
|
||||
# --- onboard ----------------------------------------------------------------
|
||||
# The token goes through the environment rather than argv, so it is not in
|
||||
# `ps` for the length of the run.
|
||||
export NESTRI_INSTALL_TOKEN="$TOKEN" NESTRI_BOX_STORE="$BOX_STORE" NESTRI_API="$API"
|
||||
if tty_ok; then
|
||||
exec "$BIN_DIR/nestri-host" onboard </dev/tty
|
||||
else
|
||||
exec "$BIN_DIR/nestri-host" onboard
|
||||
fi
|
||||
Reference in New Issue
Block a user