146 lines
5.9 KiB
Bash
146 lines
5.9 KiB
Bash
#!/usr/bin/env bash
|
||
# policy: usr_smb — auto-mount network shares in user home directories.
|
||
# usr_smb_r_<name> mount read-only share ~/‹name› for logged-in members
|
||
# usr_smb_rw_<name> mount read-write share ~/‹name› for logged-in members
|
||
# rw membership takes precedence over r for the same share name.
|
||
# Credential (server, Samba user, password) stored in IPA group description by
|
||
# ansipa-smb-setup.sh: cifs://<ipa-host>:<samba-user>:<password>
|
||
# Requires: cifs-utils installed on the client.
|
||
|
||
SMB_MOUNT_STATE="$STATE_DIR/smb-mounts"
|
||
[[ -f "$SMB_MOUNT_STATE" ]] || touch "$SMB_MOUNT_STATE"
|
||
SMB_CREDS_DIR="$STATE_DIR/smb-creds"
|
||
mkdir -p "$SMB_CREDS_DIR"
|
||
chmod 700 "$SMB_CREDS_DIR"
|
||
|
||
if command -v mount.cifs &>/dev/null; then
|
||
|
||
# ── Discover usr_smb_r_* / usr_smb_rw_* from the already-fetched group list
|
||
declare -A _SMB_R_GRP _SMB_RW_GRP
|
||
|
||
while IFS= read -r _g; do
|
||
[[ -z "$_g" ]] && continue
|
||
if [[ "$_g" =~ ^usr_smb_r_(.+)$ ]]; then _SMB_R_GRP["${BASH_REMATCH[1]}"]="$_g"
|
||
elif [[ "$_g" =~ ^usr_smb_rw_(.+)$ ]]; then _SMB_RW_GRP["${BASH_REMATCH[1]}"]="$_g"
|
||
fi
|
||
done <<< "$_ALL_USER_GROUPS"
|
||
unset _g
|
||
|
||
declare -A _SMB_NAMES
|
||
for _n in "${!_SMB_R_GRP[@]}" "${!_SMB_RW_GRP[@]}"; do _SMB_NAMES["$_n"]=1; done
|
||
unset _n
|
||
|
||
_smb_member() { id -nG "$1" 2>/dev/null | tr ' ' '\n' | grep -qxF "$2"; }
|
||
|
||
declare -A _SMB_SESS
|
||
_smb_add_sess() {
|
||
local _u
|
||
[[ "$2" == "loginctl" ]] && _u=$(awk '{print $3}' <<< "$1") \
|
||
|| _u=$(awk '{print $1}' <<< "$1")
|
||
[[ -z "$_u" || "$_u" == "root" || "$_u" == "USER" ]] && return 0
|
||
local _h; _h=$(getent passwd "$_u" | cut -d: -f6 2>/dev/null) || return 0
|
||
[[ -d "$_h" ]] && _SMB_SESS["$_u"]="$_h"
|
||
}
|
||
if loginctl list-sessions --no-legend &>/dev/null; then
|
||
while IFS= read -r _L; do _smb_add_sess "$_L" "loginctl"
|
||
done < <(loginctl list-sessions --no-legend 2>/dev/null)
|
||
else
|
||
while IFS= read -r _L; do _smb_add_sess "$_L" "who"
|
||
done < <(who 2>/dev/null)
|
||
fi
|
||
unset _L
|
||
|
||
for _SMB_NAME in "${!_SMB_NAMES[@]}"; do
|
||
_R_GRP="${_SMB_R_GRP[$_SMB_NAME]:-}"
|
||
_RW_GRP="${_SMB_RW_GRP[$_SMB_NAME]:-}"
|
||
|
||
_CRED_LINE=""
|
||
[[ -n "$_RW_GRP" ]] && _CRED_LINE=$(_smb_parse_cred "$_RW_GRP")
|
||
[[ -z "$_CRED_LINE" && -n "$_R_GRP" ]] && _CRED_LINE=$(_smb_parse_cred "$_R_GRP")
|
||
|
||
if [[ -z "$_CRED_LINE" ]]; then
|
||
warn "No credential in IPA description for share $_SMB_NAME — skipped (run ansipa-smb.service on FreeIPA container)"
|
||
continue
|
||
fi
|
||
read -r _SMB_HOST _SMB_SUSER _SMB_SPASS <<< "$_CRED_LINE"
|
||
|
||
_CREDS_FILE="$SMB_CREDS_DIR/${_SMB_NAME}.creds"
|
||
printf 'username=%s\npassword=%s\ndomain=WORKGROUP\n' "$_SMB_SUSER" "$_SMB_SPASS" \
|
||
> "$_CREDS_FILE"
|
||
chmod 600 "$_CREDS_FILE"
|
||
|
||
for _SMB_U in "${!_SMB_SESS[@]}"; do
|
||
_SMB_LVL=""
|
||
[[ -n "$_RW_GRP" ]] && _smb_member "$_SMB_U" "$_RW_GRP" && _SMB_LVL="rw"
|
||
[[ -z "$_SMB_LVL" && -n "$_R_GRP" ]] && _smb_member "$_SMB_U" "$_R_GRP" && _SMB_LVL="r"
|
||
[[ -z "$_SMB_LVL" ]] && continue
|
||
|
||
_SMB_H="${_SMB_SESS[$_SMB_U]}"
|
||
_SMB_MP="$_SMB_H/$_SMB_NAME"
|
||
_SMB_SHARE="usr-${_SMB_NAME}-${_SMB_LVL}"
|
||
|
||
if mountpoint -q "$_SMB_MP" 2>/dev/null; then
|
||
grep -qxF "${_SMB_U}:${_SMB_NAME}" "$SMB_MOUNT_STATE" 2>/dev/null || \
|
||
echo "${_SMB_U}:${_SMB_NAME}" >> "$SMB_MOUNT_STATE"
|
||
continue
|
||
fi
|
||
|
||
mkdir -p "$_SMB_MP"
|
||
chown "$_SMB_U" "$_SMB_MP" 2>/dev/null || true
|
||
|
||
if mount -t cifs "//${_SMB_HOST}/${_SMB_SHARE}" "$_SMB_MP" \
|
||
-o "credentials=${_CREDS_FILE},uid=${_SMB_U},gid=${_SMB_U},file_mode=0644,dir_mode=0755,nounix,noserverino" \
|
||
2>/dev/null; then
|
||
log "Mounted //${_SMB_HOST}/${_SMB_SHARE} → $_SMB_MP for $_SMB_U"
|
||
grep -qxF "${_SMB_U}:${_SMB_NAME}" "$SMB_MOUNT_STATE" 2>/dev/null || \
|
||
echo "${_SMB_U}:${_SMB_NAME}" >> "$SMB_MOUNT_STATE"
|
||
else
|
||
warn "Failed to mount //${_SMB_HOST}/${_SMB_SHARE} for $_SMB_U"
|
||
rmdir "$_SMB_MP" 2>/dev/null || true
|
||
fi
|
||
done
|
||
done
|
||
unset _SMB_NAME _R_GRP _RW_GRP _CRED_LINE _SMB_HOST _SMB_SUSER _SMB_SPASS \
|
||
_CREDS_FILE _SMB_U _SMB_H _SMB_MP _SMB_SHARE _SMB_LVL
|
||
|
||
_NEW_SMB_MOUNTS=()
|
||
while IFS=: read -r _OLD_USER _OLD_NAME; do
|
||
[[ -z "$_OLD_USER" || -z "$_OLD_NAME" ]] && continue
|
||
_OLD_HOME=$(getent passwd "$_OLD_USER" | cut -d: -f6 2>/dev/null) || continue
|
||
_OLD_MPT="$_OLD_HOME/$_OLD_NAME"
|
||
|
||
_STILL_MEMBER=false
|
||
if [[ -n "${_SMB_R_GRP[$_OLD_NAME]+x}" ]] && \
|
||
_smb_member "$_OLD_USER" "${_SMB_R_GRP[$_OLD_NAME]}"; then
|
||
_STILL_MEMBER=true
|
||
elif [[ -n "${_SMB_RW_GRP[$_OLD_NAME]+x}" ]] && \
|
||
_smb_member "$_OLD_USER" "${_SMB_RW_GRP[$_OLD_NAME]}"; then
|
||
_STILL_MEMBER=true
|
||
fi
|
||
|
||
if [[ "$_STILL_MEMBER" == true ]]; then
|
||
_NEW_SMB_MOUNTS+=("${_OLD_USER}:${_OLD_NAME}")
|
||
else
|
||
umount -l "$_OLD_MPT" 2>/dev/null || true
|
||
rmdir "$_OLD_MPT" 2>/dev/null || true
|
||
log "Unmounted ${_OLD_MPT} for ${_OLD_USER} (left usr_smb_*_${_OLD_NAME})"
|
||
fi
|
||
done < "$SMB_MOUNT_STATE"
|
||
unset _OLD_USER _OLD_NAME _OLD_HOME _OLD_MPT _STILL_MEMBER
|
||
|
||
if [[ ${#_NEW_SMB_MOUNTS[@]} -gt 0 ]]; then
|
||
printf '%s\n' "${_NEW_SMB_MOUNTS[@]}" | sort -u > "$SMB_MOUNT_STATE"
|
||
else
|
||
> "$SMB_MOUNT_STATE"
|
||
fi
|
||
|
||
rm -f "$SMB_CREDS_DIR"/*.creds 2>/dev/null || true
|
||
|
||
unset _SMB_R_GRP _SMB_RW_GRP _SMB_NAMES _SMB_SESS _NEW_SMB_MOUNTS
|
||
|
||
else
|
||
warn "mount.cifs not found — install cifs-utils to enable usr_smb_* policies"
|
||
[[ -s "$SMB_MOUNT_STATE" ]] && \
|
||
warn "smb-mounts state is non-empty — cannot revert existing mounts without cifs-utils" || true
|
||
fi
|