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:
MacRimi
2026-09-08 21:06:04 +02:00
co-authored by Claude Opus 5
parent ae75508eff
commit da8a480eff
102 changed files with 24118 additions and 1403 deletions
+14
View File
@@ -41,6 +41,10 @@ if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
fi
load_language
initialize_cache
@@ -134,6 +138,9 @@ select_vm() {
}
ensure_vm_stopped() {
local FUNC_VERSION="1.0"
pmx_journal_context "ensure_vm_stopped" "$FUNC_VERSION"
local status
status=$(qm status "$VMID" 2>/dev/null | awk '{print $2}')
@@ -146,6 +153,7 @@ ensure_vm_stopped() {
return 1
fi
pmx_record_execution "shut down VM ${VMID} for export" "qm shutdown ${VMID} --timeout 120"
qm shutdown "$VMID" --timeout 120 >/dev/null 2>&1 || true
local i
@@ -157,6 +165,7 @@ ensure_vm_stopped() {
if dialog --backtitle "ProxMenux" --title "$(translate "Shutdown timeout")" --yesno \
"$(translate "Graceful shutdown timed out.")\n\n$(translate "Force stop VM now?")" 10 60; then
pmx_record_execution "force stop VM ${VMID} for export" "qm stop ${VMID}"
qm stop "$VMID" >/dev/null 2>&1 || true
sleep 2
status=$(qm status "$VMID" 2>/dev/null | awk '{print $2}')
@@ -516,12 +525,17 @@ print_export_result() {
}
run_export() {
local FUNC_VERSION="1.0"
pmx_journal_context "run_export" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Export VM to OVA or OVF")"
msg_ok "$(translate "VM selected:") $VMID ($VM_NAME)"
msg_ok "$(translate "Export mode:") ${EXPORT_MODE^^}"
msg_ok "$(translate "Destination:") $DEST_DIR"
pmx_record_execution "export VM ${VMID} as ${EXPORT_MODE^^} to ${DEST_DIR}" \
"convert ${DISK_COUNT} VM disk(s), generate OVF metadata and package ${EXPORT_MODE^^}"
local ts vm_safe base_name
ts=$(date +%Y%m%d_%H%M%S)
+16 -1
View File
@@ -49,6 +49,9 @@ INSTALL_HELPERS="$LOCAL_SCRIPTS/global/utils-install-functions.sh"
[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE"
[[ -f "$INSTALL_HELPERS" ]] && source "$INSTALL_HELPERS"
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
fi
load_language
initialize_cache
@@ -87,6 +90,9 @@ BRIDGE="vmbr0"
# with "syntax error at or near ,". Returns 0 on success, 1 if install
# fails (caller is expected to abort with a clear error).
ensure_gawk() {
local FUNC_VERSION="1.0"
pmx_journal_context "ensure_gawk" "$FUNC_VERSION"
if command -v gawk >/dev/null 2>&1; then
return 0
fi
@@ -111,7 +117,7 @@ ensure_gawk() {
# Fallback when utils-install-functions.sh was not sourced.
# Here we own the spinner: msg_info opens it, msg_ok / msg_error closes it.
msg_info "$(translate "Installing gawk (required for OVF parsing)...")"
if apt-get update -qq >/dev/null 2>&1 && apt-get install -y gawk >/dev/null 2>&1; then
if apt-get update -qq >/dev/null 2>&1 && pmx_install_pkg gawk; then
msg_ok "$(translate "gawk installed")"
return 0
fi
@@ -478,6 +484,9 @@ confirm_import() {
# -------------------------------------------------------
run_import() {
local FUNC_VERSION="1.0"
pmx_journal_context "run_import" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Import VM from OVA or OVF")"
@@ -488,6 +497,8 @@ run_import() {
# 1. Create VM shell
msg_info "$(translate "Creating VM...")"
pmx_record_execution "import ${SOURCE_FILE} as VM ${NEW_VMID} on storage ${STORAGE}" \
"qm create ${NEW_VMID}; qm importdisk for ${#OVF_DISK_FILES[@]} disk(s); attach disks and configure boot"
if ! qm create "$NEW_VMID" \
--name "$NEW_VM_NAME" \
--memory "$OVF_MEMORY_MB" \
@@ -624,6 +635,7 @@ print_import_result() {
# -------------------------------------------------------
main() {
local FUNC_VERSION="1.0"
if ! command -v pveversion >/dev/null 2>&1; then
dialog --backtitle "$BACKTITLE" --title "$(translate "Error")" \
--msgbox "$(translate "This script must be run on a Proxmox host.")" 8 60
@@ -694,6 +706,9 @@ main() {
--yesno "$(translate "Remove the partial VM ($NEW_VMID) and its imported disks?")" 8 60; then
clear
msg_info "$(translate "Removing partial VM") $NEW_VMID..."
pmx_journal_context "main" "$FUNC_VERSION"
pmx_record_execution "remove partial imported VM ${NEW_VMID}" \
"qm destroy ${NEW_VMID} --destroy-unreferenced-disks 1"
if qm destroy "$NEW_VMID" --destroy-unreferenced-disks 1 &>/dev/null; then
msg_ok "$(translate "Partial VM removed")"
else
+24 -13
View File
@@ -62,6 +62,9 @@ fi
if [[ -f "$LOCAL_SCRIPTS/global/utils-install-functions.sh" ]]; then
source "$LOCAL_SCRIPTS/global/utils-install-functions.sh"
fi
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
fi
# ==========================================================
@@ -376,31 +379,37 @@ EOF
}
disable_enterprise_repo_if_present() {
local FUNC_VERSION="1.0"
pmx_journal_context "disable_enterprise_repo_if_present" "$FUNC_VERSION"
local s="/etc/apt/sources.list.d/pve-enterprise.sources"
local l="/etc/apt/sources.list.d/pve-enterprise.list"
if [[ -f "$s" ]]; then
if grep -qi '^Enabled:' "$s"; then
sed -i 's/^Enabled:.*/Enabled: false/i' "$s"
pmx_edit_file "$s" 's/^Enabled:.*/Enabled: false/i'
else
echo "Enabled: false" >> "$s"
echo "Enabled: false" | pmx_append_file "$s"
fi
fi
if [[ -f "$l" ]]; then
sed -i 's/^[[:space:]]*deb/# deb/' "$l"
pmx_edit_file "$l" 's/^[[:space:]]*deb/# deb/'
fi
}
comment_legacy_pve8_lists() {
local FUNC_VERSION="1.0"
pmx_journal_context "comment_legacy_pve8_lists" "$FUNC_VERSION"
for f in /etc/apt/sources.list.d/pve-public-repo.list /etc/apt/sources.list.d/pve-install-repo.list; do
[[ -f "$f" ]] || continue
sed -i 's/^[[:space:]]*deb/# deb/' "$f" || true
pmx_edit_file "$f" 's/^[[:space:]]*deb/# deb/' || true
done
}
comment_legacy_ceph_list() {
local FUNC_VERSION="1.0"
pmx_journal_context "comment_legacy_ceph_list" "$FUNC_VERSION"
local f="/etc/apt/sources.list.d/ceph.list"
[[ -f "$f" ]] || return 0
sed -i 's/^[[:space:]]*deb/# deb/' "$f" || true
pmx_edit_file "$f" 's/^[[:space:]]*deb/# deb/' || true
}
apt_update_with_repo_fallback() {
@@ -811,11 +820,11 @@ else
fi
FUNC_VERSION="1.0"
pmx_journal_context "upgrade_pve8_to_pve9" "$FUNC_VERSION"
if [[ "$DISABLE_AUDIT" == "1" ]]; then
append_step \
"" \
"Audit socket disabled or not required" \
"systemctl disable --now systemd-journald-audit.socket >/dev/null 2>&1 || true"
pmx_disable_service systemd-journald-audit.socket >> "$LOG" 2>&1 || true
echo -e "${BFR}${TAB}${CM}${GN}$(translate "Audit socket disabled or not required")${CL}"
fi
@@ -856,10 +865,12 @@ fi
# Step 4
# ---------------------------
FUNC_VERSION="1.0"
pmx_journal_context "upgrade_pve8_to_pve9" "$FUNC_VERSION"
OS_FILE="/etc/apt/sources.list"
if [[ -f "$OS_FILE" ]]; then
msg_info "$(translate "Updating Debian Bookworm → Trixie in sources.list...")"
if sed -i 's/bookworm/trixie/g' "$OS_FILE"; then
if pmx_edit_file "$OS_FILE" 's/bookworm/trixie/g'; then
msg_ok "$(translate "sources.list updated to Trixie")"
else
msg_ok "$(translate "sources.list update skipped (no change)")"
@@ -871,7 +882,7 @@ fi
PVE_ENT_LIST="/etc/apt/sources.list.d/pve-enterprise.list"
msg_info "$(translate "Updating pve-enterprise.list (if present) to Trixie...")"
if [[ -f "$PVE_ENT_LIST" ]]; then
if sed -i 's/bookworm/trixie/g' "$PVE_ENT_LIST"; then
if pmx_edit_file "$PVE_ENT_LIST" 's/bookworm/trixie/g'; then
msg_ok "$(translate "pve-enterprise.list updated to Trixie")"
else
msg_ok "$(translate "pve-enterprise.list update skipped (no change)")"
@@ -884,9 +895,9 @@ fi
msg_info "$(translate "Commenting any residual Bookworm lines in *.list...")"
for f in /etc/apt/sources.list.d/*.list; do
[[ -f "$f" ]] || continue
sed -i '/bookworm/s/^/# /' "$f" || true
pmx_edit_file "$f" '/bookworm/s/^/# /' || true
done
sed -i '/bookworm/s/^/# /' "$OS_FILE" 2>/dev/null || true
pmx_edit_file "$OS_FILE" '/bookworm/s/^/# /' 2>/dev/null || true
msg_ok "$(translate "Residual Bookworm entries commented where applicable")"