#!/usr/bin/env bash
# scripts/maas-region-power-key.sh <check|install> <site> <rack-virsh-uri> [--commit]
#
# Install and VERIFY the per-DC MAAS->libvirt power key inside a MAAS region's
# snap. Runs ON the region host (the snap's own machine). The private key is
# read from STDIN on `install`, so it is never written to an intermediate host
# and never appears in a process argument.
#
#   check   <site> <uri>            READ-ONLY. Exit 0 only if the key is present
#                                   with 0600, the ssh config block targets the
#                                   right host with IdentitiesOnly, AND a REAL
#                                   `virsh -c <uri> version` succeeds as the snap
#                                   runs it (root).
#   install <site> <uri> --commit   Idempotent. Key on stdin. DRY without
#                                   --commit.
#
# WHY THIS EXISTS (2026-07-30, D-132 q1 per-DC MAAS regions).
# `runbooks/dc-dc-phase2-tofu-dc-substrate.md` prerequisite 5 carries this as
# PROSE only -- and prose is not a gate. It has to be re-run at every DC standup
# and, per the SAME prerequisite's own FRAGILITY note, after EVERY MAAS snap
# refresh, because the key and config live under per-revision
# `/var/snap/maas/current/`. A per-DC secret with a documented re-assert
# requirement and no tool is the class this repo ruled against on 2026-07-30
# ("every per-DC secret needs a rotation tool, not just a generation recipe").
#
# SEC-012 (dc0) / SEC-016 (dc1): each DC gets its OWN power key, never a
# cross-DC reuse. This script does not mint -- it installs the key it is given
# and REFUSES to overwrite a DIFFERENT key already in place (that would be a
# silent cross-DC swap), so rotation is deliberate rather than accidental.
#
# ASSERT ON THE ARTIFACT, NEVER THE CONFIG: `check` ends on a real virsh call.
# A config file that names the right key proves nothing about whether libvirt
# accepts it -- the 2026-07-30 Octavia PKI work paid for this lesson twice.
#
# Exit: 0 all assertions pass | 1 assertion(s) failed | 2 could not evaluate.
# ASCII + LF.
set -uo pipefail

ACTION="${1:-}"; SITE="${2:-}"; URI="${3:-}"
COMMIT=0
if [ "$#" -gt 3 ]; then shift 3; else set --; fi
for a in "$@"; do
  case "$a" in
    --commit) COMMIT=1 ;;
    *) echo "FAIL: unknown option '$a'" >&2; exit 2 ;;
  esac
done

usage() {
  echo "usage: maas-region-power-key.sh <check|install> <site> <rack-virsh-uri> [--commit]" >&2
  echo "  e.g. maas-region-power-key.sh check   vr1-dc0 qemu+ssh://jessea123@10.12.8.2/system" >&2
  echo "       maas-region-power-key.sh install vr1-dc0 qemu+ssh://jessea123@10.12.8.2/system --commit < key" >&2
  exit 2
}
[ -n "$ACTION" ] && [ -n "$SITE" ] && [ -n "$URI" ] || usage
case "$ACTION" in check|install) ;; *) usage ;; esac

# Site token shape: <region>-<dc>. DERIVE the label, never type it (the
# transposition class the octavia-pki work was hardened against).
case "$SITE" in
  *-*) DCLABEL="${SITE#*-}" ;;
  *)   echo "FAIL: site '$SITE' is not <region>-<dc> (e.g. vr1-dc0)" >&2; exit 2 ;;
esac
[ -n "$DCLABEL" ] || { echo "FAIL: could not derive a DC label from site '$SITE'" >&2; exit 2; }

# Parse user@host out of qemu+ssh://user@host/system. Refuse anything else --
# a bare host would silently dial as root.
case "$URI" in
  qemu+ssh://*@*/*) ;;
  *) echo "FAIL: uri '$URI' is not qemu+ssh://<user>@<host>/<path>" >&2; exit 2 ;;
