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
+52 -11
View File
@@ -54,6 +54,10 @@ elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/disk_ops_helpers.sh" ]]; then
source "$LOCAL_SCRIPTS_DEFAULT/global/disk_ops_helpers.sh"
fi
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
fi
load_language
initialize_cache
@@ -471,6 +475,8 @@ format_and_mount_disk() {
local disk="$1"
local mount_path="$2"
local filesystem="$3"
local FUNC_VERSION="1.0"
pmx_journal_context "format_and_mount_disk" "$FUNC_VERSION"
# Final confirmation before any destructive operation
local disk_size
@@ -480,6 +486,8 @@ format_and_mount_disk() {
14 80; then
return 1
fi
pmx_record_execution "format disk ${disk} as ${filesystem} for ${mount_path}" \
"wipe disk, create partition and format as ${filesystem}"
show_proxmenux_logo
if [[ "$MODE_PVESM" -eq 1 && "$MODE_FSTAB" -eq 1 ]]; then
msg_title "$(translate "Add Local Disk (Proxmox storage + host mount)")"
@@ -544,6 +552,8 @@ mount_disk_permanently() {
local partition="$1"
local mount_path="$2"
local filesystem="$3"
local FUNC_VERSION="1.0"
pmx_journal_context "mount_disk_permanently" "$FUNC_VERSION"
if [[ "$filesystem" == "zfs" ]]; then
if ! zpool list "$STORAGE_ID" >/dev/null 2>&1; then
@@ -562,6 +572,8 @@ mount_disk_permanently() {
msg_ok "$(translate "Mount point created")"
msg_info "$(translate "Mounting disk...")"
pmx_record_execution "mount ${partition} at ${mount_path}" \
"mount -t ${filesystem} ${partition} ${mount_path}"
if ! mount -t "$filesystem" "$partition" "$mount_path" 2>/dev/null; then
msg_error "$(translate "Failed to mount disk")"
return 1
@@ -574,13 +586,13 @@ mount_disk_permanently() {
if [[ -n "$disk_uuid" ]]; then
# Remove any existing fstab entry for this UUID or mount point
sed -i "\|UUID=$disk_uuid|d" /etc/fstab
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
echo "UUID=$disk_uuid $mount_path $filesystem defaults,nofail 0 2" >> /etc/fstab
pmx_edit_file /etc/fstab "\|UUID=$disk_uuid|d"
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
echo "UUID=$disk_uuid $mount_path $filesystem defaults,nofail 0 2" | pmx_append_file /etc/fstab
msg_ok "$(translate "Added to /etc/fstab using UUID")"
else
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
echo "$partition $mount_path $filesystem defaults,nofail 0 2" >> /etc/fstab
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
echo "$partition $mount_path $filesystem defaults,nofail 0 2" | pmx_append_file /etc/fstab
msg_ok "$(translate "Added to /etc/fstab using device path")"
fi
@@ -604,10 +616,14 @@ mount_disk_permanently() {
# but the change is harmless: existing owners keep their access.
_apply_lxc_bind_mount_perms() {
local mount_path="$1"
local FUNC_VERSION="1.0"
pmx_journal_context "_apply_lxc_bind_mount_perms" "$FUNC_VERSION"
[[ "${MODE_FSTAB:-0}" -eq 1 ]] || return 0
[[ -d "$mount_path" ]] || return 0
msg_info "$(translate "Applying host permissions for unprivileged LXC bind-mounts...")"
pmx_record_execution "apply LXC bind-mount permissions to ${mount_path}" \
"chmod o+rwx and setfacl on ${mount_path}"
chmod o+rwx "$mount_path" 2>/dev/null || true
if command -v setfacl >/dev/null 2>&1; then
setfacl -m o::rwx "$mount_path" 2>/dev/null || true
@@ -619,6 +635,8 @@ _apply_lxc_bind_mount_perms() {
mount_existing_disk() {
local disk="$1"
local mount_path="$2"
local FUNC_VERSION="1.0"
pmx_journal_context "mount_existing_disk" "$FUNC_VERSION"
local existing_fs
existing_fs=$(blkid -s TYPE -o value "$disk" 2>/dev/null || true)
@@ -635,6 +653,7 @@ mount_existing_disk() {
msg_ok "$(translate "Mount point created")"
msg_info "$(translate "Mounting existing") $existing_fs $(translate "filesystem...")"
pmx_record_execution "mount existing disk ${disk} at ${mount_path}" "mount ${disk} ${mount_path}"
if ! mount "$disk" "$mount_path" 2>/dev/null; then
msg_error "$(translate "Failed to mount disk")"
return 1
@@ -645,9 +664,9 @@ mount_existing_disk() {
local disk_uuid
disk_uuid=$(blkid -s UUID -o value "$disk" 2>/dev/null)
if [[ -n "$disk_uuid" ]]; then
sed -i "\|UUID=$disk_uuid|d" /etc/fstab
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
echo "UUID=$disk_uuid $mount_path $existing_fs defaults,nofail 0 2" >> /etc/fstab
pmx_edit_file /etc/fstab "\|UUID=$disk_uuid|d"
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
echo "UUID=$disk_uuid $mount_path $existing_fs defaults,nofail 0 2" | pmx_append_file /etc/fstab
msg_ok "$(translate "Added to /etc/fstab")"
fi
@@ -664,6 +683,8 @@ add_proxmox_dir_storage() {
local content="$3"
local storage_kind="dir"
local pool_name="$storage_id"
local FUNC_VERSION="1.0"
pmx_journal_context "add_proxmox_dir_storage" "$FUNC_VERSION"
if [[ "${FILESYSTEM:-}" == "zfs" ]]; then
storage_kind="zfspool"
@@ -681,6 +702,7 @@ add_proxmox_dir_storage() {
8 60; then
return 0
fi
pmx_record_execution "remove existing Proxmox storage ${storage_id}" "pvesm remove ${storage_id}"
pvesm remove "$storage_id" 2>/dev/null || true
fi
@@ -688,12 +710,16 @@ add_proxmox_dir_storage() {
local pvesm_output
local add_ok=false
if [[ "$storage_kind" == "zfspool" ]]; then
pmx_record_execution "add ZFS pool ${pool_name} as Proxmox storage ${storage_id}" \
"pvesm add zfspool ${storage_id} --pool ${pool_name} --content ${content}"
if pvesm_output=$(pvesm add zfspool "$storage_id" \
--pool "$pool_name" \
--content "$content" 2>&1); then
add_ok=true
fi
else
pmx_record_execution "add directory ${path} as Proxmox storage ${storage_id}" \
"pvesm add dir ${storage_id} --path ${path} --content ${content}"
if pvesm_output=$(pvesm add dir "$storage_id" \
--path "$path" \
--content "$content" 2>&1); then
@@ -742,6 +768,9 @@ add_proxmox_dir_storage() {
# ==========================================================
add_disk_to_proxmox() {
local FUNC_VERSION="1.0"
pmx_journal_context "add_disk_to_proxmox" "$FUNC_VERSION"
# Check required tools
for tool in parted mkfs.ext4 mkfs.xfs blkid lsblk sgdisk; do
if ! command -v "$tool" >/dev/null 2>&1; then
@@ -749,7 +778,7 @@ add_disk_to_proxmox() {
msg_title "$(translate "Add Local Disk as Proxmox Storage")"
msg_info "$(translate "Installing required tools...")"
apt-get update &>/dev/null
apt-get install -y parted e2fsprogs util-linux xfsprogs gdisk btrfs-progs &>/dev/null
pmx_install_pkg parted e2fsprogs util-linux xfsprogs gdisk btrfs-progs
stop_spinner
break
fi
@@ -990,6 +1019,8 @@ view_disk_storages() {
_remove_pvesm_storage() {
local storage_id="$1"
local FUNC_VERSION="1.0"
pmx_journal_context "_remove_pvesm_storage" "$FUNC_VERSION"
local path pool content stype
path=$(get_storage_config "$storage_id" | awk '$1 == "path" {print $2}')
pool=$(get_storage_config "$storage_id" | awk '$1 == "pool" {print $2}')
@@ -1017,6 +1048,7 @@ _remove_pvesm_storage() {
# Step 1: Remove from Proxmox
msg_info "$(translate "Removing storage from Proxmox...")"
pmx_record_execution "remove Proxmox storage ${storage_id}" "pvesm remove ${storage_id}"
if ! pvesm remove "$storage_id" 2>/dev/null; then
msg_error "$(translate "Failed to remove storage from Proxmox.")"
echo ""
@@ -1029,6 +1061,7 @@ _remove_pvesm_storage() {
# Step 2: Unmount if mounted (dir-backed storages only)
if [[ -n "$path" ]] && mountpoint -q "$path" 2>/dev/null; then
msg_info "$(translate "Unmounting disk...")"
pmx_record_execution "unmount disk from ${path}" "umount ${path}"
if umount "$path" 2>/dev/null; then
msg_ok "$(translate "Disk unmounted from") $path"
else
@@ -1045,7 +1078,9 @@ _remove_pvesm_storage() {
msg_info "$(translate "Removing from /etc/fstab...")"
local tmp
tmp=$(mktemp)
awk -v mp="$path" '$2 != mp' /etc/fstab > "$tmp" && mv "$tmp" /etc/fstab
if awk -v mp="$path" '$2 != mp' /etc/fstab > "$tmp"; then
pmx_write_file /etc/fstab < "$tmp" && rm -f "$tmp"
fi
systemctl daemon-reload 2>/dev/null || true
msg_ok "$(translate "Removed from /etc/fstab")"
fi
@@ -1053,6 +1088,7 @@ _remove_pvesm_storage() {
# Step 3b: Export ZFS pool if applicable
if [[ -n "$pool" ]] && zpool list "$pool" >/dev/null 2>&1; then
msg_info "$(translate "Exporting ZFS pool...") $pool"
pmx_record_execution "export ZFS pool ${pool}" "zpool export ${pool}"
if zpool export "$pool" 2>/dev/null; then
msg_ok "$(translate "ZFS pool exported:") $pool"
else
@@ -1069,6 +1105,7 @@ _remove_pvesm_storage() {
read -r
echo ""
msg_warn "$(translate "Rebooting the system...")"
pmx_record_execution "reboot host after removing storage ${storage_id}" "reboot"
reboot
else
echo ""
@@ -1082,6 +1119,8 @@ _remove_pvesm_storage() {
_remove_fstab_entry() {
local mount_point="$1"
local FUNC_VERSION="1.0"
pmx_journal_context "_remove_fstab_entry" "$FUNC_VERSION"
local fs fstype
while IFS= read -r line; do
@@ -1122,6 +1161,7 @@ _remove_fstab_entry() {
if $mounted; then
msg_info "$(translate "Unmounting") $mount_point..."
pmx_record_execution "unmount disk from ${mount_point}" "umount ${mount_point}"
if umount "$mount_point" 2>/dev/null; then
msg_ok "$(translate "Unmounted successfully")"
else
@@ -1133,7 +1173,8 @@ _remove_fstab_entry() {
local tmp
tmp=$(mktemp)
awk -v mp="$mount_point" '$2 != mp' /etc/fstab > "$tmp"
mv "$tmp" /etc/fstab
pmx_write_file /etc/fstab < "$tmp"
rm -f "$tmp"
systemctl daemon-reload 2>/dev/null || true
msg_ok "$(translate "Removed from /etc/fstab")"
+22 -3
View File
@@ -30,6 +30,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
@@ -56,16 +60,20 @@ get_storage_config() {
# ==========================================================
ensure_iscsi_tools() {
local FUNC_VERSION="1.0"
pmx_journal_context "ensure_iscsi_tools" "$FUNC_VERSION"
if ! command -v iscsiadm >/dev/null 2>&1; then
msg_info "$(translate "Installing iSCSI initiator tools...")"
apt-get update &>/dev/null
apt-get install -y open-iscsi &>/dev/null
systemctl enable --now iscsid 2>/dev/null || true
pmx_install_pkg open-iscsi
pmx_enable_service iscsid 2>/dev/null || true
msg_ok "$(translate "iSCSI tools installed")"
fi
if ! systemctl is-active --quiet iscsid 2>/dev/null; then
systemctl start iscsid 2>/dev/null || true
pmx_apply_setting "iscsid active state" "systemctl is-active iscsid 2>/dev/null || true" \
systemctl start iscsid || true
fi
}
@@ -217,6 +225,9 @@ configure_iscsi_storage() {
# ==========================================================
add_proxmox_iscsi_storage() {
local FUNC_VERSION="1.0"
pmx_journal_context "add_proxmox_iscsi_storage" "$FUNC_VERSION"
local storage_id="$1"
local portal="$2"
local target="$3"
@@ -233,6 +244,8 @@ add_proxmox_iscsi_storage() {
8 60 --title "$(translate "Storage Exists")"; then
return 0
fi
pmx_record_execution "remove existing Proxmox iSCSI storage ${storage_id}" \
"pvesm remove ${storage_id}"
pvesm remove "$storage_id" 2>/dev/null || true
fi
@@ -240,6 +253,8 @@ add_proxmox_iscsi_storage() {
msg_info "$(translate "Adding iSCSI storage to Proxmox...")"
local pvesm_output pvesm_result
pmx_record_execution "add iSCSI target ${target} as Proxmox storage ${storage_id}" \
"pvesm add iscsi ${storage_id} --portal ${portal} --target ${target} --content ${content}"
pvesm_output=$(pvesm add iscsi "$storage_id" \
--portal "$portal" \
--target "$target" \
@@ -359,6 +374,9 @@ view_iscsi_storages() {
}
remove_iscsi_storage() {
local FUNC_VERSION="1.0"
pmx_journal_context "remove_iscsi_storage" "$FUNC_VERSION"
if ! command -v pvesm >/dev/null 2>&1; then
dialog --backtitle "ProxMenux" --title "$(translate "Error")" \
--msgbox "\n$(translate "pvesm not found.")" 8 60
@@ -400,6 +418,7 @@ remove_iscsi_storage() {
show_proxmenux_logo
msg_title "$(translate "Remove iSCSI Storage")"
pmx_record_execution "remove Proxmox iSCSI storage ${SELECTED}" "pvesm remove ${SELECTED}"
if pvesm remove "$SELECTED" 2>/dev/null; then
msg_ok "$(translate "Storage") $SELECTED $(translate "removed successfully from Proxmox.")"
else
+13
View File
@@ -42,6 +42,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
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
msg_error "$(translate "Could not load shared functions. Script cannot continue.")"
@@ -64,9 +68,14 @@ fi
lsm_apply_multi_unpriv_permissions() {
local dir="$1"
local FUNC_VERSION="1.0"
pmx_journal_context "lsm_apply_multi_unpriv_permissions" "$FUNC_VERSION"
[[ -z "$dir" || ! -d "$dir" ]] && return 1
pmx_record_execution "apply shared LXC permission profile to ${dir}" \
"chown root:root; chmod 1777; chmod -R a+rwX; apply default ACLs when available"
# root:root ownership — no new group needed.
chown root:root "$dir" 2>/dev/null || true
@@ -224,6 +233,9 @@ lsm_select_host_mount_point_dialog() {
}
create_shared_directory() {
local FUNC_VERSION="1.0"
pmx_journal_context "create_shared_directory" "$FUNC_VERSION"
lsm_select_host_mount_point_dialog "$(translate "Select Shared Directory Location")" "shared"
[[ -z "$LSM_SELECTED_MOUNT_POINT" ]] && return
SHARED_DIR="$LSM_SELECTED_MOUNT_POINT"
@@ -231,6 +243,7 @@ create_shared_directory() {
show_proxmenux_logo
msg_title "$(translate "Create Shared Directory")"
pmx_record_execution "create shared directory ${SHARED_DIR}" "mkdir -p ${SHARED_DIR}"
if ! mkdir -p "$SHARED_DIR" 2>/dev/null; then
msg_error "$(translate "Failed to create directory:") $SHARED_DIR"
echo ""
+40 -1
View File
@@ -30,6 +30,10 @@
BASE_DIR="/usr/local/share/proxmenux"
source "$BASE_DIR/utils.sh"
if [[ -f "/usr/local/share/proxmenux/scripts/global/pmx_journal.sh" ]]; then
source "/usr/local/share/proxmenux/scripts/global/pmx_journal.sh"
fi
load_language
initialize_cache
@@ -289,6 +293,8 @@ select_lxc_container() {
select_container_mount_point() {
local ctid="$1"
local host_dir="$2"
local FUNC_VERSION="1.0"
pmx_journal_context "select_container_mount_point" "$FUNC_VERSION"
local base_name
base_name=$(basename "$host_dir")
@@ -333,6 +339,8 @@ select_container_mount_point() {
local ct_status
ct_status=$(pct status "$ctid" 2>/dev/null | awk '{print $2}')
if [[ "$ct_status" == "running" ]]; then
pmx_record_execution "create mount directory ${mount_point} in CT ${ctid}" \
"pct exec ${ctid} -- mkdir -p ${mount_point}"
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
fi
@@ -367,6 +375,8 @@ add_bind_mount() {
local ctid="$1"
local host_path="$2"
local ct_path="$3"
local FUNC_VERSION="1.0"
pmx_journal_context "add_bind_mount" "$FUNC_VERSION"
if [[ ! "$ctid" =~ ^[0-9]+$ || -z "$host_path" || -z "$ct_path" ]]; then
msg_error "$(translate "Invalid parameters for bind mount")"
@@ -383,6 +393,8 @@ add_bind_mount() {
mpidx=$(get_next_mp_index "$ctid")
local result
pmx_record_execution "add bind mount ${host_path} to CT ${ctid} at ${ct_path}" \
"pct set ${ctid} -mp${mpidx} ${host_path},mp=${ct_path},shared=1,backup=0"
result=$(pct set "$ctid" -mp${mpidx} "$host_path,mp=$ct_path,shared=1,backup=0" 2>&1)
if [[ $? -eq 0 ]]; then
@@ -451,6 +463,9 @@ view_mount_points() {
}
remove_mount_point() {
local FUNC_VERSION="1.0"
pmx_journal_context "remove_mount_point" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Remove LXC Mount Point")"
@@ -532,6 +547,8 @@ $(translate "Proceed with removal")?"
msg_title "$(translate "Remove LXC Mount Point")"
msg_info "$(translate "Removing mount point") $selected_mp $(translate "from container") $container_id..."
pmx_record_execution "remove mount point ${selected_mp} from CT ${container_id}" \
"pct set ${container_id} --delete ${selected_mp}"
if pct set "$container_id" --delete "$selected_mp" 2>/dev/null; then
msg_ok "$(translate "Mount point removed successfully")"
@@ -541,6 +558,8 @@ $(translate "Proceed with removal")?"
echo ""
if whiptail --yesno "$(translate "Container is running. Restart to apply changes?")" 8 60; then
msg_info "$(translate "Restarting container...")"
pmx_record_execution "restart CT ${container_id} after removing ${selected_mp}" \
"pct reboot ${container_id}"
if pct reboot "$container_id"; then
sleep 3
msg_ok "$(translate "Container restarted successfully")"
@@ -573,6 +592,8 @@ $(translate "Proceed with removal")?"
lmm_fix_cifs_access() {
local host_dir="$1"
local is_unprivileged="$2"
local FUNC_VERSION="1.0"
pmx_journal_context "lmm_fix_cifs_access" "$FUNC_VERSION"
# CIFS mounted by Proxmox GUI uses uid=0/gid=0 by default (root only).
# The fix: remount with uid/gid that the LXC can access.
@@ -620,13 +641,16 @@ $(translate "Apply fix now? (The share will be briefly remounted)")" \
18 84 3>&1 1>&2 2>&3; then
msg_info "$(translate "Remounting CIFS share with open permissions...")"
pmx_record_execution "remount CIFS share ${mount_src} at ${host_dir}" \
"umount ${host_dir}; mount -t cifs ${mount_src} ${host_dir} -o ${new_opts}"
if umount "$host_dir" 2>/dev/null && \
mount -t cifs "$mount_src" "$host_dir" -o "$new_opts" 2>/dev/null; then
msg_ok "$(translate "CIFS share remounted — LXC containers can now read and write")"
# Update fstab if the mount is there
if grep -qF "$host_dir" /etc/fstab 2>/dev/null; then
sed -i "s|^\(${mount_src}[[:space:]].*${host_dir}.*cifs[[:space:]]\).*|\1${new_opts} 0 0|" /etc/fstab 2>/dev/null || true
pmx_edit_file /etc/fstab \
"s|^\(${mount_src}[[:space:]].*${host_dir}.*cifs[[:space:]]\).*|\1${new_opts} 0 0|" 2>/dev/null || true
msg_ok "$(translate "/etc/fstab updated — permissions will persist after reboot")"
fi
else
@@ -639,6 +663,8 @@ lmm_fix_nfs_access() {
local host_dir="$1"
local is_unprivileged="$2"
local uid_shift="${3:-100000}"
local FUNC_VERSION="1.0"
pmx_journal_context "lmm_fix_nfs_access" "$FUNC_VERSION"
# NFS: the host cannot override server-side permissions.
# BUT: if the server exports with root_squash (default), we can check
@@ -678,6 +704,8 @@ $(translate "If it still fails, the NFS server export options must be changed on
$(translate "Apply fix now?")" \
18 84 3>&1 1>&2 2>&3; then
pmx_record_execution "apply LXC access permissions to NFS directory ${host_dir}" \
"chmod 1777 and setfacl on ${host_dir}"
if chmod 1777 "$host_dir" 2>/dev/null; then
msg_ok "$(translate "NFS directory permissions set — containers should now be able to write")"
else
@@ -716,6 +744,8 @@ $(translate "You can still mount this share for READ-ONLY access.")" \
lmm_offer_host_permissions() {
local host_dir="$1"
local is_unprivileged="$2"
local FUNC_VERSION="1.0"
pmx_journal_context "lmm_offer_host_permissions" "$FUNC_VERSION"
# Privileged containers: UID 0 inside = UID 0 on host — always accessible
[[ "$is_unprivileged" != "1" ]] && return 0
@@ -749,6 +779,8 @@ $(translate "Apply read+write access for 'others' on the host directory?")\n\n\
$(translate "(Only the host directory is modified. Nothing inside the container is changed.")" \
16 80 3>&1 1>&2 2>&3; then
pmx_record_execution "grant mapped LXC users access to host directory ${host_dir}" \
"chmod o+rwx and setfacl on ${host_dir}"
chmod o+rwx "$host_dir" 2>/dev/null || true
if command -v setfacl >/dev/null 2>&1; then
setfacl -m o::rwx "$host_dir" 2>/dev/null || true
@@ -798,6 +830,8 @@ _lmm_verify_writable() {
# ==========================================================
mount_host_directory_minimal() {
local FUNC_VERSION="1.0"
# Step 1: Select container
local container_id
container_id=$(select_lxc_container)
@@ -900,10 +934,13 @@ $(translate "Proceed")?"
# bind-mount is supposed to spare them.
local ct_status
ct_status=$(pct status "$container_id" 2>/dev/null | awk '{print $2}')
pmx_journal_context "mount_host_directory_minimal" "$FUNC_VERSION"
echo ""
if [[ "$ct_status" == "running" ]]; then
if whiptail --yesno "$(translate "Restart container to activate mount?")" 8 60; then
msg_info "$(translate "Restarting container...")"
pmx_record_execution "restart CT ${container_id} to activate bind mount" \
"pct reboot ${container_id}"
if pct reboot "$container_id"; then
sleep 5
msg_ok "$(translate "Container restarted successfully")"
@@ -918,6 +955,8 @@ $(translate "Proceed")?"
# declines, fall back to the informational line.
if whiptail --yesno "$(translate "Container is stopped. Start it now to verify the mount works?")" 8 70; then
msg_info "$(translate "Starting container...")"
pmx_record_execution "start CT ${container_id} to activate and verify bind mount" \
"pct start ${container_id}"
if pct start "$container_id"; then
sleep 5
msg_ok "$(translate "Container started successfully")"
+29 -5
View File
@@ -29,6 +29,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 shared functions
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
@@ -44,6 +48,8 @@ select_privileged_lxc
install_nfs_client() {
local FUNC_VERSION="1.0"
pmx_journal_context "install_nfs_client" "$FUNC_VERSION"
if pct exec "$CTID" -- dpkg -s nfs-common &>/dev/null; then
return 0
@@ -65,6 +71,8 @@ install_nfs_client() {
fi
msg_info "$(translate "Installing NFS client packages...")"
pmx_record_execution "install NFS client packages in CT ${CTID}" \
"pct exec ${CTID} -- apt-get update and apt-get install -y nfs-common"
if ! pct exec "$CTID" -- apt-get update >/dev/null 2>&1; then
msg_error "$(translate "Failed to update package list.")"
msg_success "$(translate "Press Enter to return to menu...")"
@@ -99,6 +107,9 @@ install_nfs_client() {
discover_nfs_servers() {
local FUNC_VERSION="1.0"
pmx_journal_context "discover_nfs_servers" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Mount NFS Client in LXC")"
msg_info "$(translate "Scanning network for NFS servers...")"
@@ -110,7 +121,7 @@ discover_nfs_servers() {
if ! which nmap >/dev/null 2>&1; then
apt-get install -y nmap &>/dev/null
pmx_install_pkg nmap
fi
@@ -367,6 +378,7 @@ validate_export_exists() {
mount_nfs_share() {
local FUNC_VERSION="1.0"
# Step 0: Install NFS client first
install_nfs_client || return
@@ -395,7 +407,9 @@ mount_nfs_share() {
# Step 4: Configure mount options
configure_mount_options || return
pmx_journal_context "mount_nfs_share" "$FUNC_VERSION"
pmx_record_execution "mount NFS export ${NFS_SERVER}:${NFS_EXPORT} in CT ${CTID} at ${MOUNT_POINT}" \
"pct exec ${CTID} -- mount NFS; persistent=${PERMANENT_MOUNT}"
if ! pct exec "$CTID" -- test -d "$MOUNT_POINT"; then
@@ -432,9 +446,9 @@ mount_nfs_share() {
# Add to fstab if permanent
if [[ "$PERMANENT_MOUNT" == "true" ]]; then
pct exec "$CTID" -- sed -i "\|$MOUNT_POINT|d" /etc/fstab
pct exec "$CTID" -- sed --in-place "\|$MOUNT_POINT|d" /etc/fstab
FSTAB_ENTRY="$NFS_PATH $MOUNT_POINT nfs ${MOUNT_OPTIONS},_netdev,x-systemd.automount,noauto 0 0"
pct exec "$CTID" -- bash -c "echo '$FSTAB_ENTRY' >> /etc/fstab"
pct exec "$CTID" -- bash -c "printf '%s\\n' '$FSTAB_ENTRY' | tee -a /etc/fstab >/dev/null"
msg_ok "$(translate "Added to /etc/fstab for permanent mounting.")"
fi
@@ -543,6 +557,9 @@ view_nfs_mounts() {
unmount_nfs_share() {
local FUNC_VERSION="1.0"
pmx_journal_context "unmount_nfs_share" "$FUNC_VERSION"
# Get current NFS mounts
MOUNTS=$(pct exec "$CTID" -- mount | grep -E "type nfs|:.*on.*nfs" | awk '{print $3}' | sort -u || true)
FSTAB_MOUNTS=$(pct exec "$CTID" -- grep -E "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | awk '{print $2}' | sort -u || true)
@@ -568,7 +585,9 @@ unmount_nfs_share() {
msg_title "$(translate "Unmount NFS Share")"
# Remove from fstab
pct exec "$CTID" -- sed -i "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
pmx_record_execution "remove NFS mount ${SELECTED_MOUNT} from CT ${CTID}" \
"remove CT fstab entry and unmount ${SELECTED_MOUNT}"
pct exec "$CTID" -- sed --in-place "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
msg_ok "$(translate "Removed from /etc/fstab.")"
# Actually unmount it now (the previous version only edited fstab,
@@ -598,6 +617,9 @@ unmount_nfs_share() {
test_nfs_connectivity() {
local FUNC_VERSION="1.0"
pmx_journal_context "test_nfs_connectivity" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Test NFS Connectivity")"
@@ -621,6 +643,8 @@ test_nfs_connectivity() {
else
echo "$(translate "RPC Bind Service: STOPPED")"
msg_warn "$(translate "Starting rpcbind service...")"
pmx_record_execution "start rpcbind in CT ${CTID}" \
"pct exec ${CTID} -- systemctl start rpcbind"
pct exec "$CTID" -- systemctl start rpcbind 2>/dev/null || true
fi
+38 -6
View File
@@ -38,6 +38,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
@@ -64,6 +68,9 @@ get_storage_config() {
# ==========================================================
discover_nfs_servers() {
local FUNC_VERSION="1.0"
pmx_journal_context "discover_nfs_servers" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Add NFS Share as Proxmox Storage")"
msg_info "$(translate "Scanning network for NFS servers...")"
@@ -72,7 +79,7 @@ discover_nfs_servers() {
NETWORK=$(echo "$HOST_IP" | cut -d. -f1-3).0/24
if ! which nmap >/dev/null 2>&1; then
apt-get install -y nmap &>/dev/null
pmx_install_pkg nmap
fi
SERVERS=$(nmap -p 2049 --open "$NETWORK" 2>/dev/null | grep -B 4 "2049/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
@@ -253,6 +260,8 @@ add_proxmox_nfs_storage() {
local server="$2"
local export="$3"
local content="${4:-import}"
local FUNC_VERSION="1.0"
pmx_journal_context "add_proxmox_nfs_storage" "$FUNC_VERSION"
msg_info "$(translate "Starting Proxmox storage integration...")"
@@ -267,11 +276,15 @@ add_proxmox_nfs_storage() {
8 60 --title "$(translate "Storage Exists")"; then
return 0
fi
pmx_record_execution "remove existing Proxmox NFS storage ${storage_id}" \
"pvesm remove ${storage_id}"
pvesm remove "$storage_id" 2>/dev/null || true
fi
msg_ok "$(translate "Storage ID is available")"
msg_info "$(translate "NFS storage adding in progress...")"
pmx_record_execution "add NFS export ${server}:${export} as Proxmox storage ${storage_id}" \
"pvesm add nfs ${storage_id} --server ${server} --export ${export} --content ${content}"
if pvesm_output=$(pvesm add nfs "$storage_id" \
--server "$server" \
--export "$export" \
@@ -384,6 +397,8 @@ mount_nfs_via_fstab() {
local mount_path="$3"
local mount_opts="$4"
local replace="$5"
local FUNC_VERSION="1.0"
pmx_journal_context "mount_nfs_via_fstab" "$FUNC_VERSION"
msg_info "$(translate "Preparing host mount...")"
@@ -396,6 +411,8 @@ mount_nfs_via_fstab() {
msg_ok "$(translate "Mount point ready:") $mount_path"
msg_info "$(translate "Mounting NFS share...")"
pmx_record_execution "mount NFS export ${server}:${export_path} at ${mount_path}" \
"mount -t nfs -o ${mount_opts} ${server}:${export_path} ${mount_path}"
if ! mount -t nfs -o "$mount_opts" "${server}:${export_path}" "$mount_path" >/dev/null 2>&1; then
msg_error "$(translate "Failed to mount NFS share on host.")"
return 1
@@ -418,11 +435,12 @@ mount_nfs_via_fstab() {
# Persist in /etc/fstab.
if [[ "$replace" == "1" ]]; then
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
fi
echo "${server}:${export_path} $mount_path nfs $mount_opts 0 0" >> /etc/fstab
echo "${server}:${export_path} $mount_path nfs $mount_opts 0 0" | pmx_append_file /etc/fstab
msg_ok "$(translate "Added to /etc/fstab.")"
pmx_record_execution "reload systemd units after NFS fstab update" "systemctl daemon-reload"
systemctl daemon-reload 2>/dev/null || true
echo -e ""
@@ -480,10 +498,13 @@ select_mount_methods() {
# ==========================================================
mount_nfs_share() {
local FUNC_VERSION="1.0"
pmx_journal_context "mount_nfs_share" "$FUNC_VERSION"
if ! which showmount >/dev/null 2>&1; then
msg_info "$(translate "Installing NFS client tools...")"
apt-get update &>/dev/null
apt-get install -y nfs-common &>/dev/null
pmx_install_pkg nfs-common
msg_ok "$(translate "NFS client tools installed")"
fi
@@ -654,6 +675,9 @@ view_nfs_storages() {
}
remove_nfs_storage() {
local FUNC_VERSION="1.0"
pmx_journal_context "remove_nfs_storage" "$FUNC_VERSION"
# Collect every removable NFS entry: pvesm storages and fstab-only mounts.
local OPTIONS=()
local has_pvesm=0
@@ -718,6 +742,7 @@ remove_nfs_storage() {
show_proxmenux_logo
msg_title "$(translate "Remove NFS Storage")"
pmx_record_execution "remove Proxmox NFS storage ${target}" "pvesm remove ${target}"
if pvesm remove "$target" 2>/dev/null; then
msg_ok "$(translate "Storage") $target $(translate "removed successfully from Proxmox.")"
else
@@ -742,6 +767,7 @@ remove_nfs_storage() {
# Try umount only if currently mounted; never force.
if mount | grep -q " on ${mount_path} type "; then
pmx_record_execution "unmount NFS path ${mount_path}" "umount ${mount_path}"
if umount "$mount_path" 2>/dev/null; then
msg_ok "$(translate "Unmounted:") $mount_path"
else
@@ -756,12 +782,14 @@ remove_nfs_storage() {
if awk -v mp="$mount_path" '
$2 == mp && ($3 == "nfs" || $3 == "nfs4") { next }
{ print }
' /etc/fstab > /etc/fstab.tmp && mv /etc/fstab.tmp /etc/fstab; then
' /etc/fstab > /etc/fstab.tmp && pmx_write_file /etc/fstab < /etc/fstab.tmp; then
rm -f /etc/fstab.tmp
msg_ok "$(translate "Removed entry from /etc/fstab") ($(translate "backup at /etc/fstab.proxmenux.bak"))"
else
msg_error "$(translate "Failed to edit /etc/fstab — remove the line manually.")"
fi
pmx_record_execution "reload systemd units after NFS fstab removal" "systemctl daemon-reload"
systemctl daemon-reload 2>/dev/null || true
# Try to remove the directory if empty; keep it otherwise.
@@ -778,6 +806,9 @@ remove_nfs_storage() {
}
test_nfs_connectivity() {
local FUNC_VERSION="1.0"
pmx_journal_context "test_nfs_connectivity" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Test NFS Connectivity")"
@@ -791,7 +822,8 @@ test_nfs_connectivity() {
msg_ok "$(translate "RPC Bind Service: RUNNING")"
else
msg_warn "$(translate "RPC Bind Service: STOPPED - starting...")"
systemctl start rpcbind 2>/dev/null || true
pmx_apply_setting "rpcbind active state" "systemctl is-active rpcbind 2>/dev/null || true" \
systemctl start rpcbind || true
fi
else
msg_warn "$(translate "NFS Client Tools: NOT AVAILABLE")"
+33 -5
View File
@@ -31,6 +31,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 shared functions
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
@@ -49,6 +53,10 @@ select_privileged_lxc
setup_universal_sharedfiles_group() {
local ctid="$1"
local FUNC_VERSION="1.0"
pmx_journal_context "setup_universal_sharedfiles_group" "$FUNC_VERSION"
pmx_record_execution "configure sharedfiles group and UID mappings in CT ${ctid}" \
"pct exec ${ctid} -- manage sharedfiles group, memberships and remapped users"
msg_info "$(translate "Setting sharedfiles group with UID remapping...")"
@@ -135,6 +143,9 @@ setup_universal_sharedfiles_group() {
select_mount_point() {
local FUNC_VERSION="1.0"
pmx_journal_context "select_mount_point" "$FUNC_VERSION"
while true; do
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
--menu "$(translate "How do you want to select the folder to export?")" 15 60 5 \
@@ -181,6 +192,8 @@ select_mount_point() {
--msgbox "$(translate "No mount point was specified.")" 8 50
continue
fi
pmx_record_execution "create NFS export directory ${MOUNT_POINT} in CT ${CTID}" \
"pct exec ${CTID} -- mkdir -p ${MOUNT_POINT}"
pct exec "$CTID" -- mkdir -p "$MOUNT_POINT" 2>/dev/null
return 0
;;
@@ -252,6 +265,7 @@ select_export_options() {
create_nfs_export() {
local FUNC_VERSION="1.0"
show_proxmenux_logo
msg_title "$(translate "Create LXC server NFS")"
@@ -262,6 +276,10 @@ create_nfs_export() {
get_network_config || return
select_export_options || return
pmx_journal_context "create_nfs_export" "$FUNC_VERSION"
pmx_record_execution "configure NFS export ${MOUNT_POINT} in CT ${CTID}" \
"install and enable NFS services, update /etc/exports and reload exports"
msg_ok "$(translate "Directory successfully.")"
@@ -269,7 +287,7 @@ create_nfs_export() {
if ! pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
msg_info "$(translate "Installing NFS server packages inside the CT...")"
pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y nfs-kernel-server nfs-common rpcbind"
pct exec "$CTID" -- systemctl enable --now rpcbind nfs-kernel-server
pct exec "$CTID" -- systemctl --now enable rpcbind nfs-kernel-server
msg_ok "$(translate "NFS server installed successfully.")"
else
msg_ok "$(translate "NFS server is already installed.")"
@@ -296,8 +314,8 @@ create_nfs_export() {
if pct exec "$CTID" -- grep -q "^$MOUNT_POINT " /etc/exports; then
if dialog --yesno "$(translate "Do you want to update the existing export?")" \
10 60 --title "$(translate "Update Export")"; then
pct exec "$CTID" -- sed -i "\|^$MOUNT_POINT |d" /etc/exports
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
pct exec "$CTID" -- sed --in-place "\|^$MOUNT_POINT |d" /etc/exports
pct exec "$CTID" -- bash -c "printf '%s\\n' '$EXPORT_LINE' | tee -a /etc/exports >/dev/null"
show_proxmenux_logo
msg_title "$(translate "Create LXC server NFS")"
msg_ok "$(translate "Directory successfully.")"
@@ -307,7 +325,7 @@ create_nfs_export() {
fi
else
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
pct exec "$CTID" -- bash -c "printf '%s\\n' '$EXPORT_LINE' | tee -a /etc/exports >/dev/null"
msg_ok "$(translate "Export added successfully.")"
fi
@@ -405,6 +423,9 @@ view_exports() {
}
delete_export() {
local FUNC_VERSION="1.0"
pmx_journal_context "delete_export" "$FUNC_VERSION"
if ! pct exec "$CTID" -- test -f /etc/exports; then
dialog --title "$(translate "Error")" --msgbox "\n$(translate "No exports file found.")" 8 50
return
@@ -435,7 +456,9 @@ delete_export() {
if whiptail --yesno "$(translate "Are you sure you want to delete this export?")\n\n$EXPORT_LINE" 10 70 --title "$(translate "Confirm Deletion")"; then
show_proxmenux_logo
msg_title "$(translate "Delete Export")"
pct exec "$CTID" -- sed -i "${SELECTED_NUM}d" /etc/exports
pmx_record_execution "remove NFS export line ${SELECTED_NUM} from CT ${CTID}" \
"edit /etc/exports and restart nfs-kernel-server"
pct exec "$CTID" -- sed --in-place "${SELECTED_NUM}d" /etc/exports
pct exec "$CTID" -- exportfs -ra
pct exec "$CTID" -- systemctl restart nfs-kernel-server
msg_ok "$(translate "Export deleted and NFS service restarted.")"
@@ -506,6 +529,9 @@ check_nfs_status() {
}
uninstall_nfs() {
local FUNC_VERSION="1.0"
pmx_journal_context "uninstall_nfs" "$FUNC_VERSION"
if ! pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
dialog --title "$(translate "NFS Not Installed")" --msgbox "\n$(translate "NFS server is not installed in this CT.")" 8 60
return
@@ -519,6 +545,8 @@ uninstall_nfs() {
show_proxmenux_logo
msg_title "$(translate "Uninstall NFS Server")"
pmx_record_execution "uninstall NFS server from CT ${CTID}" \
"stop and disable NFS services, clear exports, remove users, groups and packages"
msg_info "$(translate "Stopping NFS services...")"
pct exec "$CTID" -- systemctl stop nfs-kernel-server 2>/dev/null || true
+30 -5
View File
@@ -33,6 +33,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
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
@@ -49,6 +53,10 @@ select_privileged_lxc
install_samba_client() {
local FUNC_VERSION="1.0"
pmx_journal_context "install_samba_client" "$FUNC_VERSION"
pmx_record_execution "install and prepare Samba client in CT ${CTID}" \
"pct exec ${CTID} -- install cifs-utils and smbclient; create ${CREDENTIALS_DIR}"
if pct exec "$CTID" -- dpkg -s cifs-utils &>/dev/null && pct exec "$CTID" -- dpkg -s smbclient &>/dev/null; then
pct exec "$CTID" -- mkdir -p "$CREDENTIALS_DIR"
@@ -94,6 +102,9 @@ install_samba_client() {
discover_samba_servers() {
local FUNC_VERSION="1.0"
pmx_journal_context "discover_samba_servers" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Samba LXC Manager")"
msg_info "$(translate "Scanning network for Samba servers...")"
@@ -105,7 +116,7 @@ discover_samba_servers() {
for pkg in nmap samba-common-bin; do
if ! which ${pkg%%-*} >/dev/null 2>&1; then
apt-get install -y "$pkg" &>/dev/null
pmx_install_pkg "$pkg"
fi
done
@@ -678,13 +689,18 @@ configure_mount_options() {
}
create_credentials_file() {
local FUNC_VERSION="1.0"
pmx_journal_context "create_credentials_file" "$FUNC_VERSION"
if [[ "$USE_GUEST" == "true" ]]; then
return 0
fi
CRED_FILE="$CREDENTIALS_DIR/${SAMBA_SERVER}_${SAMBA_SHARE}.cred"
pmx_record_execution "create Samba credentials file ${CRED_FILE} in CT ${CTID}" \
"pct exec ${CTID} -- write credentials file and chmod 600"
pct exec "$CTID" -- bash -c "cat > '$CRED_FILE' << EOF
username=$USERNAME
@@ -729,6 +745,7 @@ EOF"
}
mount_samba_share() {
local FUNC_VERSION="1.0"
# Step 0:
install_samba_client || return
@@ -754,6 +771,10 @@ mount_samba_share() {
# Step 5:
configure_mount_options || return
pmx_journal_context "mount_samba_share" "$FUNC_VERSION"
pmx_record_execution "mount Samba share //${SAMBA_SERVER}/${SAMBA_SHARE} in CT ${CTID} at ${MOUNT_POINT}" \
"pct exec ${CTID} -- mount CIFS share; persistent=${PERMANENT_MOUNT}"
show_proxmenux_logo
msg_title "$(translate "Installing Samba Client in LXC")"
@@ -803,11 +824,11 @@ mount_samba_share() {
if [[ "$PERMANENT_MOUNT" == "true" ]]; then
pct exec "$CTID" -- sed -i "\|$MOUNT_POINT|d" /etc/fstab
pct exec "$CTID" -- sed --in-place "\|$MOUNT_POINT|d" /etc/fstab
FSTAB_ENTRY="$UNC_PATH $MOUNT_POINT cifs ${FULL_OPTIONS},_netdev,x-systemd.automount,noauto 0 0"
pct exec "$CTID" -- bash -c "echo '$FSTAB_ENTRY' >> /etc/fstab"
pct exec "$CTID" -- bash -c "printf '%s\\n' '$FSTAB_ENTRY' | tee -a /etc/fstab >/dev/null"
msg_ok "$(translate "Added to /etc/fstab for permanent mounting.")"
fi
@@ -927,6 +948,8 @@ view_samba_mounts() {
unmount_samba_share() {
local FUNC_VERSION="1.0"
pmx_journal_context "unmount_samba_share" "$FUNC_VERSION"
MOUNTS=$(pct exec "$CTID" -- mount -t cifs 2>/dev/null | awk '{print $3}' | sort -u || true)
@@ -955,7 +978,9 @@ unmount_samba_share() {
msg_title "$(translate "Unmount Samba Share")"
CRED_FILE=$(pct exec "$CTID" -- grep -E "\s+$SELECTED_MOUNT\s+" /etc/fstab 2>/dev/null | grep -o "credentials=[^, ]*" | cut -d= -f2 || true)
pct exec "$CTID" -- sed -i "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
pmx_record_execution "remove Samba mount ${SELECTED_MOUNT} from CT ${CTID}" \
"remove CT fstab entry and credentials file when present"
pct exec "$CTID" -- sed --in-place "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
msg_ok "$(translate "Removed from /etc/fstab.")"
if [[ -n "$CRED_FILE" && "$CRED_FILE" != "guest" ]]; then
+47 -8
View File
@@ -44,6 +44,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
@@ -70,6 +74,9 @@ get_storage_config() {
# ==========================================================
discover_samba_servers() {
local FUNC_VERSION="1.0"
pmx_journal_context "discover_samba_servers" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Add Samba Share as Proxmox Storage")"
msg_info "$(translate "Scanning network for Samba servers...")"
@@ -79,7 +86,7 @@ discover_samba_servers() {
for pkg in nmap samba-common-bin; do
if ! which "${pkg%%-*}" >/dev/null 2>&1; then
apt-get install -y "$pkg" &>/dev/null
pmx_install_pkg "$pkg" &>/dev/null
fi
done
@@ -274,6 +281,8 @@ add_proxmox_cifs_storage() {
local server="$2"
local share="$3"
local content="${4:-import}"
local FUNC_VERSION="1.0"
pmx_journal_context "add_proxmox_cifs_storage" "$FUNC_VERSION"
if ! command -v pvesm >/dev/null 2>&1; then
msg_error "$(translate "pvesm command not found. This should not happen on Proxmox.")"
@@ -288,6 +297,8 @@ add_proxmox_cifs_storage() {
8 60 --title "$(translate "Storage Exists")"; then
return 0
fi
pmx_record_execution "remove Proxmox CIFS storage ${storage_id}" \
"pvesm remove ${storage_id}"
pvesm remove "$storage_id" 2>/dev/null || true
fi
@@ -295,6 +306,8 @@ add_proxmox_cifs_storage() {
msg_info "$(translate "Adding CIFS storage to Proxmox...")"
local pvesm_result pvesm_output
pmx_record_execution "add Proxmox CIFS storage ${storage_id}" \
"pvesm add cifs ${storage_id} --server ${server} --share ${share} --content ${content}"
if [[ "$USE_GUEST" == "true" ]]; then
pvesm_output=$(pvesm add cifs "$storage_id" \
--server "$server" \
@@ -414,15 +427,20 @@ select_cifs_mount_options() {
# Write a root-only credentials file for the fstab mount.
# Sets HOST_CRED_FILE on success, or empty string for guest mode.
write_host_credentials_file() {
local FUNC_VERSION="1.0"
pmx_journal_context "write_host_credentials_file" "$FUNC_VERSION"
if [[ "$USE_GUEST" == "true" ]]; then
HOST_CRED_FILE=""
return 0
fi
local creds_dir="/etc/samba/credentials"
pmx_record_execution "create Samba credentials directory ${creds_dir}" \
"mkdir -p ${creds_dir}; chmod 0700 ${creds_dir}"
mkdir -p "$creds_dir"
chmod 0700 "$creds_dir"
HOST_CRED_FILE="${creds_dir}/$(echo "${SAMBA_SERVER}_${SAMBA_SHARE}" | tr -c 'A-Za-z0-9._-' '_').cred"
cat > "$HOST_CRED_FILE" <<EOF
pmx_write_file "$HOST_CRED_FILE" <<EOF
username=${USERNAME}
password=${PASSWORD}
EOF
@@ -440,10 +458,14 @@ mount_cifs_via_fstab() {
local replace="$5"
local cred_file="$6"
local use_guest="$7"
local FUNC_VERSION="1.0"
pmx_journal_context "mount_cifs_via_fstab" "$FUNC_VERSION"
msg_info "$(translate "Preparing host mount...")"
if [[ ! -d "$mount_path" ]]; then
pmx_record_execution "create CIFS mount point ${mount_path}" \
"mkdir -p ${mount_path}"
if ! mkdir -p "$mount_path" 2>/dev/null; then
msg_error "$(translate "Failed to create mount point:") $mount_path"
return 1
@@ -459,6 +481,8 @@ mount_cifs_via_fstab() {
fi
msg_info "$(translate "Mounting CIFS share...")"
pmx_record_execution "mount CIFS share //${server}/${share} at ${mount_path}" \
"mount -t cifs //${server}/${share} ${mount_path}"
if ! mount -t cifs -o "$mount_opts" "//${server}/${share}" "$mount_path" >/dev/null 2>&1; then
msg_error "$(translate "Failed to mount CIFS share on host.")"
return 1
@@ -474,11 +498,12 @@ mount_cifs_via_fstab() {
# Persist in /etc/fstab.
if [[ "$replace" == "1" ]]; then
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
fi
echo "//${server}/${share} $mount_path cifs $mount_opts 0 0" >> /etc/fstab
echo "//${server}/${share} $mount_path cifs $mount_opts 0 0" | pmx_append_file /etc/fstab
msg_ok "$(translate "Added to /etc/fstab.")"
pmx_record_execution "reload systemd after CIFS fstab update" "systemctl daemon-reload"
systemctl daemon-reload 2>/dev/null || true
echo -e ""
@@ -535,10 +560,13 @@ select_cifs_mount_methods() {
# ==========================================================
mount_cifs_share() {
local FUNC_VERSION="1.0"
pmx_journal_context "mount_cifs_share" "$FUNC_VERSION"
if ! which smbclient >/dev/null 2>&1; then
msg_info "$(translate "Installing Samba client tools...")"
apt-get update &>/dev/null
apt-get install -y cifs-utils smbclient &>/dev/null
pmx_install_pkg cifs-utils smbclient &>/dev/null
msg_ok "$(translate "Samba client tools installed")"
fi
@@ -721,6 +749,9 @@ view_cifs_storages() {
}
remove_cifs_storage() {
local FUNC_VERSION="1.0"
pmx_journal_context "remove_cifs_storage" "$FUNC_VERSION"
local OPTIONS=()
local has_pvesm=0
local has_fstab=0
@@ -784,6 +815,8 @@ remove_cifs_storage() {
show_proxmenux_logo
msg_title "$(translate "Remove CIFS Storage")"
pmx_record_execution "remove Proxmox CIFS storage ${target}" \
"pvesm remove ${target}"
if pvesm remove "$target" 2>/dev/null; then
msg_ok "$(translate "Storage") $target $(translate "removed successfully from Proxmox.")"
else
@@ -817,6 +850,8 @@ remove_cifs_storage() {
msg_title "$(translate "Remove CIFS fstab Mount")"
if mount | grep -q " on ${mount_path} type "; then
pmx_record_execution "unmount CIFS path ${mount_path}" \
"umount ${mount_path}"
if umount "$mount_path" 2>/dev/null; then
msg_ok "$(translate "Unmounted:") $mount_path"
else
@@ -831,17 +866,18 @@ remove_cifs_storage() {
if awk -v mp="$mount_path" '
$2 == mp && $3 == "cifs" { next }
{ print }
' /etc/fstab > /etc/fstab.tmp && mv /etc/fstab.tmp /etc/fstab; then
' /etc/fstab > /etc/fstab.tmp && pmx_write_file /etc/fstab < /etc/fstab.tmp; then
msg_ok "$(translate "Removed entry from /etc/fstab") ($(translate "backup at /etc/fstab.proxmenux.bak"))"
else
msg_error "$(translate "Failed to edit /etc/fstab — remove the line manually.")"
fi
pmx_record_execution "reload systemd after CIFS fstab removal" "systemctl daemon-reload"
systemctl daemon-reload 2>/dev/null || true
# Remove credentials file if it's under the standard ProxMenux dir
if [[ -n "$cred_file" && -f "$cred_file" && "$cred_file" == /etc/samba/credentials/* ]]; then
rm -f "$cred_file"
pmx_remove_file "$cred_file"
msg_ok "$(translate "Removed credentials file:") $cred_file"
fi
@@ -858,6 +894,9 @@ remove_cifs_storage() {
}
test_samba_connectivity() {
local FUNC_VERSION="1.0"
pmx_journal_context "test_samba_connectivity" "$FUNC_VERSION"
show_proxmenux_logo
msg_title "$(translate "Test Samba Connectivity")"
@@ -869,7 +908,7 @@ test_samba_connectivity() {
else
msg_warn "$(translate "CIFS Client Tools: NOT AVAILABLE - installing...")"
apt-get update &>/dev/null
apt-get install -y cifs-utils smbclient &>/dev/null
pmx_install_pkg cifs-utils smbclient &>/dev/null
msg_ok "$(translate "CIFS client tools installed.")"
fi
+22 -2
View File
@@ -32,6 +32,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
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
@@ -48,6 +52,9 @@ select_privileged_lxc
select_mount_point() {
local FUNC_VERSION="1.0"
pmx_journal_context "select_mount_point" "$FUNC_VERSION"
while true; do
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
--menu "$(translate "How do you want to select the folder to share?")" 15 60 5 \
@@ -104,12 +111,16 @@ select_mount_point() {
create_share() {
local FUNC_VERSION="1.0"
show_proxmenux_logo
msg_title "$(translate "Create Samba server service")"
sleep 2
select_mount_point || return
pmx_journal_context "create_share" "$FUNC_VERSION"
pmx_record_execution "configure Samba share ${MOUNT_POINT} in CT ${CTID}" \
"pct exec ${CTID} -- install and configure Samba share ${MOUNT_POINT}"
if ! pct exec "$CTID" -- test -d "$MOUNT_POINT"; then
@@ -311,7 +322,7 @@ EOF
msg_warn "$(translate "The share already exists in smb.conf:") [$SHARE_NAME]"
if whiptail --yesno "$(translate "Do you want to update the existing share?")" 10 60 --title "$(translate "Update Share")"; then
pct exec "$CTID" -- sed -i "/^\[$SHARE_NAME\]/,/^$/d" /etc/samba/smb.conf
pct exec "$CTID" -- sed --in-place "/^\[$SHARE_NAME\]/,/^$/d" /etc/samba/smb.conf
pct exec "$CTID" -- bash -c "echo '$CONFIG' >> /etc/samba/smb.conf"
msg_ok "$(translate "Share updated successfully.")"
else
@@ -406,6 +417,9 @@ view_shares() {
delete_share() {
local FUNC_VERSION="1.0"
pmx_journal_context "delete_share" "$FUNC_VERSION"
if ! pct exec "$CTID" -- test -f /etc/samba/smb.conf; then
dialog --backtitle "ProxMenux" --title "$(translate "Error")" --msgbox "\n$(translate "No smb.conf file found.")" 8 50
return
@@ -438,7 +452,9 @@ delete_share() {
msg_title "$(translate "Delete Share")"
pct exec "$CTID" -- sed -i "/^\[$SELECTED_SHARE\]/,/^$/d" /etc/samba/smb.conf
pmx_record_execution "remove Samba share ${SELECTED_SHARE} from CT ${CTID}" \
"pct exec ${CTID} -- remove share ${SELECTED_SHARE} from /etc/samba/smb.conf and restart smbd"
pct exec "$CTID" -- sed --in-place "/^\[$SELECTED_SHARE\]/,/^$/d" /etc/samba/smb.conf
pct exec "$CTID" -- systemctl restart smbd.service
msg_ok "$(translate "Share deleted and Samba service restarted.")"
fi
@@ -495,6 +511,7 @@ check_samba_status() {
uninstall_samba() {
local FUNC_VERSION="1.0"
if ! pct exec "$CTID" -- dpkg -s samba &>/dev/null; then
dialog --backtitle "ProxMenux" --title "$(translate "Samba Not Installed")" --msgbox "\n$(translate "Samba server is not installed in this CT.")" 8 60
@@ -510,6 +527,9 @@ uninstall_samba() {
show_proxmenux_logo
msg_title "$(translate "Uninstall Samba Server")"
pmx_journal_context "uninstall_samba" "$FUNC_VERSION"
pmx_record_execution "uninstall Samba server from CT ${CTID}" \
"pct exec ${CTID} -- stop services, preserve smb.conf backup, remove Samba users and packages"
msg_info "$(translate "Stopping Samba services...")"