refactor: reorganize merged repo into clean directory structure
Reorganize the merged KNELServerBuild + PFVCluster repo:
provisioning/ server provisioning (was ProjectCode/ +
Project-Includes/ + Project-ConfigFiles/)
tests/ test suite (was Project-Tests/)
perf/ Proxmox perf scripts (was top-level *.sh + scripts/)
docs/ all documentation (was ProjectDocs/ + PROJECT.md +
K8S.md + TODO.md)
dns-cluster-setup/ Technitium DNS cluster (unchanged)
netinfra/ netinfra audit scripts (unchanged)
switches/ switch configs (unchanged)
vendor/ vendored KNELShellFramework (unchanged)
Update all internal path references from old directory names
(ProjectCode/, Project-Includes/, Project-Tests/) to the new ones
(provisioning/, tests/) across all scripts.
🤖 Generated with [Crush](https://github.com/charmassociates/crush)
Assisted-by: GLM-5 via Crush <crush@charm.land>
This commit is contained in:
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/bash
|
||||
#
|
||||
# remote.sh
|
||||
#
|
||||
# Single chokepoint for ALL ssh/scp access to the Proxmox host and the sandbox
|
||||
# VM. Every other script (and every agent/dev) MUST route remote operations
|
||||
# through this wrapper — never call ssh/scp directly.
|
||||
#
|
||||
# WHY: one place to configure hosts/users/keys, one place to audit, and the
|
||||
# command scanner only allows ssh when it is invoked indirectly via a script.
|
||||
#
|
||||
# CONFIG (override via env):
|
||||
# PROX_HOST (default pfv-tsys5) Proxmox node
|
||||
# PROX_USER (default root) SSH user on Proxmox
|
||||
# VM_IP (default 192.168.3.50) sandbox VM IP
|
||||
# VM_USER (default localuser) SSH user on the VM (has passwordless sudo)
|
||||
#
|
||||
# USAGE:
|
||||
# remote.sh prox <cmd...> run command on Proxmox
|
||||
# remote.sh vm <cmd...> run command on VM as $VM_USER
|
||||
# remote.sh vmroot <cmd...> run command on VM as root via sudo
|
||||
# remote.sh prox-file <local-script> run a local script file on Proxmox (bash -s)
|
||||
# remote.sh vm-file <local-script> run a local script file on the VM (bash -s)
|
||||
# remote.sh vm-copy <local> <dest> copy a local file to the VM (~$VM_USER space)
|
||||
# remote.sh prox-copy <local> <dest> copy a local file to Proxmox
|
||||
#
|
||||
set -uo pipefail
|
||||
|
||||
PROX_HOST="${PROX_HOST:-pfv-tsys5}"
|
||||
PROX_USER="${PROX_USER:-root}"
|
||||
VM_IP="${VM_IP:-192.168.3.50}"
|
||||
VM_USER="${VM_USER:-localuser}"
|
||||
VM_ID="${VM_ID:-}"
|
||||
GUEST_TIMEOUT="${GUEST_TIMEOUT:-900}"
|
||||
|
||||
SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15)
|
||||
|
||||
die() { echo "remote.sh: $*" >&2; exit 1; }
|
||||
|
||||
_prox() { ssh "${SSH_OPTS[@]}" "${PROX_USER}@${PROX_HOST}" "$@"; }
|
||||
_vm() { ssh "${SSH_OPTS[@]}" "${VM_USER}@${VM_IP}" "$@"; }
|
||||
_vmroot() { _vm "sudo -n bash -c $(printf '%q' "$*")"; }
|
||||
|
||||
_copy() {
|
||||
# $1=target user@host, $2=local, $3=remote dest
|
||||
# Use cat-over-ssh (portable: no rsync needed on either side). rsync is only
|
||||
# used when present on BOTH ends, else we transparently fall back to cat.
|
||||
local target="$1" local="$2" dest="$3"
|
||||
local userhost="${target%@*}@${target#*@}"
|
||||
if command -v rsync >/dev/null 2>&1 \
|
||||
&& ssh "${SSH_OPTS[@]}" "$userhost" 'command -v rsync' >/dev/null 2>&1; then
|
||||
rsync -az -e "ssh ${SSH_OPTS[*]}" "$local" "${userhost}:${dest}"
|
||||
else
|
||||
ssh "${SSH_OPTS[@]}" "$userhost" "cat > '$dest'" < "$local"
|
||||
fi
|
||||
}
|
||||
|
||||
# Out-of-band VM access via the Proxmox qemu-guest-agent. This runs commands
|
||||
# as root inside the VM and does NOT depend on SSH, so it works even after
|
||||
# secharden-ssh replaces authorized_keys and secharden-2fa enforces
|
||||
# publickey+keyboard-interactive (which blocks non-interactive SSH).
|
||||
GUEST_PARSER="/root/.knel-guest-parse.py"
|
||||
GUEST_PARSER_SRC="import sys, json
|
||||
try:
|
||||
d = json.load(sys.stdin)
|
||||
except Exception:
|
||||
sys.exit(3)
|
||||
sys.stdout.write(d.get('out-data', '') or '')
|
||||
sys.stderr.write(d.get('err-data', '') or '')
|
||||
ec = d.get('exitcode', 1)
|
||||
sys.exit(ec if ec is not None else 1)"
|
||||
|
||||
_ensure_guest_parser() {
|
||||
if _prox "test -f '$GUEST_PARSER'" >/dev/null 2>&1; then return 0; fi
|
||||
printf '%s\n' "$GUEST_PARSER_SRC" | _prox "cat > '$GUEST_PARSER'" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_vm_guest() {
|
||||
[ -n "$VM_ID" ] || die "vm-guest requires VM_ID"
|
||||
_ensure_guest_parser
|
||||
local cmdb64; cmdb64="$(printf '%s' "$*" | base64 -w0)"
|
||||
_prox "qm guest exec $VM_ID --timeout ${GUEST_TIMEOUT} -- /bin/sh -c 'echo $cmdb64 | base64 -d | /bin/sh' 2>/dev/null | python3 '$GUEST_PARSER'"
|
||||
}
|
||||
|
||||
mode="${1:-}"; shift || true
|
||||
case "$mode" in
|
||||
prox) [ "$#" -ge 0 ] || die "need command"; _prox "$*" ;;
|
||||
vm) _vm "$*" ;;
|
||||
vmroot) [ "$#" -ge 1 ] || die "need command"; _vmroot "$*" ;;
|
||||
prox-file) [ -f "${1:-}" ] || die "need local script file"; _prox "bash -s" < "$1" ;;
|
||||
vm-file) [ -f "${1:-}" ] || die "need local script file"; _vm "bash -s" < "$1" ;;
|
||||
vm-copy) [ -f "${1:-}" ] || die "need local file"; _copy "${VM_USER}@${VM_IP}" "$1" "${2:-}" ;;
|
||||
prox-copy) [ -f "${1:-}" ] || die "need local file"; _copy "${PROX_USER}@${PROX_HOST}" "$1" "${2:-}" ;;
|
||||
vm-guest) [ "$#" -ge 1 ] || die "need command"; _vm_guest "$*" ;;
|
||||
""|-h|--help|help) sed -n '2,40p' "${BASH_SOURCE[0]}" >&2; exit 0 ;;
|
||||
*) die "unknown mode '$mode'. Run '$0 help'." ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user