diff --git a/01_set_env.sh b/01_set_env.sh new file mode 100644 index 0000000..dda5dad --- /dev/null +++ b/01_set_env.sh @@ -0,0 +1,14 @@ +#!/usr/bin/with-contenv bash +# shellcheck shell=bash + +## Set our dynamic variables in Renviron.site to be reflected by RStudio Server or Shiny Server +exclude_vars="HOME PASSWORD RSTUDIO_VERSION BATCH_USER_CREATION" +for file in /var/run/s6/container_environment/* +do + sed -i "/^${file##*/}=/d" ${R_HOME}/etc/Renviron.site + regex="(^| )${file##*/}($| )" + [[ ! $exclude_vars =~ $regex ]] && echo "${file##*/}=$(cat $file)" >> ${R_HOME}/etc/Renviron.site || echo "skipping $file" +done + +## only file-owner (root) should read container_environment files: +chmod 600 /var/run/s6/container_environment/* diff --git a/02_userconf.sh b/02_userconf.sh new file mode 100644 index 0000000..9831fe2 --- /dev/null +++ b/02_userconf.sh @@ -0,0 +1,117 @@ +#!/usr/bin/with-contenv bash +# shellcheck shell=bash + +## Set defaults for environmental variables in case they are undefined +DEFAULT_USER=${DEFAULT_USER:-rstudio} +USER=${USER:=${DEFAULT_USER}} +USERID=${USERID:=1000} +GROUPID=${GROUPID:=1000} +ROOT=${ROOT:=FALSE} +UMASK=${UMASK:=022} +LANG=${LANG:=en_US.UTF-8} +TZ=${TZ:=Etc/UTC} +HOME=/home/${USER} + +bold=$(tput bold) +normal=$(tput sgr0) + +if [[ ${DISABLE_AUTH,,} == "true" ]] + +then + mv /etc/rstudio/disable_auth_rserver.conf /etc/rstudio/rserver.conf + echo "USER=$USER" >> /etc/environment +fi + +if grep --quiet "auth-none=1" /etc/rstudio/rserver.conf +then + echo "Skipping authentication as requested" +elif [ -z "$PASSWORD" ] +then + PASSWORD=$(pwgen 16 1) + printf "\n\n" + tput bold + printf "The password is set to \e[31m%s\e[39m\n" "$PASSWORD" + printf "If you want to set your own password, set the PASSWORD environment variable. e.g. run with:\n" + printf "docker run -e PASSWORD=\e[92m\e[39m -p 8787:8787 rocker/rstudio\n" + tput sgr0 + printf "\n\n" +fi + +if [ "$USERID" -lt 1000 ] +# Probably a macOS user, https://github.com/rocker-org/rocker/issues/205 + then + echo "$USERID is less than 1000" + check_user_id=$(grep -F "auth-minimum-user-id" /etc/rstudio/rserver.conf) + if [[ ! -z $check_user_id ]] + then + echo "minumum authorised user already exists in /etc/rstudio/rserver.conf: $check_user_id" + else + echo "setting minumum authorised user to 499" + echo auth-minimum-user-id=499 >> /etc/rstudio/rserver.conf + fi +fi + +if [ "$USERID" -ne 1000 ] +## Configure user with a different USERID if requested. + then + 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 + 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 || true + ## 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 + echo "USER is now $USER" +fi + +[ "$USER" != "rstudio" ] && rm -fr /home/rstudio + +if [ "$GROUPID" -ne 1000 ] +## Configure the primary GID (whether rstudio or $USER) with a different GROUPID if requested. + then + echo "Modifying primary group $(id $USER -g -n)" + groupmod -g $GROUPID $(id $USER -g -n) + echo "Primary group ID is now custom_group $GROUPID" +fi + +if [[ ${DISABLE_AUTH,,} != "true" ]] +then + ## Add a password to user + echo "$USER:$PASSWORD" | chpasswd +fi + +# Use Env flag to know if user should be added to sudoers +if [[ ${ROOT,,} == "true" ]] + then + adduser $USER sudo && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + echo "$USER added to sudoers" +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 +fi + +## Next one for timezone setup +if [ "$TZ" != "Etc/UTC" ] + then + 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 +fi diff --git a/03_setup_root_path.sh b/03_setup_root_path.sh new file mode 100644 index 0000000..e282d68 --- /dev/null +++ b/03_setup_root_path.sh @@ -0,0 +1,11 @@ +#!/usr/bin/with-contenv bash + +echo "root path: $WWW_ROOT_PATH" + +if [[ ! -z $WWW_ROOT_PATH ]] +then + echo "Set www-root-path to $WWW_ROOT_PATH" + echo "www-root-path=$WWW_ROOT_PATH" >> /etc/rstudio/rserver.conf +else + echo "Not setting www-root-path" +fi diff --git a/04_copy_env.sh b/04_copy_env.sh new file mode 100644 index 0000000..7a378e6 --- /dev/null +++ b/04_copy_env.sh @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv bash + +printenv >> /etc/R/Renviron.site + +printenv > /home/$USER/.Renviron diff --git a/05_setup_rsession_parameters.sh b/05_setup_rsession_parameters.sh new file mode 100644 index 0000000..ca1dc27 --- /dev/null +++ b/05_setup_rsession_parameters.sh @@ -0,0 +1,13 @@ +#!/usr/bin/with-contenv bash + +echo "session-timeout-minutes=7200" >> /etc/rstudio/rsession.conf +echo "session-disconnected-timeout-minutes=1440" >> /etc/rstudio/rsession.conf +echo "session-quit-child-processes-on-exit=0" >> /etc/rstudio/rsession.conf +#echo "session-default-working-dir=~" >> /etc/rstudio/rsession.conf +#echo "session-default-new-project-dir=~" >> /etc/rstudio/rsession.conf +#echo "session-save-action-default=yes" >> /etc/rstudio/rsession.conf +echo "allow-shell=0" >> /etc/rstudio/rsession.conf +#echo "allow-terminal-websockets=1" >> /etc/rstudio/rsession.conf +echo "limit-cpu-time-minutes=0" >> /etc/rstudio/rsession.conf +echo "limit-file-upload-size-mb=0" >> /etc/rstudio/rsession.conf +#echo "limit-xfs-disk-quota=no" >> /etc/rstudio/rsession.conf diff --git a/06-fuse-logback.xml b/06-fuse-logback.xml new file mode 100644 index 0000000..dd761e2 --- /dev/null +++ b/06-fuse-logback.xml @@ -0,0 +1,24 @@ + + + + /var/log/workspace-lib/fuse-workspace.log + true + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{0}: %msg%n + + + + ./var/log/workspace-lib/fuse-workspace.log%d{yyyy-MM-dd}.log + 30 + 10MB + 100MB + + + + + + + + + + diff --git a/06_workspace_mount.sh b/06_workspace_mount.sh new file mode 100644 index 0000000..8e8ef46 --- /dev/null +++ b/06_workspace_mount.sh @@ -0,0 +1,54 @@ +#!/usr/bin/with-contenv bash + +# Functions that decode a JWT token +_decode_base64_url() { + local len=$((${#1} % 4)) + local result="$1" + if [ $len -eq 2 ]; then result="$1"'==' + elif [ $len -eq 3 ]; then result="$1"'=' + fi + echo "$result" | tr '_-' '/+' | base64 -d +} + +# $1 => JWT to decode +# $2 => either 1 for header or 2 for body (default is 2) +decode_jwt() { _decode_base64_url $(echo -n $1 | cut -d "." -f ${2:-2}) | jq .; } + +_workspace_scope="/d4science.research-infrastructures.eu" +echo "Manage the user's workspace" +workspace_dir='/workspace' +workspace_logdir='/var/log/workspace-lib' +workspace_libdir='/opt/workspace-lib' +workspace_fuse_jar="$workspace_libdir/fuse-workspace.jar" + +[[ ! -d "$workspace_dir" ]] || [[ -z `ls -A "$workspace_dir"` ]] || mv $workspace_dir ${workspace_dir}.old + +mkdir -p $workspace_dir +chown ${USER}:${USER} $workspace_dir +chown -R ${USER}:${USER} $workspace_logdir +chmod 444 $workspace_fuse_jar + +_retval= +if [ -d /home/${USER}/workspace ]; then + rmdir /home/${USER}/workspace + _retval=$? + if [ $_retval -ne 0 ]; then + echo "The user has a directory named 'workspace' inside their home directory and it is not empty." + echo "Not starting the workspace mount" + exit $_retval + fi +fi + +echo "Mount the workspace" +su - "$USER" -c "/usr/bin/java -cp .:${workspace_dir}:${workspace_logdir}/ -Dlogback.configurationFile=${workspace_logdir}/logback.xml -jar $workspace_fuse_jar $SHINYPROXY_OIDC_ACCESS_TOKEN ${_workspace_scope} $workspace_dir" >/dev/null 2>&1 & + +_retval= +_fuse_process=$(ps auwwx | grep fuse | grep java) +_retval=$? +if [ $_retval -ne 0 ]; then + echo "The mount of the workspace failed" + exit 1 +fi + +ln -sf $workspace_dir /home/${USER}/workspace +exit 0 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fbecb86 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +FROM d4science/r-studio-requirement + +LABEL org.d4science.image.licenses="EUPL-1.2" \ + org.d4science.image.source="https://code-repo.d4science.org/gCubeSystem/r-studio-requirement" \ + org.d4science.image.vendor="D4Science " \ + org.d4science.image.authors="Andrea Dell'Amico , Roberto Cirillo " \ + org.d4science.image.r_version="2022.02.1+461" + +ENV S6_VERSION=v2.1.0.2 +ENV RSTUDIO_VERSION=2022.02.1+461 +ENV DEFAULT_USER=rstudio +ENV PANDOC_VERSION=default +ENV PATH=/usr/lib/rstudio-server/bin:$PATH + +RUN /rocker_scripts/install_rstudio.sh + +# This part comes from https://github.com/openanalytics/shinyproxy-rstudio-ide-demo/blob/master/Dockerfile +RUN echo "www-frame-origin=same" >> /etc/rstudio/disable_auth_rserver.conf +RUN echo "www-verify-user-agent=0" >> /etc/rstudio/disable_auth_rserver.conf + +ADD 01_set_env.sh /etc/cont-init.d/01_set_env +ADD 02_userconf.sh /etc/cont-init.d/02_userconf +ADD 03_setup_root_path.sh /etc/cont-init.d/03_setup_root_path + +# By default RStudio does not give access to all enviornment variables defined in the container (e.g. using ShinyProxy). +# Uncomment the next line, to change this behavior. +#ADD 04_copy_env.sh /etc/cont-init.d/04_copy_env +ADD 05_setup_rsession_parameters.sh /etc/cont-init.d/05_setup_rsession_parameters + +# Prepare the workspace environment +RUN mkdir -p /opt/workspace-lib +RUN mkdir /var/log/workspace-lib +ADD https://maven.d4science.org/nexus/content/repositories/gcube-snapshots/org/gcube/data-access/sh-fuse-integration/2.0.0-SNAPSHOT/sh-fuse-integration-2.0.0-20211005.090627-1-jar-with-dependencies.jar /opt/workspace-lib/fuse-workspace.jar +ADD 06_workspace_mount.sh /etc/cont-init.d/06_workspace_mount +ADD 06-fuse-logback.xml /opt/workspace-lib/logback.xml + +EXPOSE 8787 + +CMD ["/init"]