Leave the image's default user as 'root' so we can modify the non-root user's uid/gid. This is a common technique used in containers to gain file permissions in mounted dirs. The default user's uid/gid can't be changed because a bash process runs under their ownership as long as the container is up. A user's uid/gid cannot be changed while that user owns a process (at least not via 'usermod').
251 lines
6.5 KiB
Bash
Executable File
251 lines
6.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
function _exit() {
|
|
if [[ "$#" -gt 1 ]]; then
|
|
# When there are multiple args, the last arg is the exit code.
|
|
echo "${@:1:($# - 1)}"
|
|
exit "${@: -1}"
|
|
fi
|
|
echo "$1"
|
|
exit 0
|
|
}
|
|
|
|
help_text="\
|
|
Add or update a user's uid
|
|
|
|
This script is intended to be run in a Docker container as a means to obtain
|
|
file permissions in mounted directories owned by the specified UID.
|
|
|
|
Usage: set_user.sh [OPTIONS] USER UID
|
|
|
|
Positionals:
|
|
USER The name of the user
|
|
|
|
Options:
|
|
--user USER The name of the user (defaults to 'bts')
|
|
--uid UID The new uid to give USER (defaults to '1000')
|
|
--group GROUP The primary group to give USER (defaults to USER)
|
|
--gid GID The primary gid to give USER (defaults to UID)
|
|
--pass PASSWORD The password to give USER (defaults to USER, only applies
|
|
if USER hasn't been created yet)
|
|
--modify-existing Update UID/GID even if USER already exists
|
|
-h,--help Print this help message and exit
|
|
|
|
"
|
|
|
|
function _add_group() {
|
|
if [[ "$#" -ne 2 ]]; then
|
|
_exit "'_add_group' requires exactly 2 args (group, gid). Received $#." 1
|
|
fi
|
|
local group="$1"
|
|
local gid="$2"
|
|
# Check if gid already exists
|
|
local existing_group=""
|
|
existing_group="$(getent group "$gid" | cut -d : -f 1)"
|
|
if [[ -n "$existing_group" ]]; then
|
|
if [[ "$existing_group" != "$group" ]]; then
|
|
echo "\
|
|
The gid '$gid' is already in use by group '$existing_group' (will not rename \
|
|
the group)"
|
|
fi
|
|
return
|
|
fi
|
|
|
|
# Check if group with same name already exists
|
|
local existing_gid=""
|
|
existing_gid="$(getent group "$group" | cut -d : -f 3)"
|
|
if [[ -n "$existing_gid" ]]; then
|
|
# The group already exists. Only modify if --modify-existing was specified.
|
|
if [[ "$modify_existing" -eq 0 ]]; then
|
|
_exit "\
|
|
The group '$group' already exists with gid '$existing_gid'.
|
|
Specify --modify-existing to change the gid anyways." 1
|
|
fi
|
|
echo "Changing gid for group '$group' from '$existing_gid' to '$gid'"
|
|
groupmod -g "$gid" "$group"
|
|
else
|
|
# The group doesn't exist. Add it
|
|
echo "Adding group '$group:$gid'"
|
|
groupadd -g "$gid" "$group"
|
|
fi
|
|
}
|
|
|
|
function _add_user() {
|
|
local user="$1"
|
|
local uid="$2"
|
|
local group="$3"
|
|
local gid="$4"
|
|
local pass="$5"
|
|
if [[ "$#" -ne 5 ]]; then
|
|
_exit "'_add_user' requires exactly 5 args (user, uid, group, gid, pass). \
|
|
Received $#." 1
|
|
fi
|
|
|
|
# Check if uid is being used by another user
|
|
local existing_user=""
|
|
existing_user="$(getent passwd "$uid" | cut -d : -f 1)"
|
|
if [[ -n "$existing_user" ]]; then
|
|
if [[ "$existing_user" != "$user" ]]; then
|
|
_exit "The uid '$uid' is already in use by user '$existing_user'." 1
|
|
fi
|
|
|
|
echo "The user '$user' already has the specified uid '$uid'."
|
|
# Modify gid if necessary
|
|
local existing_gid=""
|
|
existing_gid="$(id -g "$user" 2> /dev/null)"
|
|
if [[ "$existing_gid" = "$gid" ]]; then
|
|
echo "The user '$user' already has the specified gid '$gid'."
|
|
return
|
|
fi
|
|
|
|
if [[ "$modify_existing" -eq 0 ]]; then
|
|
_exit "\
|
|
The user '$user' is associated with the primary group '$existing_gid'.
|
|
Specify --modify-existing to change the gid anyways." 1
|
|
fi
|
|
|
|
echo "Changing primary group for '$user' from '$existing_gid' to '$gid'."
|
|
usermod -g "$gid" "$user"
|
|
return
|
|
fi
|
|
|
|
# Check if user with same name already exists
|
|
local existing_uid=""
|
|
existing_uid="$(id -u "$user" 2> /dev/null)"
|
|
if [[ -n "$existing_uid" ]]; then
|
|
if [[ "$existing_uid" = "$uid" ]]; then
|
|
_exit "The user '$user' already has the specified uid '$uid'" 0
|
|
fi
|
|
|
|
if [[ "$modify_existing" -eq 0 ]]; then
|
|
_exit "\
|
|
User '$user' already exists with uid '$existing_uid'.
|
|
Specify --modify-existing to change the uid anyways." 1
|
|
fi
|
|
|
|
echo "Changing uid for user '$user' from '$existing_uid' to '$uid'"
|
|
if ! usermod -u "$uid" -g "$gid" "$user"; then
|
|
_exit "usermod exited with code $?" 1
|
|
fi
|
|
return
|
|
fi
|
|
|
|
# The user doesn't exist. Add it
|
|
echo "Adding user '$user:$uid' (group='$group:$gid', password='$pass')"
|
|
useradd -m -u "$uid" -g "$gid" -G sudo --password "$(openssl passwd -1 "$pass")" "$user"
|
|
}
|
|
|
|
function _update_fish_dir_perms() {
|
|
local user="$1"
|
|
local uid="$2"
|
|
local gid="$3"
|
|
# When a fish shell session starts, a temp dir named '/tmp/fish.<user>' is
|
|
# created with the user's current uid/gid. If such a dir exists for the
|
|
# specified USER, make sure the uid/gid match the new values.
|
|
fish_dir="/tmp/fish.$user"
|
|
if [[ -d "$fish_dir" ]]; then
|
|
perms="$uid:$gid"
|
|
existing_perms="$(stat -c %u:%g "$fish_dir")"
|
|
if [[ "$existing_perms" != "$perms" ]]; then
|
|
echo "Changing uid:gid for dir '$fish_dir' from '$existing_perms' to '$perms'"
|
|
chown -R "$perms" "$fish_dir"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
user=""
|
|
group=""
|
|
pass=""
|
|
declare -i uid=0
|
|
declare -i gid=0
|
|
declare -i modify_existing=0
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--user)
|
|
if [[ -z "$2" ]]; then
|
|
_exit "USER (--user) must be non-empty" 1
|
|
elif [[ "$2" = "root" ]]; then
|
|
_exit "USER (--user) cannot be 'root'" 1
|
|
fi
|
|
user="$2"
|
|
shift 2
|
|
;;
|
|
--group)
|
|
if [[ -z "$2" ]]; then
|
|
_exit "GROUP (--group) must be non-empty" 1
|
|
elif [[ "$2" = "root" ]]; then
|
|
_exit "GROUP (--group) cannot be 'root'" 1
|
|
fi
|
|
group="$2"
|
|
shift 2
|
|
;;
|
|
--uid)
|
|
if [[ -z "$2" ]]; then
|
|
_exit "UID (--uid) must be non-empty" 1
|
|
fi
|
|
uid="$2"
|
|
shift 2
|
|
;;
|
|
--gid)
|
|
if [[ -z "$2" ]]; then
|
|
_exit "GID (--gid) must be non-empty" 1
|
|
fi
|
|
gid="$2"
|
|
shift 2
|
|
;;
|
|
--pass)
|
|
if [[ -z "$2" ]]; then
|
|
_exit "PASS (--pass) must be non-empty" 1
|
|
fi
|
|
pass="$2"
|
|
shift 2
|
|
;;
|
|
--modify-existing)
|
|
modify_existing=1
|
|
shift 1
|
|
;;
|
|
-h)
|
|
;&
|
|
--help)
|
|
_exit "$help_text" 0
|
|
;;
|
|
*)
|
|
if [[ -z "$1" ]]; then
|
|
_exit "Invalid positional argument '$1'" 1
|
|
elif [[ -z "$user" ]]; then
|
|
user="$1"
|
|
elif [[ -z "$uid" ]]; then
|
|
uid="$1"
|
|
else
|
|
_exit "Unexpected argument '$1'" 1
|
|
fi
|
|
shift 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$user" ]]; then
|
|
user="bts"
|
|
fi
|
|
|
|
if [[ $uid -eq 0 ]]; then
|
|
uid=1000
|
|
fi
|
|
|
|
# In general, a user's primary group/gid matches the user's name/uid.
|
|
# If group/gid args weren't specified, set them to name/uid.
|
|
if [[ -z "$group" ]]; then
|
|
group="$user"
|
|
fi
|
|
if [[ $gid -eq 0 ]]; then
|
|
gid=$uid
|
|
fi
|
|
if [[ -z "$pass" ]]; then
|
|
pass="$user"
|
|
fi
|
|
|
|
_add_group "$group" "$gid"
|
|
_add_user "$user" "$uid" "$group" "$gid" "$pass"
|
|
_update_fish_dir_perms "$user" "$uid" "$gid"
|