Skip to content

Instantly share code, notes, and snippets.

@parity3
Created December 1, 2023 04:41
Show Gist options
  • Save parity3/1d56e59ab49a25f4f8bd09b82a6bafd4 to your computer and use it in GitHub Desktop.
Save parity3/1d56e59ab49a25f4f8bd09b82a6bafd4 to your computer and use it in GitHub Desktop.
Create and switch to a user matching the owner of the first bind mount
#!/usr/bin/env bash
set -o pipefail
set -e
set -x
function _get_userid_from_mounts() {
type -fp awk >/dev/null || return 1
local mountpath
local folder_uid
while read -r mountpath ; do
folder_uid=$(stat --printf %u "${mountpath}") || continue
[ "${folder_uid:-0}" = "0" ] || break
done < <(findmnt -it \
sysfs,cgroup,proc,devtmpfs,devpts,pstore,debugfs,hugetlbfs,mqueue,configfs,tmpfs,shm,overlay,nsfs,tracefs,binfmt_misc,fusectl -r \
-noTARGET,SOURCE | awk '$2 ~ /\/var\/lib\/docker\/|\/libexec\// {next} $1 == "/" {next} $1 ~ /^\/(dev|snap|proc|run|sys)/ {next} {print $1}'
)
if [ "${mountpath:-0}" = "0" ] || [ "${folder_uid:-0}" = "0" ] ; then
return 1
fi
_UID=$(stat --printf %u "${mountpath}") || return 1
_GID=$(stat --printf %g "${mountpath}") || return 1
}
function _get_userid_from_mount() {
type -fp awk >/dev/null || return 1
local mountpath
local folder_uid
while read -r mountpath ; do
folder_uid=$(stat --printf %u "${mountpath}") || continue
[ "${folder_uid:-0}" = "0" ] || break
done < <(mount |
awk '$1 ~ /\/var\/lib\/docker\/|\/libexec\// {next} $3 == "/" {next} $3 ~ /^\/(dev|snap|proc|run|sys)/ {next} {print $3}'
)
if [ "${mountpath:-0}" = "0" ] || [ "${folder_uid:-0}" = "0" ] ; then
return 1
fi
_UID=$(stat --printf %u "${mountpath}") || return 1
_GID=$(stat --printf %g "${mountpath}") || return 1
}
function _get_userid_from_passwd() {
type -fp awk >/dev/null || return 1
_UID=$(getent passwd | awk -F: '$3 >= 1000 && $3 < 65000 {u=$3} END {if (u) {print u} else {exit 1}}') || return 1
_GID=$(id -g "${_UID}")
}
function _get_userid() {
if ! _get_userid_from_mounts ; then
if ! _get_userid_from_mount ; then
if ! _get_userid_from_passwd ; then
_UID=${DEFAULT_UID:-1001}
_GID=${DEFAULT_GID:-1001}
fi
fi
fi
}
function _create_user() {
# If a user with the desire id exists, use its name as-is (ignore UNAME).
# If the group with the desired gid exists, set user's primary group to that gid.
if getent passwd "${_UID}" 2>/dev/null 1>&2; then
# The user exists. Check if the user's primary group matches
UNAME=$(id -un "${_UID}")
if getent group "${_GID}" 2>/dev/null 1>&2 ; then
PRIMARY_GID=$(id -g "${_UID}")
[ "${PRIMARY_GID}" = "${_GID}" ] || usermod -g "${_GID}" "${UNAME}"
else
groupadd -g "${_GID}" "${GNAME:-unpriv}"
usermod -g "${_GID}" "${UNAME}"
fi
else
# The desired user id does not exist. If the desired username exists, rename it to old.
# Same with the group matching gid.
# Create the new user with desired name and group.
# Re-add the old group as a secondary group to the new user so that we have a better chance at accessing files.
UNAME=${UNAME:-unpriv}
GNAME=${GNAME:-unpriv}
local GNAME_OLD
if getent passwd "${UNAME}" 2>/dev/null 1>&2 ; then
usermod -l "${UNAME}_old" "${UNAME}"
fi
if ! getent group "${_GID}" 2>/dev/null 1>&2 ; then
if getent group "${GNAME}" 2>/dev/null 1>&2 ; then
GNAME_OLD="${GNAME}_old"
groupmod -n "${GNAME}_old" "${GNAME}"
fi
groupadd -g "${_GID}" "${GNAME}"
fi
local shell_path
shell_path=$(type -fp bash) || shell_path=$(type -fp sh) || shell_path=/bin/sh
useradd -u "${_UID}" -g "${_GID}" -s "${shell_path}" "${UNAME}"
if [ -n "${GNAME_OLD}" ] ; then
usermod -aG "${GNAME_OLD}" "${UNAME}"
fi
fi
}
function _create_homedir() {
# Once the user and group are set up, we must re-examine the user's home directory.
# Make sure it is owned by the proper uid/gid with 750 permissions
HOMEDIR=$(getent passwd "$_UID" |awk -F: '{print $6}')
if [ -e "${HOMEDIR}" ] ; then
if [ "$(stat --printf %u "${HOMEDIR}")" != "${_UID}" ] ; then
chown "${_UID}:${_GID}" "${HOMEDIR}"
chmod 750 "${HOMEDIR}"
fi
else
mkdir -p "${HOMEDIR}"
chown "${_UID}:${_GID}" "${HOMEDIR}"
chmod 750 "${HOMEDIR}"
fi
}
function _save_env() {
echo "#!/usr/bin/env bash"
declare -px | awk '$3 !~ /^(OLDPWD|HOSTNAME|PWD|USER|SHELL|_.*|PS1|HOME|SHLVL|LS_COLORS)=/'
echo "cd \"${PWD}\""
printf ' %q' exec "$@"
echo ""
}
function _unpriv_exec() {
_save_env "$@" >/saved_env.sh
chmod +x /saved_env.sh
exec su "${UNAME}" -- "$0" "$@"
}
function _exec_if_not_root() {
if [ "$(id -u)" != 0 ] ; then
if [ -e /saved_env.sh ] ; then
exec /saved_env.sh
else
exec "$@"
fi
fi
}
function ensure_unpriv() {
_exec_if_not_root "$@"
_get_userid
_create_user
_create_homedir
_unpriv_exec "$@"
}
if [ "$1" != "load" ]; then
ensure_unpriv "$@"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment