|
#!/usr/bin/env bash |
|
# proxmox-lxc-cifs-share.sh |
|
# https://gist.github.com/NorkzYT/14449b247dae9ac81ba4664564669299 |
|
# |
|
# Mount a CIFS/SMB share on a Proxmox VE host and bind-mount it into |
|
# an unprivileged LXC container as any container user, by dynamically |
|
# mapping UIDs/GIDs. |
|
# |
|
# --------------------------------------------------------------- |
|
# Compatible with Proxmox VE 7-8. Tested on Debian 12 host. |
|
# --------------------------------------------------------------- |
|
|
|
set -euo pipefail |
|
|
|
# |
|
#----------------------# |
|
#----- Preconditions --# |
|
#----------------------# |
|
# |
|
|
|
# Must be root |
|
if [[ $EUID -ne 0 ]]; then |
|
echo "ERROR: Please run as root." >&2 |
|
exit 1 |
|
fi |
|
|
|
# Helper for error + exit |
|
error_exit() { |
|
echo "ERROR: $1" >&2 |
|
exit 1 |
|
} |
|
|
|
# Ensure required commands exist |
|
command -v bash >/dev/null 2>&1 || error_exit "Please run with bash: bash $0" |
|
command -v pct >/dev/null 2>&1 || error_exit "'pct' command not found. Run this on the Proxmox host." |
|
command -v mount.cifs >/dev/null 2>&1 || error_exit "cifs-utils missing. Install: apt update && apt install cifs-utils" |
|
command -v systemd-escape >/dev/null 2>&1 || error_exit "systemd-escape missing. Required for systemd unit management." |
|
|
|
# |
|
#----------------------# |
|
#----- Parse Flags ----# |
|
#----------------------# |
|
# |
|
|
|
NOSERVERINO=0 |
|
if [[ "${1:-}" == "--noserverino" ]]; then |
|
NOSERVERINO=1 |
|
shift |
|
fi |
|
|
|
# |
|
#----------------------# |
|
#----- User Input -----# |
|
#----------------------# |
|
# |
|
|
|
echo "=== Proxmox CIFS Share Mount Wizard ===" |
|
read -r -p "1) Folder name under /mnt/lxc_shares (e.g. nas_rwx): " folder_name |
|
read -r -p "2) CIFS host (IP/DNS, e.g. 10.0.0.2): " cifs_host |
|
read -r -p "3) Share name (e.g. media): " share_name |
|
read -r -p "4) SMB username: " smb_username |
|
read -s -p "5) SMB password: " smb_password && echo |
|
read -r -p "6) LXC numeric ID (e.g. 105): " lxc_id |
|
read -r -p "7) Container username (e.g. ubuntu): " lxc_username |
|
echo |
|
|
|
# |
|
#----------------------# |
|
#----- Validation -----# |
|
#----------------------# |
|
# |
|
|
|
echo "Validating container ${lxc_id}..." |
|
pct status "$lxc_id" &>/dev/null \ |
|
|| error_exit "LXC ${lxc_id} does not exist." |
|
|
|
echo "Retrieving UID/GID for '${lxc_username}' inside LXC ${lxc_id}..." |
|
if ! container_uid=$(pct exec "$lxc_id" -- id -u "$lxc_username" 2>/dev/null); then |
|
error_exit "User '${lxc_username}' not found in container ${lxc_id}." |
|
fi |
|
container_gid=$(pct exec "$lxc_id" -- id -g "$lxc_username") |
|
echo " ↳ container UID=${container_uid}, GID=${container_gid}" |
|
|
|
echo "Parsing idmap offset from container config..." |
|
idmap_offset=$(pct config "$lxc_id" \ |
|
| awk '/^lxc.idmap: u 0 /{print $4; exit}') |
|
if [[ -z "$idmap_offset" ]]; then |
|
echo " ↳ Warning: idmap offset not found; defaulting to 100000" |
|
idmap_offset=100000 |
|
fi |
|
echo " ↳ host idmap offset=${idmap_offset}" |
|
|
|
host_uid=$(( idmap_offset + container_uid )) |
|
host_gid=$(( idmap_offset + container_gid )) |
|
echo " ↳ will mount with host UID=${host_uid}, GID=${host_gid}" |
|
|
|
# |
|
#----------------------# |
|
#--- Stop Container ----# |
|
#----------------------# |
|
# |
|
|
|
echo "Stopping LXC ${lxc_id}..." |
|
pct stop "$lxc_id" |
|
while [[ "$(pct status "$lxc_id")" != "status: stopped" ]]; do |
|
sleep 1 |
|
done |
|
|
|
# |
|
#----------------------# |
|
#--- Host-side Mount --# |
|
#----------------------# |
|
# |
|
|
|
mnt_root="/mnt/lxc_shares/${folder_name}" |
|
echo "Ensuring host mount point exists at ${mnt_root}..." |
|
mkdir -p "$mnt_root" |
|
|
|
echo "Building fstab entry..." |
|
options="_netdev,x-systemd.automount,noatime,nobrl" |
|
options+=",uid=${host_uid},gid=${host_gid},dir_mode=0770,file_mode=0770" |
|
options+=",username=${smb_username},password=${smb_password}" |
|
(( NOSERVERINO )) && options+=",noserverino" |
|
|
|
fstab_entry="//${cifs_host}/${share_name} ${mnt_root} cifs ${options} 0 0" |
|
|
|
# Remove any stale entry |
|
if grep -Eqs "^//${cifs_host}/${share_name}[[:space:]]+${mnt_root}[[:space:]]+cifs" /etc/fstab; then |
|
echo " ↳ Removing old /etc/fstab entry..." |
|
sed -i "\|^//${cifs_host}/${share_name} ${mnt_root} .*|d" /etc/fstab |
|
fi |
|
|
|
echo " ↳ Adding new /etc/fstab entry..." |
|
echo "$fstab_entry" >> /etc/fstab |
|
|
|
echo "Reloading systemd daemon..." |
|
systemctl daemon-reload |
|
|
|
unit_base=$(systemd-escape --path "$mnt_root") |
|
echo "Stopping potential systemd units ${unit_base}.automount & ${unit_base}.mount..." |
|
systemctl stop "${unit_base}.automount" "${unit_base}.mount" >/dev/null 2>&1 || true |
|
|
|
if mountpoint -q "$mnt_root"; then |
|
echo " ↳ Unmounting existing mount..." |
|
umount -l "$mnt_root" |
|
fi |
|
|
|
echo "Mounting //${cifs_host}/${share_name} → ${mnt_root}..." |
|
mount "$mnt_root" |
|
|
|
# |
|
#----------------------# |
|
#--- Container Bind ----# |
|
#----------------------# |
|
# |
|
|
|
echo "Configuring bind-mount into LXC (no backups)..." |
|
pct set "$lxc_id" \ |
|
--mp0 "${mnt_root},mp=/mnt/${folder_name},backup=0" |
|
echo " ↳ Bind-mount set as mp0 → /mnt/${folder_name}" |
|
|
|
echo "Starting LXC ${lxc_id}..." |
|
pct start "$lxc_id" |
|
|
|
# |
|
#----------------------# |
|
#----- Completion ------# |
|
#----------------------# |
|
# |
|
|
|
echo -e "\n✅ Configuration complete!" |
|
echo "Inspect inside the container with:" |
|
echo " ls -ld /mnt/${folder_name}" |
That suggestion from @rfResearch looks great! If @NorkzYT decides to merge it could I also make a request based on a common use case...
When running the script for the first time, all is great and I end up with CIFS share accessible in my unprivileged LXC container . But then I decide I would like the CIFS share available in another of my unprivileged LXC containers - so I'd run the script again. I am prompted to create the mount again on the host (although it already exists) - I can see from the script that it will remove the existing line in fstab if it already exists, so maybe that's good enough? Or perhaps confirm with the user that the share already exists: "would you like to skip the host mount?"... or... maybe pass in a parameter for executing in this use case to additional LXC containers, e.g.
bash ./proxmox-lxc-cifs-share.sh add
? Or we could simply even use the config file approach with all the values populated to simplify adding the share to more LXC containers?Another idea here... Is it possible to provide more than 1 user when prompted at step 7 for the lxc username? (Update: I noticed in @rfResearch's version they are not using
lxc_shares
as a group, so it's usingusername:username
which I believe is the typical way to do it, and therefore probably negates my idea of being able to add more than one user to the share - after all, I cant think of a situation where i'd need multiple different user accounts to have individual access the share)