mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-05-22 00:24:48 +00:00
Update AppImage
This commit is contained in:
@@ -997,3 +997,207 @@ pmx_ask_permanent_mount() {
|
||||
echo "false"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# ==========================================================
|
||||
# Inspect the filesystem behind a path inside a CT and report
|
||||
# which POSIX features it supports. Used by `samba_lxc_server.sh`
|
||||
# and `nfs_lxc_server.sh` to decide whether traditional
|
||||
# chown/chmod is enough, ACLs are needed, or the filesystem
|
||||
# (exFAT, FAT32, NTFS via fuseblk) supports neither — in which
|
||||
# case the only viable path is configuring the HOST mount with
|
||||
# `uid=`/`gid=`/`fmask=`/`dmask=` options.
|
||||
#
|
||||
# Args:
|
||||
# $1 = CTID
|
||||
# $2 = path inside the CT (e.g. /mnt/media)
|
||||
#
|
||||
# Echoes a single line with 4 tab-separated fields:
|
||||
# <fstype>\t<can_chown>\t<can_acl>\t<unprivileged>
|
||||
# where can_chown / can_acl / unprivileged are "yes" / "no".
|
||||
#
|
||||
# Sample outputs:
|
||||
# "ext4 yes yes no" → ext4 on privileged CT, full POSIX
|
||||
# "zfs yes no no" → ZFS without acltype=posixacl
|
||||
# "exfat no no no" → exFAT, no POSIX semantics at all
|
||||
# "ext4 yes yes yes" → ext4 on unprivileged CT (caller
|
||||
# must keep in mind chown from
|
||||
# inside is likely to fail anyway)
|
||||
# ==========================================================
|
||||
pmx_detect_share_target_caps() {
|
||||
local ctid="$1"
|
||||
local path="$2"
|
||||
|
||||
# Filesystem reported by the kernel (NOT what fstab claims —
|
||||
# the actual mounted FS as seen from inside the CT).
|
||||
local fstype
|
||||
fstype=$(pct exec "$ctid" -- stat -f -c '%T' "$path" 2>/dev/null)
|
||||
fstype="${fstype:-unknown}"
|
||||
|
||||
local can_chown="yes"
|
||||
local can_acl="yes"
|
||||
|
||||
case "$fstype" in
|
||||
ext2*|ext3*|ext4*|xfs|btrfs|tmpfs|nfs*|cifs*|smb*)
|
||||
# Native POSIX. ACL is the kernel default for these.
|
||||
;;
|
||||
zfs)
|
||||
# ZFS supports chown natively, but POSIX ACL only when
|
||||
# acltype=posixacl. Probe with a no-op setfacl. We
|
||||
# ensure setfacl exists first; if not, install it.
|
||||
if ! pct exec "$ctid" -- bash -c "command -v setfacl >/dev/null" 2>/dev/null; then
|
||||
pct exec "$ctid" -- bash -c "apt-get install -y -qq acl >/dev/null 2>&1" || true
|
||||
fi
|
||||
if ! pct exec "$ctid" -- setfacl -m "u::rwx" "$path" >/dev/null 2>&1; then
|
||||
can_acl="no"
|
||||
fi
|
||||
;;
|
||||
msdos|vfat|exfat|ntfs|fuseblk)
|
||||
# These filesystems do not carry POSIX ownership / mode
|
||||
# / ACL at all. Permissions come exclusively from the
|
||||
# mount-time options (uid=, gid=, fmask=, dmask=).
|
||||
can_chown="no"
|
||||
can_acl="no"
|
||||
;;
|
||||
*)
|
||||
# Unknown FS — probe both. We try chown to ourselves
|
||||
# (no-op when it succeeds) and a no-op setfacl. Both
|
||||
# are cheap and tell us what works.
|
||||
local cur_owner
|
||||
cur_owner=$(pct exec "$ctid" -- stat -c '%U:%G' "$path" 2>/dev/null)
|
||||
if [[ -z "$cur_owner" ]] || ! pct exec "$ctid" -- chown "$cur_owner" "$path" >/dev/null 2>&1; then
|
||||
can_chown="no"
|
||||
fi
|
||||
if ! pct exec "$ctid" -- bash -c "command -v setfacl >/dev/null" 2>/dev/null; then
|
||||
pct exec "$ctid" -- bash -c "apt-get install -y -qq acl >/dev/null 2>&1" || true
|
||||
fi
|
||||
if ! pct exec "$ctid" -- setfacl -m "u::rwx" "$path" >/dev/null 2>&1; then
|
||||
can_acl="no"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# CT type — privileged (unprivileged: 0) lets chown / chmod
|
||||
# run as effective host root. Unprivileged CTs have a user
|
||||
# namespace mapping and chown from inside the CT typically
|
||||
# fails on host-side bind mounts.
|
||||
local unprivileged
|
||||
unprivileged=$(pct config "$ctid" 2>/dev/null | awk -F': ' '/^unprivileged:/ {print $2; exit}')
|
||||
local unpriv_flag="no"
|
||||
[[ "$unprivileged" == "1" ]] && unpriv_flag="yes"
|
||||
|
||||
printf '%s\t%s\t%s\t%s\n' "$fstype" "$can_chown" "$can_acl" "$unpriv_flag"
|
||||
}
|
||||
|
||||
|
||||
# ==========================================================
|
||||
# Configure ownership / permissions on a shared mountpoint so
|
||||
# the given Samba/NFS user can write to it. Branches by the
|
||||
# filesystem capabilities reported by pmx_detect_share_target_caps.
|
||||
#
|
||||
# Args:
|
||||
# $1 = CTID
|
||||
# $2 = mount point inside the CT
|
||||
# $3 = username inside the CT (must already exist)
|
||||
#
|
||||
# Returns:
|
||||
# 0 on success or partial success (warnings shown).
|
||||
# 1 only on hard failures the caller should refuse to proceed on.
|
||||
#
|
||||
# Expects the global helper `sharedfiles` group to already exist
|
||||
# in the CT (caller is responsible for that — see
|
||||
# setup_universal_sharedfiles_group).
|
||||
# ==========================================================
|
||||
pmx_setup_share_permissions() {
|
||||
local ctid="$1"
|
||||
local mp="$2"
|
||||
local username="$3"
|
||||
|
||||
# Probe filesystem capabilities.
|
||||
local caps fstype can_chown can_acl unpriv
|
||||
caps=$(pmx_detect_share_target_caps "$ctid" "$mp")
|
||||
IFS=$'\t' read -r fstype can_chown can_acl unpriv <<<"$caps"
|
||||
|
||||
msg_info "$(translate "Detected filesystem at $mp:") $fstype (chown=$can_chown, acl=$can_acl, unprivileged_ct=$unpriv)"
|
||||
|
||||
# Always ensure the user is in the sharedfiles group — this
|
||||
# is harmless regardless of FS capabilities. Skip when no user
|
||||
# was passed (NFS path: only the group matters, no per-user ACL).
|
||||
if [[ -n "$username" ]]; then
|
||||
pct exec "$ctid" -- usermod -aG sharedfiles "$username" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# ACL spec — include the user only when one is provided.
|
||||
local acl_spec="g:sharedfiles:rwx,m::rwx"
|
||||
if [[ -n "$username" ]]; then
|
||||
acl_spec="u:$username:rwx,$acl_spec"
|
||||
fi
|
||||
|
||||
if [[ "$can_chown" == "yes" ]]; then
|
||||
# POSIX-friendly filesystem. Set group ownership +
|
||||
# setgid bit so new files inherit the group.
|
||||
if pct exec "$ctid" -- chown root:sharedfiles "$mp" 2>/dev/null \
|
||||
&& pct exec "$ctid" -- chmod 2775 "$mp" 2>/dev/null; then
|
||||
msg_ok "$(translate "Ownership set to root:sharedfiles with 2775 on:") $mp"
|
||||
else
|
||||
msg_warn "$(translate "chown/chmod failed — likely unprivileged CT against host bind mount. Falling back to ACL.")"
|
||||
fi
|
||||
|
||||
if [[ "$can_acl" == "yes" ]]; then
|
||||
# Access + default ACL so new files clients create
|
||||
# inherit write permission for the sharedfiles group
|
||||
# (and the Samba user, when one is provided). Without
|
||||
# `-d` (default ACL) the parent's ACL doesn't propagate
|
||||
# to children → new files end up with restrictive 755
|
||||
# and clients get "permission denied" on the next write.
|
||||
# `m::rwx` keeps the ACL mask from clipping rwx grants.
|
||||
pct exec "$ctid" -- setfacl -R -m "$acl_spec" "$mp" 2>/dev/null || true
|
||||
pct exec "$ctid" -- setfacl -R -d -m "$acl_spec" "$mp" 2>/dev/null || true
|
||||
msg_ok "$(translate "POSIX ACLs applied (access + default for inheritance).")"
|
||||
else
|
||||
msg_warn "$(translate "Filesystem $fstype does not support POSIX ACLs — relying on group ownership only.")"
|
||||
if [[ "$fstype" == "zfs" ]]; then
|
||||
msg_warn "$(translate "Tip: zfs set acltype=posixacl xattr=sa <pool>/<dataset> enables full ACL support.")"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# exFAT / FAT32 / NTFS-fuse / similar — permissions live
|
||||
# entirely in the host mount options. Don't waste cycles
|
||||
# trying chown/chmod/setfacl; tell the user what to do
|
||||
# and refuse to silently produce a broken share.
|
||||
local uid_in_ct gid_in_ct
|
||||
uid_in_ct=$(pct exec "$ctid" -- id -u "$username" 2>/dev/null)
|
||||
gid_in_ct=$(pct exec "$ctid" -- getent group sharedfiles 2>/dev/null | cut -d: -f3)
|
||||
msg_warn "$(translate "Filesystem $fstype does NOT support chown/chmod/ACL.")"
|
||||
msg_warn "$(translate "On a privileged CT the mount options carry the only permissions.")"
|
||||
msg_warn "$(translate "Stop the CT, unmount the disk on the HOST, and remount with:")"
|
||||
echo
|
||||
echo " mount -o uid=${uid_in_ct:-1000},gid=${gid_in_ct:-100},fmask=0002,dmask=0002 <device> <hostpath>"
|
||||
echo
|
||||
msg_warn "$(translate "Then update /etc/fstab on the host with the same options.")"
|
||||
msg_warn "$(translate "Recommendation: reformat the disk to ext4 for a robust setup — see docs.")"
|
||||
fi
|
||||
|
||||
# Verify the user can actually write. `runuser` instead of
|
||||
# `su` — `pct exec ... su -` raises 'cannot set groups:
|
||||
# Operation not permitted' due to a PAM/cap quirk with the
|
||||
# exec entry path; runuser doesn't have that issue.
|
||||
# Skipped for the NFS path (no specific user to test as — the
|
||||
# NFS server itself decides UID mapping at export time).
|
||||
if [[ -z "$username" ]]; then
|
||||
msg_ok "$(translate "Directory configured for sharedfiles group access on:") $mp"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local has_access
|
||||
has_access=$(pct exec "$ctid" -- runuser -u "$username" -- \
|
||||
bash -c "test -w '$mp' && echo yes || echo no" 2>/dev/null)
|
||||
if [[ "$has_access" == "yes" ]]; then
|
||||
msg_ok "$(translate "Write access verified for user:") $username"
|
||||
return 0
|
||||
else
|
||||
msg_error "$(translate "Write access test FAILED for user:") $username"
|
||||
msg_warn "$(translate "Samba/NFS clients will likely receive 'permission denied'. Review the steps above.")"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -602,12 +602,9 @@ EOF
|
||||
|
||||
install_log2ram_auto() {
|
||||
local FUNC_VERSION="1.2"
|
||||
|
||||
# description: Install Log2RAM with size auto-tuned to host RAM (128M/256M/512M); SSD/M.2 detection skips on rotational disks.
|
||||
# ── Reinstall detection ─────────────────────────────────────────────────
|
||||
# If log2ram was previously installed by ProxMenux, skip hardware detection
|
||||
# and reinstall directly — no prompts, transparent to user. Sprint 12A:
|
||||
# also matches the new structured form `{"installed": true, ...}` written by
|
||||
# the updated register_tool, in addition to the legacy boolean true entry.
|
||||
|
||||
if [[ -f "$TOOLS_JSON" ]] && jq -e '.log2ram == true or .log2ram.installed == true' "$TOOLS_JSON" >/dev/null 2>&1; then
|
||||
msg_ok "$(translate "Log2RAM already registered — updating to latest configuration")"
|
||||
else
|
||||
@@ -854,6 +851,11 @@ EOF
|
||||
#msg_ok "$(translate "Backup created:") /etc/systemd/journald.conf.bak.$(date +%Y%m%d-%H%M%S)"
|
||||
msg_ok "$(translate "Journald configuration adjusted to") ${USE_MB}M (Log2RAM ${LOG2RAM_SIZE})"
|
||||
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
systemctl restart log2ram >/dev/null 2>&1 || true
|
||||
log2ram clean >/dev/null 2>&1 || true
|
||||
log2ram write >/dev/null 2>&1 || true
|
||||
systemctl restart rsyslog >/dev/null 2>&1 || true
|
||||
|
||||
register_tool "log2ram" true "$FUNC_VERSION"
|
||||
}
|
||||
@@ -933,6 +935,7 @@ enable_zfs_autotrim() {
|
||||
fi
|
||||
|
||||
if ! pool_supports_autotrim "$pool"; then
|
||||
stop_spinner
|
||||
msg_info2 "$(translate "Pool does not appear to use SSD/NVMe devices with discard support. Skipping ZFS autotrim for pool:") $pool"
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -280,9 +280,13 @@ create_nfs_export() {
|
||||
|
||||
|
||||
msg_info "$(translate "Setting directory ownership and permissions...")"
|
||||
pct exec "$CTID" -- chown root:sharedfiles "$MOUNT_POINT"
|
||||
pct exec "$CTID" -- chmod 2775 "$MOUNT_POINT"
|
||||
msg_ok "$(translate "Directory configured with sharedfiles group ownership")"
|
||||
# Hand off ownership/perm setup to the shared helper. It detects the
|
||||
# underlying filesystem (ext4/xfs/zfs/exfat/ntfs-fuse/…), picks the
|
||||
# right strategy (chown+chmod, ACLs, or just inform the user when
|
||||
# the FS can't carry POSIX permissions), and verifies the result
|
||||
# with `runuser`. Empty username — NFS doesn't authenticate per-user
|
||||
# the way Samba does; the `sharedfiles` group is all we need.
|
||||
pmx_setup_share_permissions "$CTID" "$MOUNT_POINT" ""
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -172,16 +172,15 @@ create_share() {
|
||||
IS_MOUNTED=$(pct exec "$CTID" -- mount | grep "$MOUNT_POINT" || true)
|
||||
if [[ -n "$IS_MOUNTED" ]]; then
|
||||
msg_info "$(translate "Detected a mounted directory from host. Setting up shared group...")"
|
||||
|
||||
# Match the GID `nfs_lxc_server.sh` uses (101000) so the same
|
||||
# `sharedfiles` group bridges Samba- and NFS-served paths. The
|
||||
# previous `999` was inconsistent — files written via Samba were
|
||||
# owned by GID 999 and not visible to NFS clients accessing the
|
||||
# same dataset. Audit Tier 6 — GID inconsistente.
|
||||
|
||||
# The `sharedfiles` group bridges Samba- and NFS-served paths so a
|
||||
# file written by one protocol is writable by the other. Fixed GID
|
||||
# 101000 keeps the group ID consistent across CTs / hosts that
|
||||
# share the same mount.
|
||||
SHARE_GID=101000
|
||||
GROUP_EXISTS=$(pct exec "$CTID" -- getent group sharedfiles || true)
|
||||
GID_IN_USE=$(pct exec "$CTID" -- getent group "$SHARE_GID" | cut -d: -f1 || true)
|
||||
|
||||
|
||||
if [[ -z "$GROUP_EXISTS" ]]; then
|
||||
if [[ -z "$GID_IN_USE" ]]; then
|
||||
pct exec "$CTID" -- groupadd -g "$SHARE_GID" sharedfiles
|
||||
@@ -193,65 +192,23 @@ create_share() {
|
||||
else
|
||||
msg_ok "$(translate "Group 'sharedfiles' already exists inside the CT")"
|
||||
fi
|
||||
|
||||
if pct exec "$CTID" -- getent group sharedfiles >/dev/null; then
|
||||
pct exec "$CTID" -- usermod -aG sharedfiles "$USERNAME"
|
||||
# chown/chmod on a host bind-mount FAIL with "Operation not
|
||||
# permitted" inside an unprivileged CT — the kernel won't let
|
||||
# an unprivileged user namespace change ownership of files
|
||||
# that belong to a different (real-host) UID. The host owns
|
||||
# the directory; we only need write access for $USERNAME and
|
||||
# the `sharedfiles` group, which the ACL block below handles.
|
||||
# Silence the failure so it doesn't look alarming in the log.
|
||||
pct exec "$CTID" -- chown root:sharedfiles "$MOUNT_POINT" 2>/dev/null || true
|
||||
pct exec "$CTID" -- chmod 2775 "$MOUNT_POINT" 2>/dev/null || true
|
||||
else
|
||||
msg_error "$(translate "Group 'sharedfiles' was not created successfully. Skipping chown/usermod.")"
|
||||
fi
|
||||
|
||||
# Apply BOTH access and default POSIX ACLs unconditionally.
|
||||
# Previously this ran only when `test -w` failed for $USERNAME —
|
||||
# but a local `test -w` says nothing about whether Samba can
|
||||
# write through the share. Once Windows creates a *new* file or
|
||||
# subfolder, it inherits the parent's effective ACL; without a
|
||||
# `default:` entry the new entry has no ACL at all and falls
|
||||
# back to the host bind-mount's restrictive 755 → Windows shows
|
||||
# "permission denied" even though the same user can write from
|
||||
# inside the CT shell. The `-d` flag is what fixes that.
|
||||
# `m::rwx` keeps the ACL mask from clipping rwx grants.
|
||||
if pct exec "$CTID" -- bash -c "command -v setfacl >/dev/null"; then
|
||||
pct exec "$CTID" -- setfacl -R \
|
||||
-m "u:$USERNAME:rwx,g:sharedfiles:rwx,m::rwx" \
|
||||
"$MOUNT_POINT" 2>/dev/null || true
|
||||
pct exec "$CTID" -- setfacl -R -d \
|
||||
-m "u:$USERNAME:rwx,g:sharedfiles:rwx,m::rwx" \
|
||||
"$MOUNT_POINT" 2>/dev/null || true
|
||||
else
|
||||
pct exec "$CTID" -- apt-get install -y -qq acl >/dev/null 2>&1 || true
|
||||
pct exec "$CTID" -- setfacl -R \
|
||||
-m "u:$USERNAME:rwx,g:sharedfiles:rwx,m::rwx" \
|
||||
"$MOUNT_POINT" 2>/dev/null || true
|
||||
pct exec "$CTID" -- setfacl -R -d \
|
||||
-m "u:$USERNAME:rwx,g:sharedfiles:rwx,m::rwx" \
|
||||
"$MOUNT_POINT" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
HAS_ACCESS=$(pct exec "$CTID" -- su -s /bin/bash -c "test -w '$MOUNT_POINT' && echo yes || echo no" "$USERNAME" 2>/dev/null)
|
||||
if [ "$HAS_ACCESS" = "no" ]; then
|
||||
msg_warn "$(translate "ACL applied but write test still failed — check host-side permissions of:") $MOUNT_POINT"
|
||||
else
|
||||
msg_ok "$(translate "Write access (incl. default ACL for new files) confirmed for user:") $USERNAME"
|
||||
fi
|
||||
# Hand off ownership/perm setup to the shared helper. It detects
|
||||
# the underlying filesystem (ext4/xfs/zfs/exfat/ntfs-fuse/…), picks
|
||||
# the right strategy (chown+chmod, ACLs, or just inform the user
|
||||
# if the FS can't carry POSIX permissions), and verifies write
|
||||
# access with `runuser` (avoids the `su: cannot set groups`
|
||||
# PAM quirk that hits `pct exec`).
|
||||
pmx_setup_share_permissions "$CTID" "$MOUNT_POINT" "$USERNAME"
|
||||
else
|
||||
msg_ok "$(translate "No shared mount detected. Applying standard local access.")"
|
||||
# Local (CT-internal) path — chown/chmod should normally succeed,
|
||||
# but on rare bind setups (e.g. zfs with acltype=off) they can
|
||||
# still trip. Suppress stderr to keep the log clean; the
|
||||
# write-access probe below is the source of truth.
|
||||
pct exec "$CTID" -- chown -R "$USERNAME:$USERNAME" "$MOUNT_POINT" 2>/dev/null || true
|
||||
pct exec "$CTID" -- chmod -R 755 "$MOUNT_POINT" 2>/dev/null || true
|
||||
# Local (CT-internal) path: rootfs is always POSIX-friendly, so
|
||||
# chown/chmod always succeed. Keep the previous behaviour.
|
||||
pct exec "$CTID" -- chown -R "$USERNAME:$USERNAME" "$MOUNT_POINT"
|
||||
pct exec "$CTID" -- chmod -R 755 "$MOUNT_POINT"
|
||||
|
||||
HAS_ACCESS=$(pct exec "$CTID" -- su -s /bin/bash -c "test -w '$MOUNT_POINT' && echo yes || echo no" "$USERNAME" 2>/dev/null)
|
||||
HAS_ACCESS=$(pct exec "$CTID" -- runuser -u "$USERNAME" -- \
|
||||
bash -c "test -w '$MOUNT_POINT' && echo yes || echo no" 2>/dev/null)
|
||||
if [ "$HAS_ACCESS" = "no" ]; then
|
||||
pct exec "$CTID" -- setfacl -R -m "u:$USERNAME:rwx" "$MOUNT_POINT" 2>/dev/null || true
|
||||
msg_warn "$(translate "ACL permissions applied for local access for user:") $USERNAME"
|
||||
@@ -268,6 +225,14 @@ create_share() {
|
||||
|
||||
SHARE_NAME=$(basename "$MOUNT_POINT")
|
||||
|
||||
# `force user = $USERNAME` makes every Samba file operation happen
|
||||
# under that unix UID regardless of the connecting Windows account.
|
||||
# Combined with `force group = sharedfiles` and the matching
|
||||
# ownership / ACLs applied earlier, this is what keeps writes
|
||||
# consistent on host bind-mounts where the kernel sees Samba's
|
||||
# impersonated UID — without it Windows can authenticate fine but
|
||||
# writes silently fail because Samba ends up writing as some other
|
||||
# mapped UID with no permission on the target.
|
||||
case "$SHARE_OPTIONS" in
|
||||
rw)
|
||||
CONFIG=$(cat <<EOF
|
||||
@@ -279,6 +244,7 @@ create_share() {
|
||||
browseable = yes
|
||||
guest ok = no
|
||||
valid users = $USERNAME
|
||||
force user = $USERNAME
|
||||
force group = sharedfiles
|
||||
create mask = 0664
|
||||
directory mask = 2775
|
||||
@@ -298,6 +264,7 @@ EOF
|
||||
browseable = yes
|
||||
guest ok = no
|
||||
valid users = $USERNAME
|
||||
force user = $USERNAME
|
||||
force group = sharedfiles
|
||||
veto files = /lost+found/
|
||||
EOF
|
||||
@@ -310,6 +277,7 @@ EOF
|
||||
comment = Custom shared folder for $USERNAME
|
||||
path = $MOUNT_POINT
|
||||
valid users = $USERNAME
|
||||
force user = $USERNAME
|
||||
force group = sharedfiles
|
||||
$CUSTOM_CONFIG
|
||||
veto files = /lost+found/
|
||||
@@ -326,6 +294,7 @@ EOF
|
||||
browseable = yes
|
||||
guest ok = no
|
||||
valid users = $USERNAME
|
||||
force user = $USERNAME
|
||||
force group = sharedfiles
|
||||
create mask = 0664
|
||||
directory mask = 2775
|
||||
|
||||
Reference in New Issue
Block a user