gc.sh: move rename_pack() definition to earlier location
[girocco.git] / jailsetup.sh
blob39aafcc3c8f11584f4126e0f31203bbbcee00c81
1 #!/bin/sh
2 # The Girocco jail setup script
4 # If the first parameter is "dbonly", setup the database only
6 # We are designed to set up the chroot based on the output of
7 # `uname -s` by sourcing a suitable system-specific script.
8 # Unrecognized systems will generate an error. When using
9 # "dbonly" the setup of the chroot binaries is skipped so the
10 # output of `uname -s` does not matter in that case.
12 set -e
14 curdir="$(pwd)"
15 srcdir="$curdir/src"
16 getent="$srcdir/getent"
17 . ./shlib.sh
19 # find_std_utility should always come up with the full path to the standard
20 # version of the utility whose name is passed as "$1"
21 getconf="/usr/bin/getconf"
22 [ -x "$getconf" ] || getconf="/bin/getconf"
23 [ -x "$getconf" ] || getconf="getconf"
24 stdpath="$("unset" -f command; "command" "$getconf" "PATH" 2>/dev/null)" || :
25 ":" "${stdpath:=/bin:/usr/bin}"
26 stdpath="$stdpath:/sbin:/usr/sbin"
27 find_std_utility() (
28 "unset" -f unalias command "$1" >/dev/null 2>&1 || :
29 "unalias" -a >/dev/null 2>&1 || :
30 PATH="$stdpath" && "export" PATH || :
31 "command" -v "$1"
32 ) 2>/dev/null
34 dbonly=
35 [ "$1" != "dbonly" ] || dbonly=1
37 reserved_users="root sshd _sshd mob git lock bundle nobody everyone $cfg_cgi_user $cfg_mirror_user"
39 # Require either sshd or _sshd user unless "dbonly"
40 sshd_user=sshd
41 if ! "$getent" passwd sshd >/dev/null && ! "$getent" passwd _sshd >/dev/null; then
42 if [ -n "$dbonly" ]; then
43 if ! [ -s etc/passwd ]; then
44 # Only complain on initial etc/passwd creation
45 echo "WARNING: no sshd or _sshd user, omitting entries from chroot etc/passwd"
47 sshd_user=
48 else
49 echo "*** Error: You do not have required sshd or _sshd user in system." >&2
50 exit 1
52 else
53 "$getent" passwd sshd >/dev/null || sshd_user=_sshd
56 # Verify we have all we need
57 if ! "$getent" passwd "$cfg_mirror_user" >/dev/null; then
58 echo "*** Error: You do not have \"$cfg_mirror_user\" user in system yet." >&2
59 exit 1
61 if ! "$getent" passwd "$cfg_cgi_user" >/dev/null; then
62 echo "*** Error: You do not have \"$cfg_cgi_user\" user in system yet." >&2
63 exit 1
65 if [ -n "$dbonly" ] && [ -z "$cfg_owning_group" ]; then
66 cfg_owning_group="$("$getent" passwd "$cfg_mirror_user" | cut -d : -f 4)"
67 elif ! "$getent" group "$cfg_owning_group" >/dev/null; then
68 echo "*** Error: You do not have \"$cfg_owning_group\" group in system yet." >&2
69 exit 1
72 # One last paranoid check before we go writing all over everything
73 if [ -z "$cfg_chroot" ] || [ "$cfg_chroot" = "/" ]; then
74 echo "*** Error: chroot location is not set or is invalid." >&2
75 echo "*** Error: perhaps you have an incorrect Config.pm?" >&2
76 exit 1
79 umask 022
80 mkdir -p "$cfg_chroot"
81 cd "$cfg_chroot"
82 chmod 755 "$cfg_chroot" ||
83 echo "WARNING: Cannot chmod $cfg_chroot"
85 mkdir -p var/empty
86 chmod 0555 var/empty ||
87 echo "WARNING: Cannot chmod a=rx $cfg_chroot/var/empty"
89 # Set up basic user/group configuration; if there isn't any already
90 mobpass=
91 [ -n "$cfg_mob" ] || mobpass='x'
92 mkdir -p etc
93 if ! [ -s etc/passwd ]; then
94 cat >etc/passwd <<EOT
95 root:x:0:0:system administrator:/var/empty:/bin/false
96 nobody:x:$("$getent" passwd nobody | cut -d : -f 3-4):unprivileged user:/var/empty:/bin/false
97 EOT
98 [ -z "$sshd_user" ] || cat >>etc/passwd <<EOT
99 sshd:x:$("$getent" passwd $sshd_user | cut -d : -f 3-4):privilege separation:/var/empty:/bin/false
100 _sshd:x:$("$getent" passwd $sshd_user | cut -d : -f 3-4):privilege separation:/var/empty:/bin/false
102 [ "$cfg_cgi_user" = "$cfg_mirror_user" ] || cat >>etc/passwd <<EOT
103 $cfg_cgi_user:x:$("$getent" passwd "$cfg_cgi_user" | cut -d : -f 3-5):/:/bin/true
105 cat >>etc/passwd <<EOT
106 $cfg_mirror_user:x:$("$getent" passwd "$cfg_mirror_user" | cut -d : -f 3-5):/:/bin/true
107 everyone:x:65537:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):every user:/:/bin/false
108 mob:$mobpass:65538:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):the mob:/:/bin/git-shell-verify
109 git::65539:$("$getent" passwd nobody | cut -d : -f 4):read-only access:/:/bin/git-shell-verify
111 elif [ -z "$dbonly" ]; then
112 # Make sure an sshd entry is present
113 if ! grep -q '^sshd:' etc/passwd; then
114 echo "*** Error: chroot etc/passwd exists but lacks sshd entry." >&2
115 exit 1
119 if ! [ -s etc/group ]; then
120 cat >etc/group <<EOT
121 _repo:x:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):$cfg_mirror_user
125 # Set up basic default Git configuration
126 # Initialize one if none exists or update critical variables for an existing one
127 mkdir -p etc/girocco
128 didchmod=
129 if [ -e etc/girocco/.gitconfig ] && ! [ -f etc/girocco/.gitconfig ]; then
130 echo "*** Error: chroot etc/girocco/.gitconfig exists but is not a file." >&2
131 exit 1
133 if [ -f etc/girocco/.gitconfig ]; then
134 gcerr=0
135 x="$(git config --file etc/girocco/.gitconfig --get "no--such--section.no such subsection.no--such--key")" || gcerr=$?
136 if [ $gcerr -gt 1 ]; then
137 echo "*** Error: chroot etc/girocco/.gitconfig exists but is corrupt." >&2
138 echo "*** Error: either remove it or edit it to correct the problem." >&2
139 exit 1
142 if ! [ -s etc/girocco/.gitconfig ]; then
143 chmod u+w etc/girocco
144 didchmod=1
145 cat >etc/girocco/.gitconfig <<EOT
146 # Any values set here will take effect whenever Girocco runs a git command
150 # $1 => name, $2 => value, $3 => overwrite_flag
151 update_config_item() {
152 _existsnot=
153 _oldval=
154 _oldval="$(git config --file etc/girocco/.gitconfig --get "$1")" || _existsnot=1
155 if [ -z "$_existsnot" ]; then
156 [ -n "$3" ] || return 0
157 [ "$_oldval" != "$2" ] || return 0
159 [ -n "$didchmod" ] || { chmod u+w etc/girocco; didchmod=1; }
160 git config --file etc/girocco/.gitconfig "$1" "$2"
161 if [ -n "$_existsnot" ]; then
162 echo "chroot: etc/girocco/.gitconfig: config $1: (created) \"$2\""
163 else
164 echo "chroot: etc/girocco/.gitconfig: config $1: \"$_oldval\" -> \"$2\""
167 if [ -n "$cfg_git_no_mmap" ]; then
168 update_config_item core.packedGitWindowSize 1m 1
169 else
170 update_config_item core.packedGitWindowSize 32m 1
172 if [ -n "$var_window_memory" ]; then
173 update_config_item pack.windowMemory "$var_window_memory" 1
175 if [ -n "$cfg_jgit_compatible_bitmaps" ]; then
176 update_config_item pack.writeBitmapHashCache false 1
177 else
178 update_config_item pack.writeBitmapHashCache true 1
180 update_config_item core.pager "cat" 1
181 update_config_item core.compression 5
182 update_config_item transfer.unpackLimit 1 1
183 update_config_item http.lowSpeedLimit 1
184 update_config_item http.lowSpeedTime 600
185 update_config_item receive.advertisePushOptions false 1
186 update_config_item receive.maxInputSize "${cfg_max_receive_size:-0}" 1
187 [ -z "$didchmod" ] || chmod a-w etc/girocco
189 mkdir -p etc/sshkeys etc/sshcerts etc/sshactive
190 for ruser in $reserved_users; do
191 touch etc/sshkeys/$ruser
192 done
193 chgrp $cfg_owning_group etc etc/sshkeys etc/sshcerts etc/sshactive ||
194 echo "WARNING: Cannot chgrp $cfg_owning_group the etc directories"
195 chgrp $cfg_owning_group etc/passwd ||
196 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/passwd"
197 chgrp $cfg_owning_group etc/group ||
198 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/group"
199 chgrp $cfg_owning_group etc/girocco etc/girocco/.gitconfig ||
200 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/girocco"
201 chmod g+s etc etc/sshkeys etc/sshcerts etc/sshactive ||
202 echo "WARNING: Cannot chmod g+s the etc directories"
203 chmod g+w etc etc/sshkeys etc/sshcerts etc/sshactive ||
204 echo "WARNING: Cannot chmod g+w the etc directories"
205 chmod g+w etc/passwd etc/group ||
206 echo "WARNING: Cannot chmod g+w the etc/passwd and/or etc/group files"
207 chmod go-w etc/passwd etc/girocco etc/girocco/.gitconfig ||
208 echo "WARNING: Cannot chmod go-w etc/girocco and/or etc/girocco/.gitconfig"
209 chmod a-w etc/girocco ||
210 echo "WARNING: Cannot chmod a-w etc/girocco"
211 chmod -R g+w etc/sshkeys etc/sshcerts etc/sshactive 2>/dev/null ||
212 echo "WARNING: Cannot chmod g+w the sshkeys, sshcerts and/or sshactive files"
214 # Note time of last install
215 >etc/sshactive/_install
217 [ -z "$dbonly" ] || exit 0
219 # Make sure the system type is supported for chroot
220 sysname="$(uname -s | tr A-Z a-z)" || :
221 : ${sysname:=linux}
222 nosshdir=
223 # These equivalents may need to be expanded at some point
224 case "$sysname" in
225 *kfreebsd*)
226 sysname=linux;;
227 *darwin*)
228 sysname=darwin;;
229 *dragonfly*)
230 sysname=dragonfly;;
231 *freebsd*)
232 sysname=freebsd;;
233 *linux*)
234 sysname=linux;;
235 esac
237 chrootsetup="$curdir/chrootsetup_$sysname.sh"
238 if ! [ -f "$chrootsetup" ] || ! [ -r "$chrootsetup" ] || ! [ -s "$chrootsetup" ]; then
239 echo "*** Error: $chrootsetup not found" >&2
240 echo "*** Error: creating a chroot for a $(uname -s) system is not supported" >&2
241 exit 1
244 # validate reporoot, chroot, jailreporoot and sshd_bin before doing anything more
246 # validates the passed in dir if a second argument is not empty dir must NOT
247 # start with / otherwise it must. A trailing '/' is removed and any duplicated
248 # // are removed and a sole / or empty is disallowed.
249 make_valid_dir() {
250 _check="$(echo "$1" | tr -s /)"
251 _check="${_check%/}"
252 [ -n "$_check" ] && [ "$_check" != "/" ] || return 1
253 if [ -z "$2" ]; then
254 # must start with '/'
255 case "$_check" in /*) :;; *) return 1; esac
256 else
257 # must NOT start with '/'
258 case "$_check" in /*) return 1; esac
260 echo "$_check"
263 if ! reporoot="$(make_valid_dir "$cfg_reporoot")"; then
264 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
265 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
266 exit 1
268 if ! chroot="$(make_valid_dir "$cfg_chroot")"; then
269 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
270 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
271 exit 1
273 if ! jailreporoot="$(make_valid_dir "$cfg_jailreporoot" 1)"; then
274 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
275 echo "*** Error: MUST NOT start with '/' and MUST NOT be ''" >&2
276 exit 1
279 # chroot MUST NOT be reporoot
280 if [ "$chroot" = "$reporoot" ]; then
281 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
282 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
283 echo "*** Error: reporoot and chroot MUST NOT be the same" >&2
284 exit 1
287 # chroot MUST NOT be a subdirectory of reporoot
288 case "$chroot" in "$reporoot"/*)
289 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
290 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
291 echo "*** Error: chroot MUST NOT be a subdirectory of reporoot" >&2
292 exit 1
293 esac
295 # chroot/jailreporoot MUST NOT be a subdirectory of reporoot
296 case "$chroot/$jailreporoot" in "$reporoot"/*)
297 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
298 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
299 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
300 echo "*** Error: chroot/jailreporoot MUST NOT be a subdirectory of reporoot" >&2
301 exit 1
302 esac
304 # reporoot MUST NOT be a subdirectory of chroot/jailreporoot
305 case "$reporoot" in "$chroot/$jailreporoot"/*)
306 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
307 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
308 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
309 echo "*** Error: reporoot MUST NOT be a subdirectory of chroot/jailreporoot" >&2
310 exit 1
311 esac
313 # sshd_bin MUST be undef (or empty) or a full absolute path
314 sshd_bin_bad=
315 case "$cfg_sshd_bin" in *"/../"*) sshd_bin_bad=1;; ""|/?*) :;; *) sshd_bin_bad=1;; esac
316 [ -z "$sshd_bin_bad" ] || {
317 echo "*** Error: invalid Config::sshd_bin $cfg_sshd_bin" >&2
318 echo "*** Error: if set, sshd_bin must be an absolute path" >&2
319 exit 1
321 sshd_bin="$cfg_sshd_bin"
322 [ -n "$sshd_bin" ] || sshd_bin="$(find_std_utility "sshd")" || {
323 echo "*** Error: Config::sshd_bin is not set and no sshd could be found" >&2
324 echo "*** Error: please set Config::sshd_bin to an absolute path to sshd" >&2
325 exit 1
327 [ -x "$sshd_bin" ] && [ -r "$sshd_bin" ] && [ -f "$sshd_bin" ] || {
328 echo "*** Error: the selected sshd ('$sshd_bin') was not found, not readable or not executable" >&2
329 exit 1
332 # Set the user and group on the top of the chroot before creating anything else
333 chown 0:0 "$chroot"
335 # When we create a fork, the alternates always have an absolute path.
336 # If reporoot is not --bind mounted at the same location in chroot we must
337 # create a suitable symlink so the absolute path alternates continue to work
338 # in the ssh chroot or else forks will be broken in there.
339 if [ "$reporoot" != "/$jailreporoot" ]; then
340 mkdirp="$(dirname "${reporoot#/}")"
341 [ "$mkdirp" = "." ] && mkdirp=
342 lnback=
343 [ -z "$mkdirp" ] || lnback="$(echo "$mkdirp/" | sed -e 's,[^/]*/,../,g')"
344 [ -z "$mkdirp" ] || mkdir -p "$chroot/$mkdirp"
345 (umask 0; ln -s -f -n "$lnback$jailreporoot" "$chroot$reporoot")
346 [ $? -eq 0 ] || exit 1
349 # First, setup basic platform-independent directory structure
350 mkdir -p bin dev etc lib sbin var/empty var/run "$jailreporoot"
351 chmod 0555 var/empty
352 rm -rf usr local
353 ln -s . usr
354 ln -s . local
356 # Now source the platform-specific script that is responsible for dev device
357 # setup, proc setup (if needed), lib64 setup (if needed) and basic library
358 # installation to make a chroot operational. Additionally it will define a
359 # pull_in_bin function that can be used to add executables and their library
360 # dependencies to the chroot and finally will install a suitable nc.openbsd
361 # compatible version of netcat that supports connections to unix sockets.
362 . "$chrootsetup"
364 # Now, bring in sshd, sh etc.
365 # The $chrootsetup script should have already provided a suitable nc.openbsd
366 install -p "$cfg_basedir/bin/git-shell-verify" bin/git-shell-verify.new
367 install -p "$cfg_basedir/bin/git-askpass-password" bin/git-askpass-password.new
368 perl -i -p \
369 -e 's|^#!.*|#!/bin/sh| if $. == 1;' \
370 -e 'close ARGV if eof;' \
371 bin/git-shell-verify.new bin/git-askpass-password.new
372 mv -f bin/git-askpass-password.new bin/git-askpass-password
373 mv -f bin/git-shell-verify.new bin/git-shell-verify
374 pull_in_bin "$cfg_basedir/bin/can_user_push" bin
375 pull_in_bin "$cfg_basedir/bin/list_packs" bin
376 pull_in_bin "$cfg_basedir/bin/strftime" bin
377 pull_in_bin "$var_sh_bin" bin/sh
378 # be paranoid since these are going into the chroot and make sure
379 # that we get the "standard" versions of them (they are all standard "POSIX"
380 # utilities) not some wayward version picked up by a haphazard PATH
381 pull_in_bin "$(find_std_utility cat )" bin
382 pull_in_bin "$(find_std_utility chmod )" bin
383 pull_in_bin "$(find_std_utility date )" bin
384 pull_in_bin "$(find_std_utility find )" bin
385 pull_in_bin "$(find_std_utility mkdir )" bin
386 pull_in_bin "$(find_std_utility mv )" bin
387 pull_in_bin "$(find_std_utility rm )" bin
388 pull_in_bin "$(find_std_utility sleep )" bin
389 pull_in_bin "$(find_std_utility sort )" bin
390 pull_in_bin "$(find_std_utility touch )" bin
391 pull_in_bin "$(find_std_utility tr )" bin
392 pull_in_bin "$(find_std_utility wc )" bin
393 # this one's already been validated and might be in a non-standard location
394 pull_in_bin "$sshd_bin" sbin
396 # ...and the bits of git we need,
397 # being sure to use the configured git and its --exec-path to find the pieces
398 for i in git git-index-pack git-receive-pack git-shell git-update-server-info \
399 git-upload-archive git-upload-pack git-unpack-objects git-config \
400 git-for-each-ref git-rev-list git-rev-parse git-symbolic-ref; do
401 pull_in_bin "$var_git_exec_path/$i" bin git
402 done
404 # ...and any extras identified by install.sh
405 # these are also all standard "POSIX" utilities
406 # ones that a decent sh implementation would have built-in already...
407 if [ -n "$GIROCCO_CHROOT_EXTRA_INSTALLS" ]; then
408 for i in $GIROCCO_CHROOT_EXTRA_INSTALLS; do
409 pull_in_bin "$(find_std_utility "$(basename "$i")")" bin
410 done
413 # Note time of last jailsetup
414 >etc/sshactive/_jailsetup
416 # Update permissions on the database files
417 chown $cfg_cgi_user:$cfg_owning_group etc/passwd etc/group
418 chown -R $cfg_cgi_user:$cfg_owning_group etc/sshkeys etc/sshcerts etc/sshactive
419 chown $cfg_mirror_user:$cfg_owning_group etc etc/girocco etc/girocco/.gitconfig
421 # Set up basic sshd configuration:
422 if [ -n "$nosshdir" ]; then
423 rm -rf etc/ssh
424 ln -s . etc/ssh
425 ! [ -f /etc/moduli ] || { cp -p /etc/moduli etc/; chown 0:0 etc/moduli; }
426 else
427 ! [ -e etc/ssh ] || [ -d etc/ssh ] || rm -rf etc/ssh
428 mkdir -p etc/ssh
429 ! [ -f /etc/ssh/moduli ] || { cp -p /etc/ssh/moduli etc/ssh/; chown 0:0 etc/ssh/moduli; }
431 mkdir -p var/run/sshd
432 if ! [ -s etc/ssh/sshd_config ]; then
433 cat >etc/ssh/sshd_config <<EOT
434 Protocol 2
435 Port $cfg_sshd_jail_port
436 UsePAM no
437 X11Forwarding no
438 AllowAgentForwarding no
439 AllowTcpForwarding no
440 PermitTunnel no
441 IgnoreUserKnownHosts yes
442 PrintLastLog no
443 PrintMotd no
444 UseDNS no
445 PermitRootLogin no
446 UsePrivilegeSeparation yes
448 HostKey /etc/ssh/ssh_host_rsa_key
450 if [ -z "$cfg_disable_dsa" ]; then
451 cat >>etc/ssh/sshd_config <<EOT
452 HostKey /etc/ssh/ssh_host_dsa_key
455 cat >>etc/ssh/sshd_config <<EOT
456 AuthorizedKeysFile /etc/sshkeys/%u
457 StrictModes no
459 # mob and git users:
460 PermitEmptyPasswords yes
461 ChallengeResponseAuthentication no
462 PasswordAuthentication yes
465 if ! [ -s etc/ssh/ssh_host_rsa_key ]; then
466 bits=2048
467 if [ "$cfg_rsakeylength" -gt "$bits" ] 2>/dev/null; then
468 bits="$cfg_rsakeylength"
470 yes | ssh-keygen -b "$bits" -t rsa -N "" -C Girocco -f etc/ssh/ssh_host_rsa_key
472 if [ -z "$cfg_disable_dsa" ] && ! [ -s etc/ssh/ssh_host_dsa_key ]; then
473 # ssh-keygen can only create 1024 bit DSA keys
474 yes | ssh-keygen -b 1024 -t dsa -N "" -C Girocco -f etc/ssh/ssh_host_dsa_key
477 # Set the final permissions on the binaries and perform any final twiddling
478 chroot_update_permissions
480 # Change the owner of the sshd-related files
481 chown 0:0 etc/ssh/ssh_* etc/ssh/sshd_*
483 echo "--- Add to your boot scripts: mount --bind $reporoot $chroot/$jailreporoot"
484 echo "--- Add to your boot scripts: mount --bind /proc $chroot/proc"
485 echo "--- Add to your syslog configuration: listening on socket $chroot/dev/log"
486 echo "--- To restart a running jail's sshd: sudo kill -HUP \$(cat $chroot/var/run/sshd.pid)"