esac
_rest="${URI#qemu+ssh://}"
USERHOST="${_rest%%/*}"
SSHUSER="${USERHOST%@*}"
RACKHOST="${USERHOST#*@}"
[ -n "$SSHUSER" ] && [ -n "$RACKHOST" ] || { echo "FAIL: could not parse user/host from '$URI'" >&2; exit 2; }

# MAAS_SNAP_ROOT exists so the harness can exercise the derivation and parsing
# logic without a snap. In production it is never set and the default is the
# per-revision path the FRAGILITY note warns about.
SNAPBASE="${MAAS_SNAP_ROOT:-/var/snap/maas/current}"
SNAPROOT="$SNAPBASE/root/.ssh"
KEY="$SNAPROOT/id_${DCLABEL}_power"
CFG="$SNAPROOT/config"

# Echo the DERIVED values before any privileged call, so a transposition is
# visible in the capture even when a precondition later refuses.
echo "maas-region-power-key $ACTION: site=$SITE dc=$DCLABEL rack=$RACKHOST user=$SSHUSER"
echo "  key=$KEY"

command -v sudo >/dev/null 2>&1 || { echo "REFUSE: no sudo on this host" >&2; exit 2; }
sudo -n true 2>/dev/null || { echo "REFUSE: passwordless sudo unavailable -- cannot read the snap's root-owned ssh dir" >&2; exit 2; }
[ -d "$SNAPBASE" ] || { echo "REFUSE: $SNAPBASE absent -- is the MAAS snap installed on this host?" >&2; exit 2; }

# ASSERT WITH THE BINARY MAAS ACTUALLY USES. The region host has no `virsh` of
# its own (measured on vr1-dc0-maas-01, 2026-07-30) -- the snap ships one, and
# that is what MAAS's virsh power driver drives, against the snap's own ssh
# config. Checking with a host-installed virsh would grade a different program
# reading a different config, which is precisely the "assert the artifact, never
# the config" failure this repo keeps paying for.
#
# A SNAP HAS TWO ROOTS AND THEY ARE NOT INTERCHANGEABLE -- measured the hard way
# on the first live run, which REFUSED with "no virsh binary found":
#   /var/snap/<name>/current  WRITABLE data (root/.ssh lives here)
#   /snap/<name>/current      READ-ONLY squashfs (the binaries live here)
# The first version of this script looked for virsh under the DATA root and
# found nothing, while `find /snap/maas/current -name virsh` had already located
# it. SNAPEXEC is derived from SNAPBASE rather than typed, so the harness
# override still works and the two cannot drift apart.
# AND THE SNAP'S BINARY MUST BE RUN INSIDE THE SNAP'S RUNTIME. Invoking
# $SNAPEXEC/usr/bin/virsh directly fails -- measured on the second live run:
#   "error while loading shared libraries: libvirt-lxc.so.0"
# because the snap's libraries are not on the host's loader path. `snap run
# --shell <snap> -c 'virsh ...'` executes it with the snap's environment AND its
# ssh config, which is exactly the context MAAS's own power driver uses.
SNAPEXEC="${MAAS_SNAP_EXEC:-${SNAPBASE#/var}}"
_sb="${SNAPBASE%/current}"; SNAPNAME="${_sb##*/}"
VIRSH_MODE=""
if [ -n "${VIRSH_BIN:-}" ]; then
  VIRSH_MODE="direct"                     # harness override
elif command -v snap >/dev/null 2>&1 && [ -x "$SNAPEXEC/usr/bin/virsh" ]; then
  VIRSH_MODE="snap"
elif command -v virsh >/dev/null 2>&1; then
  VIRSH_MODE="host"; VIRSH_BIN="$(command -v virsh)"
fi

