mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-15 11:16:50 +00:00
Add audit and reports page, and a change journal
ProxMenux modifies the host: it rewrites configuration files, installs packages, enables services. Until now nobody could say afterwards what had changed, and showing the script does not answer that question — a four-hundred-line function may alter two values, and the reader has no way to know which two. This adds the two halves of an answer. The change journal records what ProxMenux does as it does it. Eleven bash primitives capture the previous state, apply the change and record it in the same step, writing to a spool that the Monitor reads back. One hundred and thirteen functions across twenty-five scripts are instrumented, covering post-install, shared storage, security tooling, container conversions, disk operations and the PVE 8 to 9 upgrade path. The page shows the difference — rotate 7 becoming rotate 14 — and never the script. Restore and backup scripts are deliberately left out: a restore puts the host back to a state some other script already recorded. The Audit and reports page answers the other half: what state is this host in, regardless of who put it there. Forty-three checks across seven areas read the host and classify each result as critical, warning, observation, conformant, unverified or not applicable, with the evidence they read attached to each one. A declared policy lets the reader say what this particular host is expected to do — which guests must have a backup, which storages are essential — so the report judges the host against its own intent rather than a generic template. An inventory records the hardware, network and guest topology behind those readings, a comparison shows what moved between two runs, and six report profiles produce a printable document scoped to what the reader needs. Everything is available in the eight supported languages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,9 @@ if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" ]]; then
|
||||
elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -74,6 +77,9 @@ register_vfio_iommu_tool() {
|
||||
}
|
||||
|
||||
enable_iommu_cmdline() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "enable_iommu_cmdline" "$FUNC_VERSION"
|
||||
|
||||
local silent="${1:-}"
|
||||
local cpu_vendor iommu_param
|
||||
cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}')
|
||||
@@ -95,7 +101,8 @@ enable_iommu_cmdline() {
|
||||
if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file" 2>/dev/null; then
|
||||
if ! grep -q "$iommu_param" "$cmdline_file" || ! grep -q "iommu=pt" "$cmdline_file"; then
|
||||
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
|
||||
pmx_edit_file "$cmdline_file" "s|\\s*$| ${iommu_param} iommu=pt|"
|
||||
pmx_record_execution "refresh Proxmox boot entries" "proxmox-boot-tool refresh"
|
||||
proxmox-boot-tool refresh >/dev/null 2>&1 || true
|
||||
[[ "$silent" != "silent" ]] && msg_ok "$(translate "IOMMU parameters added to /etc/kernel/cmdline")"
|
||||
else
|
||||
@@ -104,7 +111,8 @@ enable_iommu_cmdline() {
|
||||
elif [[ -f "$grub_file" ]]; then
|
||||
if ! grep -q "$iommu_param" "$grub_file" || ! grep -q "iommu=pt" "$grub_file"; then
|
||||
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
|
||||
pmx_edit_file "$grub_file" "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|"
|
||||
pmx_record_execution "regenerate GRUB configuration" "update-grub"
|
||||
update-grub >/dev/null 2>&1 || true
|
||||
[[ "$silent" != "silent" ]] && msg_ok "$(translate "IOMMU parameters added to GRUB")"
|
||||
else
|
||||
@@ -521,6 +529,9 @@ prompt_controller_conflict_policy() {
|
||||
|
||||
# ── DIALOG PHASE: resolve all conflicts before terminal ───────────────────────
|
||||
resolve_disk_conflicts() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "resolve_disk_conflicts" "$FUNC_VERSION"
|
||||
|
||||
local -a new_pci_list=()
|
||||
local pci vmid action slot_base scope_key has_running
|
||||
|
||||
@@ -559,13 +570,18 @@ resolve_disk_conflicts() {
|
||||
case "$action" in
|
||||
keep_disable_onboot)
|
||||
for vmid in "${source_vms[@]}"; do
|
||||
_vm_onboot_is_enabled "$vmid" && qm set "$vmid" -onboot 0 >/dev/null 2>&1
|
||||
if _vm_onboot_is_enabled "$vmid"; then
|
||||
pmx_record_execution "disable autostart for source VM ${vmid}" "qm set ${vmid} -onboot 0"
|
||||
qm set "$vmid" -onboot 0 >/dev/null 2>&1
|
||||
fi
|
||||
done
|
||||
new_pci_list+=("$pci")
|
||||
;;
|
||||
move_remove_source)
|
||||
slot_base=$(_pci_slot_base "$pci")
|
||||
for vmid in "${source_vms[@]}"; do
|
||||
pmx_record_execution "remove PCI slot ${slot_base} from source VM ${vmid}" \
|
||||
"_remove_pci_slot_from_vm_config ${vmid} ${slot_base}"
|
||||
_remove_pci_slot_from_vm_config "$vmid" "$slot_base"
|
||||
done
|
||||
new_pci_list+=("$pci")
|
||||
@@ -616,10 +632,15 @@ resolve_disk_conflicts() {
|
||||
for gid in "${guest_ids[@]}"; do
|
||||
gtype="${gid%%:*}"; gid_num="${gid##*:}"
|
||||
if [[ "$gtype" == "VM" ]]; then
|
||||
_vm_onboot_is_enabled "$gid_num" && qm set "$gid_num" -onboot 0 >/dev/null 2>&1
|
||||
if _vm_onboot_is_enabled "$gid_num"; then
|
||||
pmx_record_execution "disable autostart for VM ${gid_num}" "qm set ${gid_num} -onboot 0"
|
||||
qm set "$gid_num" -onboot 0 >/dev/null 2>&1
|
||||
fi
|
||||
else
|
||||
grep -qE '^onboot:\s*1' "/etc/pve/lxc/$gid_num.conf" 2>/dev/null && \
|
||||
if grep -qE '^onboot:\s*1' "/etc/pve/lxc/$gid_num.conf" 2>/dev/null; then
|
||||
pmx_record_execution "disable autostart for CT ${gid_num}" "pct set ${gid_num} -onboot 0"
|
||||
pct set "$gid_num" -onboot 0 >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
;;
|
||||
@@ -629,11 +650,15 @@ resolve_disk_conflicts() {
|
||||
if [[ "$gtype" == "VM" ]]; then
|
||||
while IFS= read -r slot; do
|
||||
[[ -z "$slot" ]] && continue
|
||||
pmx_record_execution "remove disk slot ${slot} from VM ${gid_num}" \
|
||||
"qm set ${gid_num} -delete ${slot}"
|
||||
qm set "$gid_num" -delete "$slot" >/dev/null 2>&1
|
||||
done < <(_find_disk_slots_in_vm "$gid_num" "$disk")
|
||||
else
|
||||
while IFS= read -r slot; do
|
||||
[[ -z "$slot" ]] && continue
|
||||
pmx_record_execution "remove disk slot ${slot} from CT ${gid_num}" \
|
||||
"pct set ${gid_num} -delete ${slot}"
|
||||
pct set "$gid_num" -delete "$slot" >/dev/null 2>&1
|
||||
done < <(_find_disk_slots_in_ct "$gid_num" "$disk")
|
||||
fi
|
||||
@@ -647,6 +672,9 @@ resolve_disk_conflicts() {
|
||||
}
|
||||
|
||||
apply_assignment() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "apply_assignment" "$FUNC_VERSION"
|
||||
|
||||
: >"$LOG_FILE"
|
||||
set_title
|
||||
|
||||
@@ -681,6 +709,8 @@ apply_assignment() {
|
||||
local display_name
|
||||
display_name=$(_pci_storage_display_name "$pci")
|
||||
msg_info "$(translate "Adding") ${display_name} (${pci}) → hostpci${hostpci_idx}..."
|
||||
pmx_record_execution "assign PCI device ${pci} to VM ${SELECTED_VMID} as hostpci${hostpci_idx}" \
|
||||
"qm set ${SELECTED_VMID} --hostpci${hostpci_idx} ${pci},pcie=1"
|
||||
if qm set "$SELECTED_VMID" "--hostpci${hostpci_idx}" "${pci},pcie=1" >>"$LOG_FILE" 2>&1; then
|
||||
msg_ok "$(translate "Controller/NVMe assigned") (hostpci${hostpci_idx} → ${pci})"
|
||||
assigned_count=$((assigned_count + 1))
|
||||
@@ -709,6 +739,7 @@ apply_assignment() {
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
read -r
|
||||
msg_warn "$(translate "Rebooting the system...")"
|
||||
pmx_record_execution "reboot host after enabling IOMMU" "reboot"
|
||||
reboot
|
||||
else
|
||||
msg_info2 "$(translate "To use the VM without issues, the host must be restarted before starting it.")"
|
||||
|
||||
@@ -48,6 +48,12 @@ elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/vm_storage_helpers.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS_DEFAULT/global/vm_storage_helpers.sh"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
FUNC_VERSION="1.3"
|
||||
|
||||
BACKTITLE="ProxMenux"
|
||||
UI_MENU_H=20
|
||||
UI_MENU_W=84
|
||||
@@ -120,12 +126,20 @@ get_preferred_disk_path() {
|
||||
install_fs_tools_in_ct() {
|
||||
local ctid="$1"
|
||||
local pkg="$2"
|
||||
local FUNC_VERSION="1.3"
|
||||
pmx_journal_context "install_fs_tools_in_ct" "$FUNC_VERSION"
|
||||
|
||||
if pct exec "$ctid" -- sh -c "[ -f /etc/alpine-release ]"; then
|
||||
pmx_record_execution "install ${pkg} in CT ${ctid}" \
|
||||
"pct exec ${ctid} -- apk update and apk add ${pkg}"
|
||||
pct exec "$ctid" -- sh -c "apk update >/dev/null 2>&1 && apk add --no-progress $pkg >/dev/null 2>&1"
|
||||
elif pct exec "$ctid" -- sh -c "grep -qi 'arch' /etc/os-release 2>/dev/null"; then
|
||||
pmx_record_execution "install ${pkg} in CT ${ctid}" \
|
||||
"pct exec ${ctid} -- pacman -Sy --noconfirm ${pkg}"
|
||||
pct exec "$ctid" -- sh -c "pacman -Sy --noconfirm $pkg >/dev/null 2>&1"
|
||||
elif pct exec "$ctid" -- sh -c "grep -qiE 'debian|ubuntu' /etc/os-release 2>/dev/null"; then
|
||||
pmx_record_execution "install ${pkg} in CT ${ctid}" \
|
||||
"pct exec ${ctid} -- apt-get update and apt-get install ${pkg}"
|
||||
pct exec "$ctid" -- sh -c "apt-get update -qq >/dev/null 2>&1 && apt-get install -y -qq $pkg >/dev/null 2>&1"
|
||||
else
|
||||
return 1
|
||||
@@ -247,12 +261,15 @@ msg_ok "$(translate "CT $CTID selected successfully.")"
|
||||
|
||||
if [ "$CONVERT_PRIVILEGED" = true ]; then
|
||||
|
||||
pmx_journal_context "disk_passthrough_ct" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Import Disk to LXC")"
|
||||
|
||||
CURRENT_CT_STATUS=$(pct status "$CTID" | awk '{print $2}')
|
||||
if [ "$CURRENT_CT_STATUS" == "running" ]; then
|
||||
msg_info "$(translate "Stopping container") $CTID..."
|
||||
pmx_record_execution "stop CT ${CTID} for privileged conversion" "pct shutdown ${CTID}"
|
||||
pct shutdown "$CTID" &>/dev/null
|
||||
for i in {1..10}; do
|
||||
sleep 1
|
||||
@@ -266,12 +283,13 @@ if [ "$CONVERT_PRIVILEGED" = true ]; then
|
||||
fi
|
||||
|
||||
cp "$CONF_FILE" "$CONF_FILE.bak"
|
||||
sed -i '/^unprivileged: 1/d' "$CONF_FILE"
|
||||
echo "unprivileged: 0" >> "$CONF_FILE"
|
||||
pmx_edit_file "$CONF_FILE" '/^unprivileged: 1/d'
|
||||
echo "unprivileged: 0" | pmx_append_file "$CONF_FILE"
|
||||
msg_ok "$(translate "Container successfully converted to privileged.")"
|
||||
|
||||
if [ "$CT_RUNNING" = true ]; then
|
||||
msg_info "$(translate "Starting container") $CTID..."
|
||||
pmx_record_execution "start CT ${CTID} after privileged conversion" "pct start ${CTID}"
|
||||
pct start "$CTID" &>/dev/null
|
||||
sleep 2
|
||||
if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then
|
||||
@@ -567,6 +585,8 @@ msg_title "$(translate "Import Disk to LXC")"
|
||||
msg_ok "$(translate "CT $CTID selected successfully.")"
|
||||
msg_ok "$(translate "Disks to process:") ${#DISK_LIST[@]}"
|
||||
for i in "${!DISK_LIST[@]}"; do
|
||||
pmx_journal_context "disk_passthrough_ct" "$FUNC_VERSION"
|
||||
|
||||
IFS=$'\t' read -r _desc_model _desc_size <<< "${DISK_DESCRIPTIONS[$i]}"
|
||||
echo -e "${TAB}${BL}${DISK_LIST[$i]} $_desc_model $_desc_size${CL}"
|
||||
done
|
||||
@@ -590,6 +610,8 @@ for i in "${!DISK_LIST[@]}"; do
|
||||
|
||||
if [ "$NEEDS_PARTITION" = true ]; then
|
||||
msg_info "$(translate "Creating partition table and partition...")"
|
||||
pmx_record_execution "create GPT partition on ${DISK} for CT ${CTID}" \
|
||||
"parted -s ${DISK} mklabel gpt mkpart primary 0% 100%"
|
||||
if ! parted -s "$DISK" mklabel gpt mkpart primary 0% 100% >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to create partition table on disk") $DISK_INFO."
|
||||
continue
|
||||
@@ -616,6 +638,8 @@ for i in "${!DISK_LIST[@]}"; do
|
||||
|
||||
if [ "$SKIP_FORMAT" != true ]; then
|
||||
msg_info "$(translate "Formatting partition") $PARTITION $(translate "with") $FORMAT_TYPE..."
|
||||
pmx_record_execution "format ${PARTITION} as ${FORMAT_TYPE} for CT ${CTID}" \
|
||||
"mkfs ${FORMAT_TYPE} ${PARTITION}"
|
||||
if ! case "$FORMAT_TYPE" in
|
||||
"ext4") mkfs.ext4 -F "$PARTITION" >/dev/null 2>&1 ;;
|
||||
"xfs") mkfs.xfs -f "$PARTITION" >/dev/null 2>&1 ;;
|
||||
@@ -658,6 +682,7 @@ for i in "${!DISK_LIST[@]}"; do
|
||||
--yesno "$(translate "The filesystem") $FORMAT_TYPE $(translate "requires the package") $FS_PKG $(translate "installed inside CT") $CTID.\n\n$(translate "The container is currently stopped. Do you want to start it now to install the package?")\n\n$(translate "If you choose No, install") $FS_PKG $(translate "manually inside the container before starting it.")" \
|
||||
$UI_YESNO_H $UI_YESNO_W; then
|
||||
msg_info "$(translate "Starting CT") $CTID..."
|
||||
pmx_record_execution "start CT ${CTID} to install filesystem tools" "pct start ${CTID}"
|
||||
pct start "$CTID" &>/dev/null
|
||||
sleep 2
|
||||
if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then
|
||||
@@ -685,9 +710,14 @@ for i in "${!DISK_LIST[@]}"; do
|
||||
PERSISTENT_PARTITION=$(get_preferred_disk_path "$PARTITION")
|
||||
|
||||
msg_info "$(translate "Applying passthrough to CT") $CTID..."
|
||||
pmx_journal_context "disk_passthrough_ct" "$FUNC_VERSION"
|
||||
if [ "$FORMAT_TYPE" == "xfs" ]; then
|
||||
pmx_record_execution "assign ${PERSISTENT_PARTITION} to CT ${CTID} at ${MOUNT_POINT}" \
|
||||
"pct set ${CTID} -mp${INDEX} ${PERSISTENT_PARTITION},mp=${MOUNT_POINT},backup=0,ro=0"
|
||||
RESULT=$(pct set "$CTID" -mp${INDEX} "$PERSISTENT_PARTITION,mp=$MOUNT_POINT,backup=0,ro=0" 2>&1)
|
||||
else
|
||||
pmx_record_execution "assign ${PERSISTENT_PARTITION} to CT ${CTID} at ${MOUNT_POINT}" \
|
||||
"pct set ${CTID} -mp${INDEX} ${PERSISTENT_PARTITION},mp=${MOUNT_POINT},backup=0,ro=0,acl=1"
|
||||
RESULT=$(pct set "$CTID" -mp${INDEX} "$PERSISTENT_PARTITION,mp=$MOUNT_POINT,backup=0,ro=0,acl=1" 2>&1)
|
||||
fi
|
||||
SET_STATUS=$?
|
||||
|
||||
@@ -64,6 +64,10 @@ elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/utils-install-functions.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS_DEFAULT/global/utils-install-functions.sh"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
BACKTITLE="ProxMenux"
|
||||
UI_MENU_H=20
|
||||
UI_MENU_W=84
|
||||
@@ -607,13 +611,16 @@ prompt_zfs_pool_name() {
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
ensure_fs_tool() {
|
||||
local FUNC_VERSION="2.0"
|
||||
pmx_journal_context "ensure_fs_tool" "$FUNC_VERSION"
|
||||
|
||||
case "$FORMAT_TYPE" in
|
||||
exfat)
|
||||
command -v mkfs.exfat >/dev/null 2>&1 && return 0
|
||||
if declare -F ensure_repositories >/dev/null 2>&1; then
|
||||
ensure_repositories || true
|
||||
fi
|
||||
if DEBIAN_FRONTEND=noninteractive apt-get install -y exfatprogs >/dev/null 2>&1; then
|
||||
if pmx_install_pkg exfatprogs; then
|
||||
command -v mkfs.exfat >/dev/null 2>&1 && {
|
||||
msg_ok "$(translate "exFAT tools installed successfully.")"
|
||||
return 0
|
||||
@@ -657,6 +664,9 @@ wait_for_enter_to_main() {
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
main() {
|
||||
local FUNC_VERSION="2.0"
|
||||
pmx_journal_context "main" "$FUNC_VERSION"
|
||||
|
||||
select_target_disk || exit 0
|
||||
select_operation_mode || exit 0
|
||||
confirm_format_action || exit 0
|
||||
@@ -701,6 +711,10 @@ main() {
|
||||
export DOH_SHOW_PROGRESS=0
|
||||
export DOH_ENABLE_STACK_RELEASE=0
|
||||
|
||||
pmx_record_execution \
|
||||
"disk operation ${OPERATION_MODE} on ${SELECTED_DISK}" \
|
||||
"format-disk operation=${OPERATION_MODE} disk=${SELECTED_DISK} filesystem=${FORMAT_TYPE:-none} zfs_pool=${ZFS_POOL_NAME:-none}"
|
||||
|
||||
if [[ "$OPERATION_MODE" == "wipe_all" ]]; then
|
||||
msg_info "$(translate "Wiping partitions and metadata...")"
|
||||
doh_wipe_disk "$SELECTED_DISK"
|
||||
|
||||
Reference in New Issue
Block a user