3
3
# Distributed under the terms of the Modified BSD License.
4
4
5
5
set -e
6
+ echo " Running: start.sh" " $@ "
6
7
7
8
# Exec the specified command or fall back on bash
8
9
if [ $# -eq 0 ]; then
@@ -11,140 +12,186 @@ else
11
12
cmd=( " $@ " )
12
13
fi
13
14
15
+ # The run-hooks function looks for .sh scripts to source and executable files to
16
+ # run within a passed directory.
14
17
run-hooks () {
15
- # Source scripts or run executable files in a directory
16
18
if [[ ! -d " ${1} " ]] ; then
17
19
return
18
20
fi
19
- echo " ${0} : running hooks in ${1} "
21
+ echo " ${0} : running hooks in ${1} as uid / gid: $( id -u ) / $( id -g ) "
20
22
for f in " ${1} /" * ; do
21
23
case " ${f} " in
22
24
* .sh)
23
- echo " ${0} : running ${f} "
25
+ echo " ${0} : running script ${f} "
24
26
# shellcheck disable=SC1090
25
27
source " ${f} "
26
28
;;
27
29
* )
28
30
if [[ -x " ${f} " ]] ; then
29
- echo " ${0} : running ${f} "
31
+ echo " ${0} : running executable ${f} "
30
32
" ${f} "
31
33
else
32
- echo " ${0} : ignoring ${f} "
34
+ echo " ${0} : ignoring non-executable ${f} "
33
35
fi
34
36
;;
35
37
esac
36
38
done
37
39
echo " ${0} : done running hooks in ${1} "
38
40
}
39
41
42
+
43
+ # NOTE: This hook will run as the user the container was started with!
40
44
run-hooks /usr/local/bin/start-notebook.d
41
45
42
- # Handle special flags if we're root
46
+ # If the container started as the root user, then we have permission to refit
47
+ # the jovyan user, and ensure file permissions, grant sudo rights, and such
48
+ # things before we run the command passed to start.sh as the desired user
49
+ # (NB_USER).
50
+ #
43
51
if [ " $( id -u) " == 0 ] ; then
52
+ # Environment variables:
53
+ # - NB_USER: the desired username and associated home folder
54
+ # - NB_UID: the desired user id
55
+ # - NB_GID: a group id we want our user to belong to
56
+ # - NB_GROUP: the groupname we want for the group
57
+ # - GRANT_SUDO: a boolean ("1" or "yes") to grant the user sudo rights
58
+ # - CHOWN_HOME: a boolean ("1" or "yes") to chown the user's home folder
59
+ # - CHOWN_EXTRA: a comma separated list of paths to chown
60
+ # - CHOWN_HOME_OPTS / CHOWN_EXTRA_OPTS: arguments to the chown commands
44
61
45
- # Only attempt to change the jovyan username if it exists
62
+ # Refit the jovyan user to the desired the user (NB_USER)
46
63
if id jovyan & > /dev/null ; then
47
- echo " Set username to: ${NB_USER} "
48
- usermod -d " /home/${NB_USER} " -l " ${NB_USER} " jovyan
64
+ if ! usermod --home " /home/${NB_USER} " --login " ${NB_USER} " jovyan 2>&1 | grep " no changes" > /dev/null; then
65
+ echo " Updated the jovyan user:"
66
+ echo " - username: jovyan -> ${NB_USER} "
67
+ echo " - home dir: /home/jovyan -> /home/${NB_USER} "
68
+ fi
69
+ elif ! id -u " ${NB_USER} " & > /dev/null; then
70
+ echo " ERROR: Neither the jovyan user or '${NB_USER} ' exists."
71
+ echo " This could be the result of stopping and starting, the"
72
+ echo " container with a different NB_USER environment variable."
73
+ exit 1
74
+ fi
75
+ # Ensure the desired user (NB_USER) gets its desired user id (NB_UID) and is
76
+ # a member of the desired group (NB_GROUP, NB_GID)
77
+ if [ " ${NB_UID} " != " $( id -u " ${NB_USER} " ) " ] || [ " ${NB_GID} " != " $( id -g " ${NB_USER} " ) " ]; then
78
+ echo " Update ${NB_USER} 's UID:GID to ${NB_UID} :${NB_GID} "
79
+ # Ensure the desired group's existence
80
+ if [ " ${NB_GID} " != " $( id -g " ${NB_USER} " ) " ]; then
81
+ groupadd --force --gid " ${NB_GID} " --non-unique " ${NB_GROUP:- ${NB_USER} } "
82
+ fi
83
+ # Recreate the desired user as we want it
84
+ userdel " ${NB_USER} "
85
+ useradd --home " /home/${NB_USER} " --uid " ${NB_UID} " --gid " ${NB_GID} " --groups 100 --no-log-init " ${NB_USER} "
49
86
fi
50
87
51
- # handle home and working directory if the username changed
88
+ # Move or symlink the jovyan home directory to the desired users home
89
+ # directory if it doesn't already exist, and update the current working
90
+ # directory to the new location if needed.
52
91
if [[ " ${NB_USER} " != " jovyan" ]]; then
53
- # changing username, make sure homedir exists
54
- # (it could be mounted, and we shouldn't create it if it already exists)
55
92
if [[ ! -e " /home/${NB_USER} " ]]; then
56
- echo " Copying home dir to /home/${NB_USER} "
93
+ echo " Attempting to copy /home/jovyan to /home/${NB_USER} ... "
57
94
mkdir " /home/${NB_USER} "
58
- cp -a /home/jovyan/. " /home/${NB_USER} /" || ln -s /home/jovyan " /home/${NB_USER} "
95
+ if cp -a /home/jovyan/. " /home/${NB_USER} /" ; then
96
+ echo " Success!"
97
+ else
98
+ echo " Failed!"
99
+ echo " Attempting to symlink /home/jovyan to /home/${NB_USER} ..."
100
+ if ln -s /home/jovyan " /home/${NB_USER} " ; then
101
+ echo " Success!"
102
+ else
103
+ echo " Failed!"
104
+ fi
105
+ fi
59
106
fi
60
- # if workdir is in /home/jovyan, cd to /home/${NB_USER}
107
+ # Ensure the current working directory is updated to the new path
61
108
if [[ " ${PWD} /" == " /home/jovyan/" * ]]; then
62
- newcwd =" /home/${NB_USER} /${PWD: 13} "
63
- echo " Setting CWD to ${newcwd } "
64
- cd " ${newcwd } "
109
+ new_wd =" /home/${NB_USER} /${PWD: 13} "
110
+ echo " Changing working directory to ${new_wd } "
111
+ cd " ${new_wd } "
65
112
fi
66
113
fi
67
114
68
- # Handle case where provisioned storage does not have the correct permissions by default
69
- # Ex: default NFS/EFS (no auto-uid/gid)
70
- if [[ " ${CHOWN_HOME} " == " 1" || " ${CHOWN_HOME} " == ' yes' ]]; then
71
- echo " Changing ownership of /home/${NB_USER} to ${NB_UID} :${NB_GID} with options ' ${CHOWN_HOME_OPTS} ' "
115
+ # Optionally ensure the desired user get filesystem ownership of it's home
116
+ # folder and/or additional folders
117
+ if [[ " ${CHOWN_HOME} " == " 1" || " ${CHOWN_HOME} " == " yes" ]]; then
118
+ echo " Ensuring /home/${NB_USER} is owned by ${NB_UID} :${NB_GID} ${CHOWN_HOME_OPTS : +(chown options: ${CHOWN_HOME_OPTS} )} "
72
119
# shellcheck disable=SC2086
73
120
chown ${CHOWN_HOME_OPTS} " ${NB_UID} :${NB_GID} " " /home/${NB_USER} "
74
121
fi
75
122
if [ -n " ${CHOWN_EXTRA} " ]; then
76
123
for extra_dir in $( echo " ${CHOWN_EXTRA} " | tr ' ,' ' ' ) ; do
77
- echo " Changing ownership of ${extra_dir} to ${NB_UID} :${NB_GID} with options ' ${CHOWN_EXTRA_OPTS} ' "
124
+ echo " Ensuring ${extra_dir} is owned by ${NB_UID} :${NB_GID} ${CHOWN_HOME_OPTS : +(chown options: ${CHOWN_HOME_OPTS} )} "
78
125
# shellcheck disable=SC2086
79
126
chown ${CHOWN_EXTRA_OPTS} " ${NB_UID} :${NB_GID} " " ${extra_dir} "
80
127
done
81
128
fi
82
129
83
- # Change UID:GID of NB_USER to NB_UID:NB_GID if it does not match
84
- if [ " ${NB_UID} " != " $( id -u " ${NB_USER} " ) " ] || [ " ${NB_GID} " != " $( id -g " ${NB_USER} " ) " ]; then
85
- echo " Set user ${NB_USER} UID:GID to: ${NB_UID} :${NB_GID} "
86
- if [ " ${NB_GID} " != " $( id -g " ${NB_USER} " ) " ]; then
87
- groupadd -f -g " ${NB_GID} " -o " ${NB_GROUP:- ${NB_USER} } "
88
- fi
89
- userdel " ${NB_USER} "
90
- useradd --home " /home/${NB_USER} " -u " ${NB_UID} " -g " ${NB_GID} " -G 100 -l " ${NB_USER} "
91
- fi
92
-
93
- # Enable sudo if requested
94
- if [[ " ${GRANT_SUDO} " == " 1" || " ${GRANT_SUDO} " == ' yes' ]]; then
95
- echo " Granting ${NB_USER} sudo access and appending ${CONDA_DIR} /bin to sudo PATH"
96
- echo " ${NB_USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/notebook
97
- fi
130
+ # Update potentially outdated environment variables since image build
131
+ export XDG_CACHE_HOME=" /home/${NB_USER} /.cache"
98
132
99
133
# Add ${CONDA_DIR}/bin to sudo secure_path
100
134
sed -r " s#Defaults\s+secure_path\s*=\s*\" ?([^\" ]+)\" ?#Defaults secure_path=\" \1:${CONDA_DIR} /bin\" #" /etc/sudoers | grep secure_path > /etc/sudoers.d/path
101
135
102
- # Exec the command as NB_USER with the PATH and the rest of
103
- # the environment preserved
136
+ # Optionally grant passwordless sudo rights for the desired user
137
+ if [[ " $GRANT_SUDO " == " 1" || " $GRANT_SUDO " == " yes" ]]; then
138
+ echo " Granting ${NB_USER} passwordless sudo rights!"
139
+ echo " ${NB_USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/added-by-start-script
140
+ fi
141
+
142
+ # NOTE: This hook is run as the root user!
104
143
run-hooks /usr/local/bin/before-notebook.d
105
- echo " Executing the command:" " ${cmd[@]} "
106
- exec sudo -E -H -u " ${NB_USER} " PATH=" ${PATH} " XDG_CACHE_HOME=" /home/${NB_USER} /.cache" PYTHONPATH=" ${PYTHONPATH:- } " " ${cmd[@]} "
144
+
145
+ echo " Running as ${NB_USER} :" " ${cmd[@]} "
146
+ exec sudo --preserve-env --set-home --user " ${NB_USER} " \
147
+ PATH=" ${PATH} " XDG_CACHE_HOME=" /home/${NB_USER} /.cache" \
148
+ PYTHONPATH=" ${PYTHONPATH:- } " \
149
+ " ${cmd[@]} "
150
+
151
+ # The container didn't start as the root user, so we will have to act as the
152
+ # user we started as.
107
153
else
108
- if [[ " ${NB_UID} " == " $( id -u jovyan 2> /dev/null) " && " ${NB_GID} " == " $( id -g jovyan 2> /dev/null) " ]]; then
109
- # User is not attempting to override user/group via environment
110
- # variables, but they could still have overridden the uid/gid that
111
- # container runs as. Check that the user has an entry in the passwd
112
- # file and if not add an entry.
113
- STATUS=0 && whoami & > /dev/null || STATUS=$? && true
114
- if [[ " ${STATUS} " != " 0" ]]; then
115
- if [[ -w /etc/passwd ]]; then
116
- echo " Adding passwd file entry for $( id -u) "
117
- sed -e " s/^jovyan:/nayvoj:/" /etc/passwd > /tmp/passwd
118
- echo " jovyan:x:$( id -u) :$( id -g) :,,,:/home/jovyan:/bin/bash" >> /tmp/passwd
119
- cat /tmp/passwd > /etc/passwd
120
- rm /tmp/passwd
121
- else
122
- echo ' Container must be run with group "root" to update passwd file'
123
- fi
124
- fi
154
+ # Warn about misconfiguration of: desired username, user id, or group id
155
+ if [[ -n " ${NB_USER} " && " ${NB_USER} " != " $( id -un) " ]]; then
156
+ echo " WARNING: container must be started as root to change the desired user's name with NB_USER!"
157
+ fi
158
+ if [[ -n " ${NB_UID} " && " ${NB_UID} " != " $( id -u) " ]]; then
159
+ echo " WARNING: container must be started as root to change the desired user's id with NB_UID!"
160
+ fi
161
+ if [[ -n " ${NB_GID} " && " ${NB_GID} " != " $( id -g) " ]]; then
162
+ echo " WARNING: container must be started as root to change the desired user's group id with NB_GID!"
163
+ fi
125
164
126
- # Warn if the user isn't going to be able to write files to ${HOME}.
127
- if [[ ! -w /home/jovyan ]]; then
128
- echo ' Container must be run with group "users" to update files'
129
- fi
130
- else
131
- # Warn if looks like user want to override uid/gid but hasn't
132
- # run the container as root.
133
- if [[ -n " ${NB_UID} " && " ${NB_UID} " != " $( id -u) " ]]; then
134
- echo " Container must be run as root to set NB_UID to ${NB_UID} "
135
- fi
136
- if [[ -n " ${NB_GID} " && " ${NB_GID} " != " $( id -g) " ]]; then
137
- echo " Container must be run as root to set NB_GID to ${NB_GID} "
165
+ # Warn about misconfiguration of: granting sudo rights
166
+ if [[ " ${GRANT_SUDO} " == " 1" || " ${GRANT_SUDO} " == " yes" ]]; then
167
+ echo " WARNING: container must be started as root to grant sudo permissions!"
168
+ fi
169
+
170
+ # Attempt to ensure the user uid we currently run as has a named entry in
171
+ # the /etc/passwd file, as it avoids software crashing on hard assumptions
172
+ # on such entry. Writing to the /etc/passwd was allowed for the root group
173
+ # from the Dockerfile during build.
174
+ #
175
+ # ref: https://github.com/jupyter/docker-stacks/issues/552
176
+ if ! whoami & > /dev/null; then
177
+ echo " There is no entry in /etc/passwd for our UID. Attempting to fix..."
178
+ if [[ -w /etc/passwd ]]; then
179
+ echo " Renaming old jovyan user to nayvoj ($( id -u jovyan) :$( id -g jovyan) )"
180
+ sed --in-place " s/^jovyan:/nayvoj:/" /etc/passwd
181
+
182
+ echo " jovyan:x:$( id -u) :$( id -g) :,,,:/home/jovyan:/bin/bash" >> /etc/passwd
183
+ echo " Added new jovyan user ($( id -u) :$( id -g) ). Fixed UID!"
184
+ else
185
+ echo " WARNING: unable to fix missing /etc/passwd entry because we don't have write permission."
138
186
fi
139
187
fi
140
188
141
- # Warn if looks like user want to run in sudo mode but hasn't run
142
- # the container as root.
143
- if [[ " ${GRANT_SUDO} " == " 1" || " ${GRANT_SUDO} " == ' yes' ]]; then
144
- echo ' Container must be run as root to grant sudo permissions'
189
+ # Warn if the user isn't able to write files to ${HOME}
190
+ if [[ ! -w /home/jovyan ]]; then
191
+ echo " WARNING: no write access to /home/jovyan. Try starting the container with group 'users' (100)."
145
192
fi
146
193
147
- # Execute the command
194
+ # NOTE: This hook is run as the user we started the container as!
148
195
run-hooks /usr/local/bin/before-notebook.d
149
196
echo " Executing the command:" " ${cmd[@]} "
150
197
exec " ${cmd[@]} "
0 commit comments