update security

This commit is contained in:
David Schroeder
2026-07-26 16:41:16 -05:00
parent 825a35924d
commit ebddd792b2
8 changed files with 530 additions and 4 deletions
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/env bash
# Cluster discovery and remote update helpers for TA-ProxMenu.
TAPM_COROSYNC_CONFIG="${TAPM_COROSYNC_CONFIG:-/etc/pve/corosync.conf}"
TAPM_CLUSTER_NODES_FROM_JSON() {
python3 -c '
import json, re, sys
try:
payload = json.load(sys.stdin)
except (json.JSONDecodeError, OSError):
raise SystemExit(1)
if isinstance(payload, dict):
payload = payload.get("data", [])
if not isinstance(payload, list):
raise SystemExit(1)
valid_name = re.compile(r"^[A-Za-z0-9][A-Za-z0-9.-]{0,62}$")
rows = []
for item in payload:
if not isinstance(item, dict):
continue
name = str(item.get("node", "")).strip()
status = str(item.get("status", "unknown")).strip().lower()
if valid_name.fullmatch(name):
rows.append((name, status))
for name, status in sorted(rows):
print(f"{name}\t{status}")
'
}
TAPM_CLUSTER_NODE_ROWS() {
local node_json
node_json="$(timeout 10 pvesh get /nodes --output-format json 2>/dev/null)" ||
return 1
printf '%s' "$node_json" | TAPM_CLUSTER_NODES_FROM_JSON
}
TAPM_LOCAL_CLUSTER_NODE() {
local local_link=''
local_link="$(readlink /etc/pve/local 2>/dev/null || true)"
if [[ -n "$local_link" ]]; then
basename "$local_link"
else
hostname -s
fi
}
TAPM_UPDATE_REMOTE_NODE() {
local node="$1"
[[ "$node" =~ ^[A-Za-z0-9][A-Za-z0-9.-]{0,62}$ ]] || return 1
timeout 240 ssh \
-o BatchMode=yes \
-o ConnectTimeout=10 \
-o ServerAliveInterval=15 \
-o ServerAliveCountMax=2 \
"root@${node}" \
'/opt/idssys/ta-proxmenu/run.sh update --local-only'
}
INSTALL_CLUSTER_UPDATES() {
local index
local local_node
local node
local required_command
local status
local node_rows=''
local cluster_failed=0
local -a cluster_nodes=()
local -a cluster_statuses=()
if [[ ! -s "$TAPM_COROSYNC_CONFIG" ]]; then
INSTALL_LOCAL_UPDATES
return $?
fi
for required_command in pvesh python3 ssh timeout; do
if ! command -v "$required_command" >/dev/null 2>&1; then
echo -e "${idsCL[Red]}Cluster update requires ${required_command}; no nodes were updated.${idsCL[Default]}"
return 1
fi
done
node_rows="$(TAPM_CLUSTER_NODE_ROWS)" || {
echo -e "${idsCL[Red]}Could not retrieve the Proxmox cluster node list; no nodes were updated.${idsCL[Default]}"
return 1
}
while IFS=$'\t' read -r node status; do
[[ -n "$node" ]] || continue
cluster_nodes+=("$node")
cluster_statuses+=("$status")
done <<<"$node_rows"
if (( ${#cluster_nodes[@]} == 0 )); then
echo -e "${idsCL[Red]}The Proxmox API returned no cluster nodes; no nodes were updated.${idsCL[Default]}"
return 1
fi
local_node="$(TAPM_LOCAL_CLUSTER_NODE)"
echo -e "${idsCL[LightCyan]}Updating TA-ProxMenu across ${#cluster_nodes[@]} cluster node(s)...${idsCL[Default]}"
for index in "${!cluster_nodes[@]}"; do
node="${cluster_nodes[$index]}"
status="${cluster_statuses[$index]}"
if [[ "$node" == "$local_node" ]]; then
continue
fi
if [[ "$status" != "online" ]]; then
echo -e "${idsCL[LightYellow]}Skipping ${node}: node status is ${status}.${idsCL[Default]}"
cluster_failed=1
continue
fi
echo -e "${idsCL[LightCyan]}Updating remote node ${node}...${idsCL[Default]}"
if TAPM_UPDATE_REMOTE_NODE "$node"; then
echo -e "${idsCL[Green]}Remote node ${node} is updated.${idsCL[Default]}"
else
echo -e "${idsCL[Red]}Remote node ${node} failed to update.${idsCL[Default]}"
cluster_failed=1
fi
done
echo -e "${idsCL[LightCyan]}Updating local node ${local_node}...${idsCL[Default]}"
INSTALL_LOCAL_UPDATES || cluster_failed=1
if (( cluster_failed == 0 )); then
echo -e "${idsCL[Green]}TA-ProxMenu is updated on all ${#cluster_nodes[@]} cluster node(s).${idsCL[Default]}"
return 0
fi
echo -e "${idsCL[LightYellow]}Cluster update completed with failures; review the node messages above.${idsCL[Default]}"
return 1
}