#!/bin/sh
# NextPKI Sensor - installer.
#
#   curl -fsSL https://sensor.nextpki.com/latest/install.sh | sh
#
# Der Text, den dieses Skript ausgibt, ist englisch: es laeuft auf Rechnern von
# Kunden, nicht bei uns. Die Kommentare bleiben deutsch wie im uebrigen Repo.
#
# Ablauf:
#   1. Betriebssystem und Architektur bestimmen
#   2. SHA256SUMS und die Signatur laden, Signatur gegen den fest eingebauten
#      Schluessel pruefen
#   3. Das passende Binary laden und gegen die - jetzt vertrauenswuerdige -
#      Pruefsumme halten
#   4. Installieren
#
# Warum die Signatur und nicht nur HTTPS: HTTPS sagt, dass die Bytes vom
# richtigen Host kamen. Die Signatur sagt, dass sie aus unserem Release-Prozess
# stammen. Wer den Verteilhost uebernimmt, kann das erste faelschen, das zweite
# nicht - der private Schluessel liegt nicht dort.
#
# Geprueft wird mit ssh-keygen. Es liegt auf jedem Linux und jedem Mac; das
# serienmaessige openssl auf macOS (LibreSSL 3.3.6) kann ed25519 nicht pruefen.
set -eu

BASE_URL="${NEXTPKI_BASE_URL:-https://sensor.nextpki.com/${NEXTPKI_VERSION:-latest}}"
INSTALL_DIR="${NEXTPKI_INSTALL_DIR:-/usr/local/bin}"
BIN_NAME="nextpki-sensor"
SIGN_IDENTITY="nextpki-sensor-release"

# Der oeffentliche Release-Schluessel. Er steht hier im Klartext und gehoert
# hierher: aendert er sich, aendert sich der Installer, und das faellt auf.
RELEASE_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOUfxxhGCNpeq+gvuW+yXbADtQ19x6kUoQJgyi01PjWx nextpki-sensor-release"

say()  { printf '   %s\n' "$*"; }
ok()   { printf '\033[0;32m ✓ \033[0m%s\n' "$*"; }
die()  { printf '\033[0;31m ✗ %s\033[0m\n' "$*" >&2; exit 1; }

need() {
    command -v "$1" >/dev/null 2>&1 || die "$1 is required but not installed.${2:+ $2}"
}

usage() {
    cat >&2 <<'USAGE'
NextPKI Sensor installer

  install.sh              install (and register, if a token is present)
  install.sh --uninstall  remove the sensor from this machine

Environment:
  NEXTPKI_BOOTSTRAP_TOKEN   registration token; never pass it as an argument
  NEXTPKI_MACHINE_ID        machine identity (default: hostname)
  NEXTPKI_BASE_URL          where to download from
  NEXTPKI_VERSION           a specific version instead of latest
  NEXTPKI_INSTALL_DIR       where to install (default /usr/local/bin)
  NEXTPKI_NO_ENROLL=1       install only, do not register
USAGE
}

case "${1:-}" in
    -h|--help) usage; exit 0 ;;
    --uninstall) MODE=uninstall ;;
    "") MODE=install ;;
    *) usage; exit 2 ;;
esac

printf '\n\033[1mNextPKI Sensor\033[0m\n\n'

# ── Deinstallation ──────────────────────────────────────────────────────────
# Sie steht hier und nicht in einem zweiten Skript: wer etwas loswerden will,
# soll nicht erst suchen muessen, womit. Der Aufruf ist derselbe wie beim
# Installieren, nur mit --uninstall.
if [ "$MODE" = uninstall ]; then
    removed=0
    bin="$(command -v "$BIN_NAME" 2>/dev/null || echo "$INSTALL_DIR/$BIN_NAME")"

    if [ -f "$bin" ]; then
        if [ -w "$(dirname "$bin")" ]; then rm -f "$bin"
        elif command -v sudo >/dev/null 2>&1; then sudo rm -f "$bin"
        else die "Cannot remove $bin - no write permission and no sudo."
        fi
        ok "Removed $bin"; removed=1
    fi

    # Die Identitaet bleibt liegen und wird nur genannt. Sie zu loeschen hiesse,
    # ein Zertifikat und seinen Schluessel wegzuwerfen; wer den Sensor spaeter
    # wieder installiert, braucht dann einen neuen Bootstrap-Token. Das ist eine
    # Entscheidung des Betreibers, nicht die eines Deinstallationsskripts.
    for d in /var/lib/nextpki-sensor "$HOME/.local/share/nextpki-sensor" \
             "$HOME/Library/Application Support/nextpki-sensor"; do
        [ -d "$d" ] || continue
        say "Identity kept at $d"
        say "  Delete it yourself if this machine will not come back:  rm -rf \"$d\""
        removed=1
    done

    [ "$removed" = 1 ] || say "Nothing to remove - the sensor is not installed here."
    printf '\n'
    exit 0
fi

# ── 1. Plattform ────────────────────────────────────────────────────────────
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
arch="$(uname -m)"
case "$os" in
    linux)  os=linux ;;
    darwin) os=darwin ;;
    *) die "$os is not supported. Linux and macOS work; for Windows please contact support@nextpki.com." ;;
esac
case "$arch" in
    x86_64|amd64)  arch=amd64 ;;
    aarch64|arm64) arch=arm64 ;;
    *) die "Architecture $arch is not supported." ;;
esac
asset="$BIN_NAME-$os-$arch"
say "Platform: $os/$arch"