# virsh_run <args...> -- dispatch to whichever mode was resolved.
virsh_run() {
  case "$VIRSH_MODE" in
    direct) sudo -n "$VIRSH_BIN" "$@" ;;
    host)   sudo -n "$VIRSH_BIN" "$@" ;;
    snap)   sudo -n snap run --shell "$SNAPNAME" -c "virsh $*" ;;
    *)      return 127 ;;
  esac
}
virsh_label() {
  case "$VIRSH_MODE" in
    snap) echo "snap run --shell $SNAPNAME -c virsh" ;;
    *)    echo "$VIRSH_BIN" ;;
  esac
}

# DERIVE the public half FROM the private key, then fingerprint that.
# `ssh-keygen -lf <privkey>` reads an ADJACENT <privkey>.pub when one exists and
# reports ITS fingerprint -- so a stale sibling .pub makes the comparison answer
# about the wrong key entirely. Found by this script's own harness, 2026-07-30,
# where a leftover .pub let a freshly-installed key read back as a different one.
# `-y` reads the private key and cannot be fooled by a neighbouring file.
fp_of_privkey() { # $1 = path (root-owned); prints the SHA256 fingerprint only
  sudo -n ssh-keygen -y -f "$1" 2>/dev/null | ssh-keygen -lf - 2>/dev/null | awk '{print $2}'
}
fp_of_local_privkey() { # $1 = path readable by this user
  ssh-keygen -y -f "$1" 2>/dev/null | ssh-keygen -lf - 2>/dev/null | awk '{print $2}'
}

# ---------------------------------------------------------------- check ----
do_check() {
  local pass=0 fail=0
  ck() { if [ "$1" = 0 ]; then echo "  [ok]   $2"; pass=$((pass+1)); else echo "  [FAIL] $2"; fail=$((fail+1)); fi; }

  sudo -n test -f "$KEY"; ck $? "power key present"
  if ! sudo -n test -f "$KEY"; then
    echo "REFUSE: no key to evaluate -- 'could not look' is not 'nothing wrong'" >&2
    echo "maas-region-power-key: $pass passed, $fail failed"; return 2
  fi

  local mode; mode="$(sudo -n stat -c %a "$KEY" 2>/dev/null)"
  [ "$mode" = "600" ]; ck $? "key mode is 0600 (got ${mode:-unknown})"

  local owner; owner="$(sudo -n stat -c %U "$KEY" 2>/dev/null)"
  [ "$owner" = "root" ]; ck $? "key owned by root (got ${owner:-unknown})"

  local fp; fp="$(fp_of_privkey "$KEY")"
  [ -n "$fp" ]; ck $? "key parses as a valid PRIVATE key${fp:+ ($fp)}"

  sudo -n test -f "$CFG"; ck $? "ssh config present"
  # Assert the config actually BINDS this rack host to this key. A config that
  # merely mentions the key proves nothing -- match the Host stanza.
  local blk
  blk="$(sudo -n awk -v h="$RACKHOST" '
    $1=="Host" { inblk = 0; for (i=2;i<=NF;i++) if ($i==h) inblk=1 }
    inblk { print }
  ' "$CFG" 2>/dev/null)"
  printf '%s' "$blk" | grep -q "IdentityFile[[:space:]]\+$KEY"; ck $? "config binds Host $RACKHOST -> $KEY"
  printf '%s' "$blk" | grep -qi "IdentitiesOnly[[:space:]]\+yes"; ck $? "config sets IdentitiesOnly yes for $RACKHOST"

  # THE ARTIFACT ASSERTION. Everything above is configuration; only this proves
  # the region can actually drive power on that rack.
  if [ -z "$VIRSH_MODE" ]; then
    echo "REFUSE: no usable virsh found (looked for the $SNAPNAME snap's own, and PATH)" >&2
    echo "        the config assertions above cannot substitute for the artifact test" >&2
    echo "maas-region-power-key: $pass passed, $fail failed"; return 2
  fi
  local vout vrc
  vout="$(virsh_run -c "$URI" version 2>&1)"; vrc=$?
  [ "$vrc" -eq 0 ]; ck $? "REAL virsh ($(virsh_label)) connect to $URI succeeds"
  if [ "$vrc" -ne 0 ]; then
    echo "  virsh said: $(printf '%s' "$vout" | tr '\n' ' ' | cut -c1-200)"
  fi

  # And that it can enumerate domains -- a connect that lists nothing would let a
  # power op no-op silently.
  local n
  n="$(virsh_run -c "$URI" list --all --name 2>/dev/null | sed '/^$/d' | wc -l)"
  [ "${n:-0}" -gt 0 ]; ck $? "virsh enumerates domains on the rack (got ${n:-0})"

  echo "maas-region-power-key: $pass passed, $fail failed"
  [ "$fail" -eq 0 ] || return 1
  return 0
}

