From 0190acde42e9b2db676c43648205d53ee2e32701 Mon Sep 17 00:00:00 2001 From: Sergio Oller Date: Fri, 21 Apr 2023 11:20:31 +0200 Subject: [PATCH 1/7] Fix typo on minimum authorized user echo message --- scripts/init_userconf.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/init_userconf.sh b/scripts/init_userconf.sh index 972b8dfa..ac3c68c1 100755 --- a/scripts/init_userconf.sh +++ b/scripts/init_userconf.sh @@ -33,9 +33,9 @@ if [ "$USERID" -lt 1000 ]; then # Probably a macOS user, https://github.com/rock echo "$USERID is less than 1000" check_user_id=$(grep -F "auth-minimum-user-id" /etc/rstudio/rserver.conf) if [[ -n $check_user_id ]]; then - echo "minumum authorised user already exists in /etc/rstudio/rserver.conf: $check_user_id" + echo "minimum authorised user already exists in /etc/rstudio/rserver.conf: $check_user_id" else - echo "setting minumum authorised user to 499" + echo "setting minimum authorised user to 499" echo auth-minimum-user-id=499 >>/etc/rstudio/rserver.conf fi fi From 7fe5202c42edafa92f8fd556fa3fc7961864c9f2 Mon Sep 17 00:00:00 2001 From: Sergio Oller Date: Fri, 21 Apr 2023 11:21:21 +0200 Subject: [PATCH 2/7] Allow to run images under a rootless daemon --- scripts/init_userconf.sh | 70 ++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/scripts/init_userconf.sh b/scripts/init_userconf.sh index ac3c68c1..8773365f 100755 --- a/scripts/init_userconf.sh +++ b/scripts/init_userconf.sh @@ -10,6 +10,41 @@ ROOT=${ROOT:=FALSE} UMASK=${UMASK:=022} LANG=${LANG:=en_US.UTF-8} TZ=${TZ:=Etc/UTC} +RUNROOTLESS=${RUNROOTLESS:=FALSE} + + +USERHOME="/home/${USER}" + +if [ "${RUNROOTLESS}" = "true" ]; then + printf "Assuming the container runs under rootless mode as requested\n" + printf "Under rootless mode,\n" + printf " - You will log in using 'root' as user\n" + printf " - You will have root privileges within the container (e.g. apt)\n" + printf " - The files you create as root on mounted volumes will appear at the host as owned by the user who started the container\n" + printf " - You can't modify host files you don't have permission to\n" + printf " - You should NOT run in RUNROOTLESS=true if you are using the container with privileges (e.g. sudo docker run... or sudo podman run...)\n" + # The container was started asking to login as the root user. + # This is a good approach when running docker or podman rootless + # https://docs.docker.com/engine/security/rootless/ + # + # When running docker rootless or podman rootless, the root user in + # the container has the capabilities of the actual host user. Nothing else. + # + # All files modified inside the container by the root user that are mapped + # to the host will appear in the host as modified by the user who runs the + # container. However from inside the container they appear to be modified by + # root. + # + # So, the user can run apt-get as the root user inside the container. No + # need for handling sudoers, since to the container the user is root. + # + # Higher user ids in the container (e.g. 1000) get mapped to very high user + # ids at the host. We don't need that and it just confuses things + USER="root" + USERID=0 + GROUPID=0 + USERHOME="/root" +fi if [[ ${DISABLE_AUTH,,} == "true" ]]; then cp /etc/rstudio/disable_auth_rserver.conf /etc/rstudio/rserver.conf @@ -29,7 +64,17 @@ elif [ -z "$PASSWORD" ]; then printf "\n\n" fi -if [ "$USERID" -lt 1000 ]; then # Probably a macOS user, https://github.com/rocker-org/rocker/issues/205 +if [ "${RUNROOTLESS}" = "true" ]; then + check_user_id=$(grep -F "auth-minimum-user-id" /etc/rstudio/rserver.conf) + if [[ -n $check_user_id ]]; then + echo "minimum authorised user already exists in /etc/rstudio/rserver.conf: $check_user_id" + echo "RUNROOTLESS=true mode requires setting minimum authorised user to 0. Exiting" + exit 1 + else + echo "setting minimum authorised user to 0 (RUNROOTLESS=true)" + echo auth-minimum-user-id=0 >>/etc/rstudio/rserver.conf + fi +elif [ "$USERID" -lt 1000 ]; then # Probably a macOS user, https://github.com/rocker-org/rocker/issues/205 echo "$USERID is less than 1000" check_user_id=$(grep -F "auth-minimum-user-id" /etc/rstudio/rserver.conf) if [[ -n $check_user_id ]]; then @@ -40,7 +85,7 @@ if [ "$USERID" -lt 1000 ]; then # Probably a macOS user, https://github.com/rock fi fi -if [ "$USER" != "$DEFAULT_USER" ]; then +if [ "${RUNROOTLESS}" != "true" -a "$USER" != "$DEFAULT_USER" ]; then printf "\n\n" tput bold printf "Settings by \e[31m\`-e USER=\`\e[39m is now deprecated and will be removed in the future.\n" @@ -49,26 +94,29 @@ if [ "$USER" != "$DEFAULT_USER" ]; then printf "\n\n" fi -if [ "$USERID" -ne 1000 ]; then ## Configure user with a different USERID if requested. +if [ "${RUNROOTLESS}" = "true" ]; then + echo "deleting the default user ($DEFAULT_USER) since it is not needed." + userdel "$DEFAULT_USER" +elif [ "$USERID" -ne 1000 ]; then ## Configure user with a different USERID if requested. echo "deleting the default user" userdel "$DEFAULT_USER" echo "creating new $USER with UID $USERID" useradd -m "$USER" -u $USERID - mkdir -p /home/"$USER" - chown -R "$USER" /home/"$USER" + mkdir -p "${USERHOME}" + chown -R "$USER" "${USERHOME}" usermod -a -G staff "$USER" elif [ "$USER" != "$DEFAULT_USER" ]; then ## cannot move home folder when it's a shared volume, have to copy and change permissions instead - cp -r /home/"$DEFAULT_USER" /home/"$USER" + cp -r /home/"$DEFAULT_USER" "${USERHOME}" ## RENAME the user usermod -l "$USER" -d /home/"$USER" "$DEFAULT_USER" groupmod -n "$USER" "$DEFAULT_USER" usermod -a -G staff "$USER" - chown -R "$USER":"$USER" /home/"$USER" + chown -R "$USER":"$USER" "${USERHOME}" echo "USER is now $USER" fi -if [ "$GROUPID" -ne 1000 ]; then ## Configure the primary GID (whether rstudio or $USER) with a different GROUPID if requested. +if [ "${RUNROOTLESS}" != "true" -a "$GROUPID" -ne 1000 ]; then ## Configure the primary GID (whether rstudio or $USER) with a different GROUPID if requested. echo "Modifying primary group $(id "${USER}" -g -n)" groupmod -o -g $GROUPID "$(id "${USER}" -g -n)" echo "Primary group ID is now custom_group $GROUPID" @@ -78,7 +126,9 @@ fi echo "$USER:$PASSWORD" | chpasswd # Use Env flag to know if user should be added to sudoers -if [[ ${ROOT,,} == "true" ]]; then +if [ "${RUNROOTLESS}" = "true" ]; then + echo "No sudoers changes needed when running rootless" +elif [[ ${ROOT,,} == "true" ]]; then adduser "$USER" sudo && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >>/etc/sudoers echo "$USER added to sudoers" fi @@ -86,7 +136,7 @@ fi ## Change Umask value if desired if [ "$UMASK" -ne 022 ]; then echo "server-set-umask=false" >>/etc/rstudio/rserver.conf - echo "Sys.umask(mode=$UMASK)" >>/home/"$USER"/.Rprofile + echo "Sys.umask(mode=$UMASK)" >>"${USERHOME}"/.Rprofile fi ## Next one for timezone setup From 4241d4bfabbb30b52f3b262b2f568abecf59e0ed Mon Sep 17 00:00:00 2001 From: Sergio Oller Date: Fri, 21 Apr 2023 12:44:10 +0200 Subject: [PATCH 3/7] Fix linter warnings and checks --- scripts/init_userconf.sh | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/scripts/init_userconf.sh b/scripts/init_userconf.sh index 8773365f..4592d324 100755 --- a/scripts/init_userconf.sh +++ b/scripts/init_userconf.sh @@ -12,7 +12,6 @@ LANG=${LANG:=en_US.UTF-8} TZ=${TZ:=Etc/UTC} RUNROOTLESS=${RUNROOTLESS:=FALSE} - USERHOME="/home/${USER}" if [ "${RUNROOTLESS}" = "true" ]; then @@ -38,7 +37,7 @@ if [ "${RUNROOTLESS}" = "true" ]; then # So, the user can run apt-get as the root user inside the container. No # need for handling sudoers, since to the container the user is root. # - # Higher user ids in the container (e.g. 1000) get mapped to very high user + # Higher user ids in the container (e.g. 1000) get mapped to very high user # ids at the host. We don't need that and it just confuses things USER="root" USERID=0 @@ -85,7 +84,7 @@ elif [ "$USERID" -lt 1000 ]; then # Probably a macOS user, https://github.com/ro fi fi -if [ "${RUNROOTLESS}" != "true" -a "$USER" != "$DEFAULT_USER" ]; then +if [ "${RUNROOTLESS}" != "true" ] && [ "$USER" != "$DEFAULT_USER" ]; then printf "\n\n" tput bold printf "Settings by \e[31m\`-e USER=\`\e[39m is now deprecated and will be removed in the future.\n" @@ -101,7 +100,7 @@ elif [ "$USERID" -ne 1000 ]; then ## Configure user with a different USERID if r echo "deleting the default user" userdel "$DEFAULT_USER" echo "creating new $USER with UID $USERID" - useradd -m "$USER" -u $USERID + useradd -m "$USER" -u "$USERID" mkdir -p "${USERHOME}" chown -R "$USER" "${USERHOME}" usermod -a -G staff "$USER" @@ -116,9 +115,9 @@ elif [ "$USER" != "$DEFAULT_USER" ]; then echo "USER is now $USER" fi -if [ "${RUNROOTLESS}" != "true" -a "$GROUPID" -ne 1000 ]; then ## Configure the primary GID (whether rstudio or $USER) with a different GROUPID if requested. +if [ "${RUNROOTLESS}" != "true" ] && [ "$GROUPID" -ne 1000 ]; then ## Configure the primary GID (whether rstudio or $USER) with a different GROUPID if requested. echo "Modifying primary group $(id "${USER}" -g -n)" - groupmod -o -g $GROUPID "$(id "${USER}" -g -n)" + groupmod -o -g "$GROUPID" "$(id "${USER}" -g -n)" echo "Primary group ID is now custom_group $GROUPID" fi @@ -141,11 +140,11 @@ fi ## Next one for timezone setup if [ "$TZ" != "Etc/UTC" ]; then - ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ >/etc/timezone + ln -snf /usr/share/zoneinfo/"$TZ" /etc/localtime && echo "$TZ" >/etc/timezone fi ## Update Locale if needed if [ "$LANG" != "en_US.UTF-8" ]; then - /usr/sbin/locale-gen --lang $LANG - /usr/sbin/update-locale --reset LANG=$LANG + /usr/sbin/locale-gen --lang "$LANG" + /usr/sbin/update-locale --reset LANG="$LANG" fi From ba40969132cf37b7b9eaeaabfb6d6ed1d4f86d1b Mon Sep 17 00:00:00 2001 From: Sergio Oller Date: Tue, 25 Apr 2023 06:05:35 +0200 Subject: [PATCH 4/7] Detect RUNROOTLESS automatically --- scripts/init_userconf.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/init_userconf.sh b/scripts/init_userconf.sh index 4592d324..1e487601 100755 --- a/scripts/init_userconf.sh +++ b/scripts/init_userconf.sh @@ -10,12 +10,16 @@ ROOT=${ROOT:=FALSE} UMASK=${UMASK:=022} LANG=${LANG:=en_US.UTF-8} TZ=${TZ:=Etc/UTC} -RUNROOTLESS=${RUNROOTLESS:=FALSE} +RUNROOTLESS=${RUNROOTLESS:=auto} + +if [ "${RUNROOTLESS}" = "auto" ]; then + RUNROOTLESS=$(grep 4294967295 /proc/self/uid_map > /dev/null && echo "false" || echo "true") +fi USERHOME="/home/${USER}" if [ "${RUNROOTLESS}" = "true" ]; then - printf "Assuming the container runs under rootless mode as requested\n" + printf "Assuming the container runs under rootless mode\n" printf "Under rootless mode,\n" printf " - You will log in using 'root' as user\n" printf " - You will have root privileges within the container (e.g. apt)\n" From a04f770eafe73e0a523276fbc32f1b6b317853cc Mon Sep 17 00:00:00 2001 From: Sergio Oller Date: Tue, 25 Apr 2023 06:54:35 +0200 Subject: [PATCH 5/7] Mitigation for missing groups after logging in with --group-add keep-groups --- scripts/init_userconf.sh | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/scripts/init_userconf.sh b/scripts/init_userconf.sh index 1e487601..4f21a226 100755 --- a/scripts/init_userconf.sh +++ b/scripts/init_userconf.sh @@ -47,6 +47,59 @@ if [ "${RUNROOTLESS}" = "true" ]; then USERID=0 GROUPID=0 USERHOME="/root" + + # Keep all groups that have been set: + # When running rootless podman, podman may set the groups of the host user + # to the process running in the container with the option + # podman run --group-add keep-groups + # + # This option has the caveat that the GIDs which have not been mapped to + # the container in the namespace will appear as the overflow_gid (65534). + # + # While this process has the GID assigned (and therefore it has the + # privileges granted by that GID, this process cannot internally refer + # to that GID, because it is not mapped, and it appears as nobody/nogroup. + # + # This lack of mapping becomes a problem when we need to be able + # to assign those same groups to the processes created when a user logs + # in through the web interface. There, we are not able to setgroups() as + # podman did when the initial process in the container was started. + # + # What can we do? + # A solution goes through a sysadmin in the host allowing users to + # impersonate the target GID in /etc/subgid. + # + # For instance, if you have a "university_data" group, that has GID 2000 + # and you have PhD students "alice" and "bob" who are in that group, you will + # need to have an additional entry in /etc/subgid for each of them: + # alice:2000:1 + # bob:2000:1 + # + # That entry reads as + # > Grant {alice/bob} the ability to become GID 2000. + # + # Those entries should be **additional** to the already existing entries that + # grant a big number of unused GIDs. + # + # Then use `podman system migrate` to refresh podman configuration. + # + # Podman will then be able to see those groups, although unfortunately + # the group name in the container will not be "university_data" but it will + # instead look like "adm" or "sys" or "bin". + # + # I'm trying to suggest an improvement to podman to address this a bit better + # at: + # https://github.com/containers/podman/issues/18333 + # + ROOT_IN_GROUPS="$(id -G)" + for g in ${ROOT_IN_GROUPS}; do + if [ "$g" -eq 0 ] || [ "$g" -eq 65534 ]; then + # 0 is already our GID + # 65534 is nogroup (the overflow_gid) + continue + fi + usermod -aG "$g" "${USER}" + done fi if [[ ${DISABLE_AUTH,,} == "true" ]]; then From c143563d31150035e079413f6480e45f3e586493 Mon Sep 17 00:00:00 2001 From: Sergio Oller Date: Wed, 10 May 2023 07:07:49 +0200 Subject: [PATCH 6/7] Fix linting with /dev/null redirection --- scripts/init_userconf.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init_userconf.sh b/scripts/init_userconf.sh index 4f21a226..b4d31857 100755 --- a/scripts/init_userconf.sh +++ b/scripts/init_userconf.sh @@ -13,7 +13,7 @@ TZ=${TZ:=Etc/UTC} RUNROOTLESS=${RUNROOTLESS:=auto} if [ "${RUNROOTLESS}" = "auto" ]; then - RUNROOTLESS=$(grep 4294967295 /proc/self/uid_map > /dev/null && echo "false" || echo "true") + RUNROOTLESS=$(grep 4294967295 /proc/self/uid_map >/dev/null && echo "false" || echo "true") fi USERHOME="/home/${USER}" From c59a166c4edb89b4a60bb7484d2583a091baa555 Mon Sep 17 00:00:00 2001 From: Sergio Oller Date: Wed, 10 May 2023 07:08:12 +0200 Subject: [PATCH 7/7] Do not hardcode the overflowgid, ask the kernel instead --- scripts/init_userconf.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/init_userconf.sh b/scripts/init_userconf.sh index b4d31857..d3284137 100755 --- a/scripts/init_userconf.sh +++ b/scripts/init_userconf.sh @@ -92,8 +92,9 @@ if [ "${RUNROOTLESS}" = "true" ]; then # https://github.com/containers/podman/issues/18333 # ROOT_IN_GROUPS="$(id -G)" + OVERFLOWGID=$(cat "/proc/sys/kernel/overflowgid") for g in ${ROOT_IN_GROUPS}; do - if [ "$g" -eq 0 ] || [ "$g" -eq 65534 ]; then + if [ "$g" -eq 0 ] || [ "$g" -eq "${OVERFLOWGID}" ]; then # 0 is already our GID # 65534 is nogroup (the overflow_gid) continue