SmartestHome/tools/fleet-bootstrap.sh

101 lines
4.6 KiB
Bash
Executable File

#!/bin/sh
# fleet-bootstrap — fetch this machine's published fleet script, run it, report back.
#
# Installed to /usr/local/bin/fleet-bootstrap on every endpoint, driven by a systemd
# timer. What it is FOR: getting each machine into CheckMK monitoring without anybody
# SSHing into eleven hosts and pasting the same installer.
#
# THIS IS REMOTE CODE EXECUTION, AND IT IS SUPPOSED TO BE. There is no way to
# "distribute a script to the endpoints" that is not. So the honesty is in the
# constraints rather than in pretending otherwise:
#
# 1. It runs the PUBLISHED version only. An upload is a draft and is invisible here
# until a human publishes it deliberately — see workshop/fleet.py.
# 2. It verifies the sha256 the server sent against the body it received, and refuses
# to run on a mismatch. That is not protection against a malicious server (the
# server chose both), it is protection against a truncated download, which is the
# failure that actually happens.
# 3. It runs a version ONCE. The recorded state file is what stops a timer from
# re-running an installer every fifteen minutes forever.
# 4. It reports what it ran, pass or fail. A script that was served is not a script
# that succeeded, and only the endpoint knows which.
# 5. It is opt-in per host: no WORKSHOP_URL, no execution.
#
# The whole capability is worth one sentence in your own words before enabling it:
# anyone who can publish to that service can run code as root on every machine here.
# Treat WORKSHOP_TOKEN accordingly — it is the most powerful credential in this project.
set -eu
CONFIG="${FLEET_CONFIG:-/etc/fleet-bootstrap.conf}"
[ -f "$CONFIG" ] && . "$CONFIG"
WORKSHOP_URL="${WORKSHOP_URL:-}"
WORKSHOP_TOKEN="${WORKSHOP_TOKEN:-}"
FLEET_PLATFORM="${FLEET_PLATFORM:-}"
STATE_DIR="${FLEET_STATE_DIR:-/var/lib/fleet-bootstrap}"
if [ -z "$WORKSHOP_URL" ] || [ -z "$WORKSHOP_TOKEN" ] || [ -z "$FLEET_PLATFORM" ]; then
echo "fleet-bootstrap: not configured (need WORKSHOP_URL, WORKSHOP_TOKEN, FLEET_PLATFORM in $CONFIG)" >&2
exit 0 # Not an error: an unconfigured host is a host that opted out.
fi
command -v curl >/dev/null 2>&1 || { echo "fleet-bootstrap: curl is required" >&2; exit 1; }
mkdir -p "$STATE_DIR"
HOSTNAME_NOW="$(hostname)"
RESPONSE="$(curl -fsS -H "Authorization: Bearer ${WORKSHOP_TOKEN}" \
"${WORKSHOP_URL%/}/fleet/script/${FLEET_PLATFORM}" 2>/dev/null || true)"
if [ -z "$RESPONSE" ]; then
# No published script for this platform yet is the normal state before somebody
# uploads one. Silence beats a daily error mail about a thing nobody has done yet.
echo "fleet-bootstrap: nothing published for ${FLEET_PLATFORM}"
exit 0
fi
# python3 rather than jq: every image in this project already has python3, and the
# script body is JSON-escaped text that a shell parser would mangle.
VERSION="$(printf '%s' "$RESPONSE" | python3 -c 'import json,sys;print(json.load(sys.stdin).get("version",""))')"
EXPECTED_SHA="$(printf '%s' "$RESPONSE" | python3 -c 'import json,sys;print(json.load(sys.stdin).get("sha256",""))')"
SCRIPT_FILE="${STATE_DIR}/${FLEET_PLATFORM}.v${VERSION}.sh"
printf '%s' "$RESPONSE" | python3 -c 'import json,sys;sys.stdout.write(json.load(sys.stdin).get("body",""))' > "$SCRIPT_FILE"
ACTUAL_SHA="$(sha256sum "$SCRIPT_FILE" | cut -d" " -f1)"
if [ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]; then
echo "fleet-bootstrap: checksum mismatch (expected $EXPECTED_SHA, got $ACTUAL_SHA) — refusing to run" >&2
rm -f "$SCRIPT_FILE"
exit 1
fi
STATE_FILE="${STATE_DIR}/ran-${FLEET_PLATFORM}"
if [ -f "$STATE_FILE" ] && [ "$(cat "$STATE_FILE")" = "$ACTUAL_SHA" ]; then
echo "fleet-bootstrap: v${VERSION} already ran here"
exit 0
fi
echo "fleet-bootstrap: running ${FLEET_PLATFORM} v${VERSION} (${ACTUAL_SHA})"
chmod 0700 "$SCRIPT_FILE"
set +e
OUTPUT="$(sh "$SCRIPT_FILE" 2>&1)"
RC=$?
set -e
if [ "$RC" -eq 0 ]; then
# Recorded only on success, so a failed run is retried on the next timer rather than
# being remembered as done.
printf '%s' "$ACTUAL_SHA" > "$STATE_FILE"
fi
# Report either way. The failure case is the one worth having on the admin page.
curl -fsS -X POST -H "Authorization: Bearer ${WORKSHOP_TOKEN}" -H "Content-Type: application/json" \
-d "$(python3 -c 'import json,sys
print(json.dumps({"hostname": sys.argv[1], "platform": sys.argv[2],
"version": int(sys.argv[3]) if sys.argv[3] else None,
"sha256": sys.argv[4], "ok": sys.argv[5] == "0",
"detail": sys.argv[6][-2000:]}))' \
"$HOSTNAME_NOW" "$FLEET_PLATFORM" "$VERSION" "$ACTUAL_SHA" "$RC" "$OUTPUT")" \
"${WORKSHOP_URL%/}/fleet/report" >/dev/null 2>&1 || \
echo "fleet-bootstrap: ran, but could not report back" >&2
exit "$RC"