# -------------------------------------------------------------- install ----
do_install() {
  if [ -t 0 ]; then
    echo "FAIL: install reads the private key from STDIN -- redirect one in" >&2
    exit 2
  fi
  local tmp; tmp="$(mktemp)"; chmod 600 "$tmp"
  # shellcheck disable=SC2064
  trap "shred -u '$tmp' 2>/dev/null || rm -f '$tmp'" EXIT
  cat > "$tmp"
  [ -s "$tmp" ] || { echo "FAIL: empty key on stdin" >&2; exit 2; }
  local newfp; newfp="$(fp_of_local_privkey "$tmp")"
  [ -n "$newfp" ] || { echo "FAIL: stdin is not a readable private key" >&2; exit 2; }

  # REFUSE a silent cross-DC swap: if a DIFFERENT key already sits here, stop.
  if sudo -n test -f "$KEY"; then
    local curfp; curfp="$(fp_of_privkey "$KEY")"
    if [ "$curfp" = "$newfp" ]; then
      echo "[idempotent] $KEY already holds this key ($newfp)"
    else
      echo "REFUSE: $KEY already holds a DIFFERENT key" >&2
      echo "        present: ${curfp:-unparseable}" >&2
      echo "        offered: $newfp" >&2
      echo "        Overwriting would silently re-point this region's power path." >&2
      echo "        Move the existing key aside deliberately if rotation is intended." >&2
      exit 1
    fi
  fi

  if [ "$COMMIT" -eq 0 ]; then
    echo "DRY RUN (no --commit). Would:"
    echo "  install -d -m 0700 -o root -g root $SNAPROOT"
    echo "  install -m 0600 -o root -g root <stdin-key> $KEY      # fp $newfp"
    echo "  append Host $RACKHOST block to $CFG -> IdentityFile $KEY"
    return 0
  fi

  sudo -n install -d -m 0700 -o root -g root "$SNAPROOT" || { echo "FAIL: could not create $SNAPROOT" >&2; exit 1; }
  sudo -n install -m 0600 -o root -g root "$tmp" "$KEY"  || { echo "FAIL: could not install $KEY" >&2; exit 1; }

  sudo -n touch "$CFG" && sudo -n chmod 0644 "$CFG" && sudo -n chown root:root "$CFG"
  # Idempotent: only append if this Host has no stanza yet.
  if sudo -n awk -v h="$RACKHOST" '$1=="Host"{for(i=2;i<=NF;i++) if($i==h) found=1} END{exit !found}' "$CFG" 2>/dev/null; then
    echo "[idempotent] $CFG already has a Host $RACKHOST stanza -- left as-is"
  else
    printf '\nHost %s\n  IdentityFile %s\n  IdentitiesOnly yes\n  StrictHostKeyChecking accept-new\n' \
      "$RACKHOST" "$KEY" | sudo -n tee -a "$CFG" >/dev/null
    echo "appended Host $RACKHOST stanza to $CFG"
  fi

  echo "installed. verifying:"
  do_check
  return $?
}

case "$ACTION" in
  check)   do_check;   exit $? ;;
  install) do_install; exit $? ;;
esac