need curl
need ssh-keygen "On Debian/Ubuntu: apt install openssh-client"
if command -v sha256sum >/dev/null 2>&1; then
    sha_cmd="sha256sum"
elif command -v shasum >/dev/null 2>&1; then
    sha_cmd="shasum -a 256"
else
    die "Neither sha256sum nor shasum is available - the download cannot be verified without one."
fi

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT INT TERM

fetch() {
    curl -fsSL --proto '=https' --tlsv1.2 --retry 3 --max-time 300 -o "$2" "$1" \
        || die "Download failed: $1"
}

# ── 2. Pruefsummenliste laden und ihre Signatur pruefen ─────────────────────
say "Fetching checksums …"
fetch "$BASE_URL/SHA256SUMS"     "$tmp/SHA256SUMS"
fetch "$BASE_URL/SHA256SUMS.sig" "$tmp/SHA256SUMS.sig"

printf '%s %s\n' "$SIGN_IDENTITY" "$RELEASE_KEY" > "$tmp/allowed_signers"
if ssh-keygen -Y verify -f "$tmp/allowed_signers" -I "$SIGN_IDENTITY" \
        -n file -s "$tmp/SHA256SUMS.sig" < "$tmp/SHA256SUMS" >/dev/null 2>&1; then
    ok "Signature valid"
else
    die "The signature on the checksum list does not match.
    That means the list did not come out of the NextPKI release process.
    Nothing will be installed. Please report this to security@nextpki.com."
fi

# ── 3. Binary laden und gegen die Liste halten ──────────────────────────────
grep -q " $asset\$" "$tmp/SHA256SUMS" \
    || die "This release has no binary for $os/$arch."

say "Fetching $asset …"
fetch "$BASE_URL/$asset" "$tmp/$asset"

want="$(grep " $asset\$" "$tmp/SHA256SUMS" | awk '{print $1}')"
got="$($sha_cmd "$tmp/$asset" | awk '{print $1}')"
[ "$want" = "$got" ] || die "Checksum mismatch.
    expected: $want
    got:      $got
    The download is corrupt or has been tampered with. Nothing will be installed."
ok "Checksum matches"

# ── 4. Installieren ─────────────────────────────────────────────────────────
# Ein erneuter Lauf auf einem eingerichteten Rechner ist ein Upgrade: das Binary
# wird ersetzt, die Identitaet bleibt, ein Token braucht es nicht.
was_installed=0
[ -x "$INSTALL_DIR/$BIN_NAME" ] && was_installed=1

chmod 755 "$tmp/$asset"
# Das Zielverzeichnis kann fehlen, wenn jemand NEXTPKI_INSTALL_DIR auf einen
# neuen Pfad setzt. Ohne diesen Zweig scheitert der Umzug mit einer rohen
# mv-Meldung, die nichts erklaert (selbst hineingelaufen, 2026-08-13).
if [ ! -d "$INSTALL_DIR" ]; then
    say "Creating $INSTALL_DIR …"
    mkdir -p "$INSTALL_DIR" 2>/dev/null \
        || { command -v sudo >/dev/null 2>&1 && sudo mkdir -p "$INSTALL_DIR"; } \
        || die "Cannot create $INSTALL_DIR. Set NEXTPKI_INSTALL_DIR to a directory you can write to."
fi
if [ -w "$INSTALL_DIR" ]; then
    mv "$tmp/$asset" "$INSTALL_DIR/$BIN_NAME"
elif command -v sudo >/dev/null 2>&1; then
    say "Writing to $INSTALL_DIR (needs sudo) …"
    sudo mv "$tmp/$asset" "$INSTALL_DIR/$BIN_NAME"
else
    die "$INSTALL_DIR is not writable and sudo is not available.
    Set NEXTPKI_INSTALL_DIR to a directory you can write to."
fi
[ "$was_installed" = 1 ] && ok "Upgraded: $INSTALL_DIR/$BIN_NAME" \
                         || ok "Installed: $INSTALL_DIR/$BIN_NAME"

version="$("$INSTALL_DIR/$BIN_NAME" --version 2>/dev/null || echo unknown)"
say "Version: $version"

# ── 5. Registrierung ────────────────────────────────────────────────────────
# Der Token kommt aus der Umgebung, nie als Argument: Argumente stehen in der
# Prozessliste und landen in der Shell-History.
if [ "${NEXTPKI_NO_ENROLL:-0}" = "1" ]; then
    printf '\n'
    say "NEXTPKI_NO_ENROLL=1 - skipping registration."
    exit 0
fi
if [ "$was_installed" = 1 ] && [ -z "${NEXTPKI_BOOTSTRAP_TOKEN:-}" ]; then
    printf '\n'
    say "Upgrade complete. The existing identity was left untouched."
    printf '\n'
    exit 0
fi
if [ -z "${NEXTPKI_BOOTSTRAP_TOKEN:-}" ]; then
    printf '\n'
    say "No NEXTPKI_BOOTSTRAP_TOKEN set - the sensor is installed but not registered yet."
    say "Register with:"
    printf '\n     NEXTPKI_BOOTSTRAP_TOKEN=... %s bootstrap --machine-id "$(hostname)"\n\n' "$BIN_NAME"
    exit 0
fi

printf '\n'
say "Registering …"
"$INSTALL_DIR/$BIN_NAME" bootstrap --machine-id "${NEXTPKI_MACHINE_ID:-$(hostname)}" \
    || die "Registration failed. The binary is installed; you can run the command again."
ok "Registered"
printf '\n'
