-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnauci_base_init.sh
More file actions
executable file
·390 lines (324 loc) · 13.5 KB
/
Copy pathnauci_base_init.sh
File metadata and controls
executable file
·390 lines (324 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
#!/bin/bash
#############
# Constants #
#############
readonly USER_GROUP_NAME_REGEX="^[a-z_][a-z0-9_-]*[$]?$"
readonly USER_GROUP_ID_REGEX="^[[:digit:]]+$"
readonly TRUE=0
#######################
# Usage Documentation #
#######################
######################################################################
read -r -d '' USAGE << EOF
Usage: nauci_base_init -hs [-n username[,...]] [-G groupname[,...]]
[-d groupname] [-D groupid] [-u groupname] [-U groupid] [-v
volume] [ command [...] ]
EOF
read -r -d '' HELP_DOCUMENTATION << EOF
$USAGE
Author: trevor.wilson@nauci.org
Depends on: POSIX ACL and extended attributes. It may work on other
ACL types depending on how ACL interoperability is handled, but I have
not tested that behaviour.
The nauci_base_init.sh is the entry point for the nauci_base_entry
docker image and is intended to facilitate the initial setup of a
docker container for the purposes of software development. Users may
be added to a list of existing groups with the -G option. The -G
option will not remove existing users from unlisted groups. Every user
will be added to a developer group which can be set with the -d
option. If the host is setup with a developer group that has a GID
matching the container's developer group GID then the setgid mode bit
can be used to grant both host and container user privileges to
attached volumes without the need of extending the all encompassing
docker privileges. Each developer will also be added to a USB
group. The idea is similar to the developer group. The USB group will
grant access to attached USB devices without the need of extending
docker privileges. For this to work the GID of the USB group must
match the GID set on the USB bus device nodes of the host.
Some optional parameters have default values. Defaults are enclosed in
brackets as the leading text of the parameters definition.
-n: a comma separated list of user names. A new user will be
created for each username in the list. Each newly created
user will be given a home directory of the same name in
/home. If a user already exists with that name, then that
user will not be created anew, but may be modified in
adherence with supplied optional parameters.
-G: a comma separated list of existing groups that the users
will be added to.
-d: [developer] the name of the developer group.
-D: [2000] the GID of the developer group.
-u: [usb] the name of the usb group.
-U: [85] the GID of the usb group.
-v: [/shared] a directory passed to docker-run as a volume with
posixacl support enabled. For each user passed to the -n
option a directory with the same name as that user will be
created as a child of /shared. Default ACL rules will be
set giving the developer group rwx permissions on the named
directories. A soft link from the user's dev directory will
be created to their named directory under /shared.
-s: switch to an interactive shell instead of exiting.
-h: displays this help document.
The first none-optional parameter will be evaluated as a shell
command.
EOF
######################################################################
############################
# Interactive Shell Prompt #
############################
read -r -d '' INTERACTIVE_SHELL_WELCOME_MSG << EOF
##########################################################################
# Welcome. You have entered an interactive shell. This is a good time to #
# set user passwords. When you are finished, run the exit command to #
# continue with the init script. Any trailing commands that you entered #
# will then execute. #
##########################################################################
EOF
####################
# Input Parameters #
####################
defaultDeveloperVolumeName="/shared" developerVolumeName=
defaultDeveloperGroupName="developer" developerGroupName=
defaultDeveloperGid=2000 developerGid=
defaultUsbGroupName="usb" usbGroupName=
defaultUsbGid=85 usbGid=
doSwitchToInteractiveShell=1
declare -a userNames;
declare -a groupNames;
declare -g -a nonExistentGroupNames;
#############
# Functions #
#############
print_usage_with_error_message() {
printf "\n%s: %s\n%s\n" "$0" "$1" "$USAGE" >&2
printf "\nTo display the complete help document please run %s -h\n" "$0" >&2
}
print_usage(){
printf "\n%s\n\n" "$HELP_DOCUMENTATION"
}
validate_id() {
return $(test -n "$(echo "$1" | grep -E $USER_GROUP_ID_REGEX)")
}
validate_name() {
return $(test -n "$(echo "$1" | grep -E "$USER_GROUP_NAME_REGEX")")
}
does_group_exist() {
return $(test -n "$(getent group $1)")
}
is_group_gid() {
return $(test "$(getent group $1 | cut -d: -f3)" = "$2")
}
validate_supplementary_group_names() {
nonExistentGroupNames=();
for groupName in $@
do
if ! does_group_exist "$groupName"
then
nonExistentGroupNames+=("$groupName")
isValid=1
fi
done
return ${#nonExistentGroupNames[@]}
}
##############################
# Optional Parameter Parsing #
##############################
while getopts :hn:u:U:g:d:D:v:s opt
do
case $opt in
n) readarray -td, userNames <<< "$OPTARG,"; unset userNames[-1];
for userName in ${userNames[@]}
do
if ! validate_name "$userName"
then
print_usage_with_error_message "$userName is not a valid username."
exit 1
fi
done
;;
g) readarray -td, groupNames <<< "$OPTARG,"; unset groupNames[-1];
if ! validate_supplementary_group_names "${groupNames[@]}"
then
declare -p nonExistentGroupNames
print_usage_with_error_message "The following group names were not found in the group database: ${nonExistentGroupNames[*]}."
exit 1
fi
;;
d) if validate_name "$OPTARG"
then
developerGroupName=$OPTARG
else
print_usage_with_error_message "Invalid Group Name: $OPTARG\nGroup names must begin with a lower case letter or underscore, optionally followed by more letters, underscores, and numbers"
exit 1
fi
;;
D) if validate_id "$OPTARG"
then
developerGid="$OPTARG"
else
print_usage
exit 1
fi
;;
v) if [ -d "$OPTARG" ]
then
developerVolumeName=$OPTARG
else
print_usage_with_error_message "Invalid Developer Volume: $OPTARG\nThe developer volume does not exist."
exit 1
fi
;;
u) if validate_name "$OPTARG"
then
usbGroupName="$OPTARG"
else
print_usage_with_error_message "Invalid Group Name: $OPTARG\nGroup names must begin with a lower case letter or underscore, optionally followed by more letters, underscores, and numbers"
exit 1
fi
;;
U) if validate_id "$OPTARG"
then
usbGid=$OPTARG
else
print_usage
exit 1
fi
;;
h) print_usage
;;
s) doSwitchToInteractiveShell=0
;;
'?') print_usage_with_error_message "invalid option -$OPTARG"
exit 1
;;
esac
done
shift $((OPTIND - 1)) #this leaves any remaining arguments, but removes the options we have processed
##################
# Apply defaults #
##################
developerVolumeName=${developerVolumeName:-$defaultDeveloperVolumeName}
developerGroupName=${developerGroupName:-$defaultDeveloperGroupName}
developerGid=${developerGid:-$defaultDeveloperGid}
usbGroupName=${usbGroupName:-$defaultUsbGroupName}
usbGid=${usbGid:-$defaultUsbGid}
#################################
# Process all of the parameters #
#################################
######################################################################
# If the developer volume is the default value then validate that it #
# exists #
######################################################################
if [ "$defaultDeveloperVolumeName" = "$developerVolumeName" ] && ! [ -d "$developerVolumeName" ]
then
print_usage_with_error_message "Invalid Developer Volume: $developerVolumeName\nThe developer volume does not exist. Since you have not specified a developer volume with the -v option the default developer option was used. It is required that you run the image with a volume attached to the developer volume path."
exit 1
fi
################################################
# Create developer group if it does not exist. #
################################################
if ! does_group_exist "$developerGroupName"
then
if does_group_exist "$developerGid"
then
print_usage_with_error_message "The GID, $developerGid, chosen for the new developer group named, $developerGroupName, is already in use."
exit 1
fi
groupadd -g "$developerGid" "$developerGroupName"
groupNames+=("$developerGroupName")
elif ! $(is_group_gid "$developerGroupName" "$developerGid")
then
print_usage_with_error_message "The GID of the existing developer group named $developerGroupName does not match the provided GID $developerGid"
exit 1
fi
#############################################
# Create the usb group if it does not exist #
#############################################
if ! does_group_exist "$usbGroupName"
then
if does_group_exist "$usbGid"
then
print_usage_with_error_message "The GID, $usbGid, chosen for the new usb group named, $usbGroupName, is already in use."
exit 1
fi
groupadd -g "$usbGid" "$usbGroupName"
groupNames+=("$usbGroupName")
elif ! $(is_group_gid "$usbGroupName" "$usbGid")
then
print_usage_with_error_message "The GID of the existing usb group named $usbGroupName does not match the provided GID $usbGid"
exit 1
fi
#######################################################################################################
# Do not bind forwarding server to the loopback address, but instead bind it to the wild card address #
#######################################################################################################
sed 's/#[[:space:]]*X11UseLocalhost yes/X11UseLocalhost no/' /etc/ssh/sshd_config > /etc/ssh/sshd_config.bk && mv /etc/ssh/sshd_config.bk /etc/ssh/sshd_config
##############################
# Create or update each user #
##############################
for userName in ${userNames[@]}
do
# if user does not already exist then create the user, otherwise modify the user based on parameters
if [ -n "$(getent passwd $userName)" ]
then
( IFS=,; usermod ${userName} -s /bin/bash -aG "${groupNames[*]}" )
else
( IFS=,; useradd ${userName} -s /bin/bash -mG "${groupNames[*]}" )
fi
# create the dev directory with setgid and develoepr group ownership
realUserDevDirectory="${developerVolumeName}/${userName}/dev"
if ! [ -d "${realUserDevDirectory}" ] || ! [ -g "${realUserDevDirectory}" ]
then
mkdir -p ${realUserDevDirectory}
chown :${developerGroupName} ${realUserDevDirectory}
chmod 2770 ${realUserDevDirectory}
fi
# if the default effective ACL permissions have not been set for
# the developer group, then let's set them now as a convenience
# since it will be needed. The ACL can always be altered later
# from a running container.
if [ -z "$(getfacl -cdep ${realUserDevDirectory} | grep -i ${developerGroupName}:rw.*effective)" ]
then
# this will fail if your host / volume is not configured correctly for ACLs
# As an example zfs *xattr property* must be set to *sa* and the *acltype* must be set to *posixacl* on the host volume
setfacl -Rdm g:${developerGroupName}:rwx ${realUserDevDirectory}
fi
# link the dev directory to the user's home directory
userHome="$(getent passwd ${userName} | cut -d: -f6)"
if ! [ -h "${userHome}/dev" ]
then
ln -sn ${realUserDevDirectory} ${userHome}/dev
chown -h ${userName}:${developerGroupName} ${userHome}/dev
fi
# ssh
mkdir -p ${userHome}/.ssh/
sed 's/#[[:space:]]*ForwardX11 no/ForwardX11 yes/' /etc/ssh/ssh_config | sed 's/#[[:space:]]*ForwardX11Trusted yes/ForwardX11Trusted yes/' > $userHome/.ssh/config
chown -R ${userName}:${userName} ${userHome}/.ssh/
done
####################
# Start ssh daemon #
####################
if ! service ssh status > /dev/null
then
service ssh start
fi
#############################################
# Optionally switch to an interactive shell #
#############################################
if [ "${doSwitchToInteractiveShell}" = "0" ]
then
standard_input_fd=0 #file descriptor for standard input
if [[ -t "$standard_input_fd" || -p /dev/stdin ]]
then
printf "\n${INTERACTIVE_SHELL_WELCOME_MSG}\n\n"
eval "$BASH"
else
printf "\nWARNING: This is not an interactive shell. Try using docker -it optional flags. Ignoring -s flag.\n" >&2
fi
fi
#################################################################################
# run trailing commands passed in after all other arguments have been processed #
#################################################################################
if [ "${*}" ]
then
eval "${*}"
fi
exit 0