#!/bin/bash # ============================================================================================== # ================================= Unraid API Key Renewal ==================================== # ============================================================================================== # # PURPOSE # ───────────────────────────────────────────────────────────────────────────── # Manages per-host named API keys in each machine's Unraid registry so every # host can call every other host's GraphQL API directly for real-time monitoring. # # KEY MODEL # ───────────────────────────────────────────────────────────────────────────── # HOST1's Unraid registry holds: # Varaverk_HOST1 — HOST1's own key (for HOST1 to call its own API) # Varaverk_HOST2 — HOST2's access key to HOST1's API (removed on offboard) # # HOST2's Unraid registry holds: # Varaverk_HOST2 — HOST2's own key # Varaverk_HOST1 — HOST1's access key to HOST2's API (removed on offboard) # # host1.conf holds: # HOST1_UNRAID_API_KEY — value of Varaverk_HOST1 from HOST1's registry # HOST2_UNRAID_API_KEY — value of Varaverk_HOST1 from HOST2's registry # (HOST1 uses this to call HOST2's GraphQL directly) # # host2.conf holds the mirror: # HOST2_UNRAID_API_KEY — value of Varaverk_HOST2 from HOST2's registry # HOST1_UNRAID_API_KEY — value of Varaverk_HOST2 from HOST1's registry # (HOST2 uses this to call HOST1's GraphQL directly) # # ── vv_remote_hosts_stats() picks up HOST2_UNRAID_API_KEY from host1.conf # automatically — direct GraphQL, no SSH needed for remote monitoring. # # ============================================================================================== # RUNTIME MODES # ============================================================================================== # # unraid_api_key_renew.sh # Local only — runs at array start. Re-registers Varaverk_HOST1 if the # ephemeral registry lost it. Fast, no SSH. # # unraid_api_key_renew.sh --all-hosts # Full cross-host key setup. Run once from Settings → API Keys → Setup. # For each partner: # • Creates Varaverk_HOST1 on PARTNER's registry → stores value in local host*.conf # • Creates Varaverk_HOST2 on LOCAL registry → SSHes value to partner's host*.conf # # unraid_api_key_renew.sh --dry-run — show what would happen # unraid_api_key_renew.sh --log — verbose output # # ============================================================================================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../load_config.sh" parse_args "$@" acquire_lock detect_hosts # Parse --all-hosts ALL_HOSTS=false for _arg in "$@"; do [[ "$_arg" == "--all-hosts" ]] && ALL_HOSTS=true; done unset _arg LOCAL_CONF="$SCRIPT_DIR/../Configurations/${MY_ID,,}.conf" LOCAL_KEY_NAME="Varaverk_${MY_ID}" LOCAL_VAR="${MY_ID}_UNRAID_API_KEY" log "$ICON_GEAR Local conf: $LOCAL_CONF" log "$ICON_GEAR Key name: $LOCAL_KEY_NAME" log "$ICON_GEAR All hosts: $ALL_HOSTS" if [[ ! -f "$LOCAL_CONF" ]]; then error "Conf file not found: $LOCAL_CONF"; exit 1 fi [[ "$DRY_RUN" == true ]] && { warn "DRY RUN — would check/create $LOCAL_KEY_NAME in registry and sync to host conf" [[ "$ALL_HOSTS" == true ]] && warn "DRY RUN — would also SSH all partners for cross-host key setup" exit 0 } # ── Helper: ensure a named key exists in the LOCAL registry, return its value ── _ensure_local_key() { local name="$1" local existing key existing=$(timeout 5 /usr/local/sbin/unraid-api apikey --name "$name" --json /dev/null) key=$(echo "$existing" | jq -r '.key // empty' 2>/dev/null) if [[ -n "$key" ]]; then echo "$key"; return 0 fi local raw raw=$(timeout 10 /usr/local/sbin/unraid-api apikey \ --name "$name" --create --overwrite \ --description "Varaverk plugin" --roles ADMIN --json &1) key=$(echo "$raw" | jq -r '.key // empty' 2>/dev/null) [[ -z "$key" ]] && { error "Failed to create $name: ${raw:0:200}"; return 1; } echo "$key" } # ── Helper: write HOST*_UNRAID_API_KEY into a conf file ──────────────────────── _write_key() { local conf="$1" var="$2" key="$3" [[ ! -f "$conf" ]] && return 1 if grep -q "^\s*${var}\s*=" "$conf"; then sed -i "s|^\(\s*${var}\s*=\s*\)\"[^\"]*\"|\1\"${key}\"|" "$conf" else printf ' %s="%s"\n' "$var" "$key" >> "$conf" fi } # ── Helper: ensure a named key exists on a REMOTE registry via SSH ───────────── _ensure_remote_key() { local ssh_key="$1" remote_ip="$2" key_name="$3" ssh -i "$ssh_key" \ -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o BatchMode=yes \ "root@${remote_ip}" " EXISTING=\$(timeout 5 /usr/local/sbin/unraid-api apikey --name '${key_name}' --json /dev/null) KEY=\$(echo \"\$EXISTING\" | jq -r '.key // empty' 2>/dev/null) if [[ -n \"\$KEY\" ]]; then echo \"\$KEY\" else timeout 10 /usr/local/sbin/unraid-api apikey \\ --name '${key_name}' --create --overwrite \\ --description 'Varaverk plugin' --roles ADMIN --json /dev/null \\ | jq -r '.key // empty' 2>/dev/null fi " 2>/dev/null | tr -d '[:space:]' } # ────────────────────────────────────────────────────────────────────────────── # Step 1 — Local key (always) # ────────────────────────────────────────────────────────────────────────────── echo "" echo "━━━ $ICON_GEAR Local — $LOCAL_KEY_NAME ━━━" LOCAL_KEY=$(_ensure_local_key "$LOCAL_KEY_NAME") if [[ -z "$LOCAL_KEY" ]]; then error "Could not obtain $LOCAL_KEY_NAME from registry" exit 1 fi # Sync to conf only if changed CONF_KEY=$(grep "^\s*${LOCAL_VAR}\s*=" "$LOCAL_CONF" 2>/dev/null | sed 's/.*="\(.*\)".*/\1/' | tr -d '[:space:]') if [[ "$CONF_KEY" != "$LOCAL_KEY" ]]; then _write_key "$LOCAL_CONF" "$LOCAL_VAR" "$LOCAL_KEY" warn "Synced $LOCAL_VAR → ${LOCAL_KEY:0:8}...${LOCAL_KEY: -4}" else echo " $LOCAL_VAR valid ✅ — ${LOCAL_KEY:0:8}...${LOCAL_KEY: -4}" fi [[ "$ALL_HOSTS" != true ]] && exit 0 # ────────────────────────────────────────────────────────────────────────────── # Step 2 — Cross-host key setup (--all-hosts) # ────────────────────────────────────────────────────────────────────────────── echo "" echo "━━━ $ICON_SYNC Cross-host key setup ━━━" PARTNER_OK=0; PARTNER_FAIL=0 for host_var in HOST1 HOST2 HOST3 HOST4 HOST5 HOST6 HOST7 HOST8; do [[ "$host_var" == "$MY_ID" ]] && continue hostname="${!host_var:-}"; [[ -z "$hostname" ]] && continue r_var="${host_var}_UNRAID_API_KEY" # e.g. HOST2_UNRAID_API_KEY r_my_key_name="Varaverk_${MY_ID}" # key HOST1 uses on HOST2's registry r_their_key_name="Varaverk_${host_var}" # key HOST2 uses on HOST1's registry r_conf="/boot/config/plugins/varaverk/Configurations/${host_var,,}.conf" echo "" echo " $host_var ($hostname)" REMOTE_IP=$(resolve_tailscale_ip "$hostname") if [[ -z "$REMOTE_IP" ]]; then warn " $host_var: cannot resolve Tailscale IP — skipping" (( PARTNER_FAIL++ )); continue fi # ── A: Get HOST1's access key from HOST2's registry ──────────────────────── # Creates Varaverk_HOST1 on HOST2 → store in host1.conf as HOST2_UNRAID_API_KEY log " Creating $r_my_key_name on $host_var registry…" MY_KEY_ON_REMOTE=$(_ensure_remote_key "$SSH_KEY" "$REMOTE_IP" "$r_my_key_name") if [[ -z "$MY_KEY_ON_REMOTE" ]]; then warn " $host_var: could not create $r_my_key_name on their registry" (( PARTNER_FAIL++ )); continue fi _write_key "$LOCAL_CONF" "$r_var" "$MY_KEY_ON_REMOTE" echo " A ✅ $r_my_key_name on $host_var → stored as $r_var in ${MY_ID,,}.conf" # ── B: Create HOST2's access key on HOST1's registry ────────────────────── # Creates Varaverk_HOST2 on HOST1 → store in host2.conf as HOST1_UNRAID_API_KEY log " Creating $r_their_key_name on local registry…" THEIR_KEY_ON_LOCAL=$(_ensure_local_key "$r_their_key_name") if [[ -z "$THEIR_KEY_ON_LOCAL" ]]; then warn " Could not create $r_their_key_name on local registry" (( PARTNER_FAIL++ )); continue fi # Write to partner's conf via SSH ssh -i "$SSH_KEY" \ -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o BatchMode=yes \ "root@${REMOTE_IP}" " CONF='$r_conf' VAR='${MY_ID}_UNRAID_API_KEY' KEY='$THEIR_KEY_ON_LOCAL' [[ ! -f \"\$CONF\" ]] && exit 1 if grep -q \"^\s*\${VAR}\s*=\" \"\$CONF\"; then sed -i \"s|\(\s*\${VAR}\s*=\s*\)\\\"[^\\\"]*\\\"|\1\\\"\${KEY}\\\"|\" \"\$CONF\" else printf ' %s=\"%s\"\n' \"\$VAR\" \"\$KEY\" >> \"\$CONF\" fi " 2>/dev/null \ && echo " B ✅ $r_their_key_name on local → stored as ${MY_ID}_UNRAID_API_KEY in ${host_var,,}.conf" \ || warn " B: could not write ${MY_ID}_UNRAID_API_KEY to ${host_var,,}.conf" (( PARTNER_OK++ )) done echo "" echo "━━━━━ $ICON_SUMMARY Key Setup Summary ━━━━━" echo " Local key: $LOCAL_VAR ✅" echo " Partner setup: $PARTNER_OK ok · $PARTNER_FAIL failed" echo "" echo " HOST1 can now call HOST2's GraphQL directly using HOST2_UNRAID_API_KEY" echo " HOST2 can now call HOST1's GraphQL directly using HOST1_UNRAID_API_KEY" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"