config: prefer core.compression=5
[girocco.git] / jailsetup.sh
blob0c1a72656d400aa77cce156f53bd944168759188
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 who's 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" -a -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" -o "$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.compression 5
181 update_config_item http.lowSpeedLimit 1
182 update_config_item http.lowSpeedTime 600
183 update_config_item receive.maxInputSize "${cfg_max_receive_size:-0}" 1
184 [ -z "$didchmod" ] || chmod a-w etc/girocco
186 mkdir -p etc/sshkeys etc/sshcerts etc/sshactive
187 for ruser in $reserved_users; do
188 touch etc/sshkeys/$ruser
189 done
190 chgrp $cfg_owning_group etc etc/sshkeys etc/sshcerts etc/sshactive ||
191 echo "WARNING: Cannot chgrp $cfg_owning_group the etc directories"
192 chgrp $cfg_owning_group etc/passwd ||
193 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/passwd"
194 chgrp $cfg_owning_group etc/group ||
195 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/group"
196 chgrp $cfg_owning_group etc/girocco etc/girocco/.gitconfig ||
197 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/girocco"
198 chmod g+s etc etc/sshkeys etc/sshcerts etc/sshactive ||
199 echo "WARNING: Cannot chmod g+s the etc directories"
200 chmod g+w etc etc/sshkeys etc/sshcerts etc/sshactive ||
201 echo "WARNING: Cannot chmod g+w the etc directories"
202 chmod g+w etc/passwd etc/group ||
203 echo "WARNING: Cannot chmod g+w the etc/passwd and/or etc/group files"
204 chmod go-w etc/passwd etc/girocco etc/girocco/.gitconfig ||
205 echo "WARNING: Cannot chmod go-w etc/girocco and/or etc/girocco/.gitconfig"
206 chmod a-w etc/girocco ||
207 echo "WARNING: Cannot chmod a-w etc/girocco"
208 chmod -R g+w etc/sshkeys etc/sshcerts etc/sshactive 2>/dev/null ||
209 echo "WARNING: Cannot chmod g+w the sshkeys, sshcerts and/or sshactive files"
211 # Note time of last install
212 > etc/sshactive/_install
214 [ -z "$dbonly" ] || exit 0
216 # Make sure the system type is supported for chroot
217 sysname="$(uname -s | tr A-Z a-z || :)"
218 : ${sysname:=linux}
219 nosshdir=
220 # These equivalents may need to be expanded at some point
221 case "$sysname" in
222 *kfreebsd*)
223 sysname=linux;;
224 *darwin*)
225 sysname=darwin;;
226 *dragonfly*)
227 sysname=dragonfly;;
228 *freebsd*)
229 sysname=freebsd;;
230 *linux*)
231 sysname=linux;;
232 esac
234 chrootsetup="$curdir/chrootsetup_$sysname.sh"
235 if ! [ -r "$chrootsetup" -a -s "$chrootsetup" ]; then
236 echo "*** Error: $chrootsetup not found" >&2
237 echo "*** Error: creating a chroot for a `uname -s` system is not supported" >&2
238 exit 1
241 # validate reporoot, chroot, jailreporoot and sshd_bin before doing anything more
243 # validates the passed in dir if a second argument is not empty dir must NOT
244 # start with / otherwise it must. A trailing '/' is removed and any duplicated
245 # // are removed and a sole / or empty is disallowed.
246 make_valid_dir() {
247 _check="$(echo "$1" | tr -s /)"
248 _check="${_check%/}"
249 [ -z "$_check" -o "$_check" = "/" ] && return 1
250 if [ -z "$2" ]; then
251 # must start with '/'
252 case "$_check" in /*) :;; *) return 1; esac
253 else
254 # must NOT start with '/'
255 case "$_check" in /*) return 1; esac
257 echo "$_check"
260 if ! reporoot="$(make_valid_dir "$cfg_reporoot")"; then
261 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
262 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
263 exit 1
265 if ! chroot="$(make_valid_dir "$cfg_chroot")"; then
266 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
267 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
268 exit 1
270 if ! jailreporoot="$(make_valid_dir "$cfg_jailreporoot" 1)"; then
271 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
272 echo "*** Error: MUST NOT start with '/' and MUST NOT be ''" >&2
273 exit 1
276 # chroot MUST NOT be reporoot
277 if [ "$chroot" = "$reporoot" ]; then
278 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
279 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
280 echo "*** Error: reporoot and chroot MUST NOT be the same" >&2
281 exit 1
284 # chroot MUST NOT be a subdirectory of reporoot
285 case "$chroot" in "$reporoot"/*)
286 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
287 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
288 echo "*** Error: chroot MUST NOT be a subdirectory of reporoot" >&2
289 exit 1
290 esac
292 # chroot/jailreporoot MUST NOT be a subdirectory of reporoot
293 case "$chroot/$jailreporoot" in "$reporoot"/*)
294 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
295 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
296 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
297 echo "*** Error: chroot/jailreporoot MUST NOT be a subdirectory of reporoot" >&2
298 exit 1
299 esac
301 # reporoot MUST NOT be a subdirectory of chroot/jailreporoot
302 case "$reporoot" in "$chroot/$jailreporoot"/*)
303 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
304 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
305 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
306 echo "*** Error: reporoot MUST NOT be a subdirectory of chroot/jailreporoot" >&2
307 exit 1
308 esac
310 # sshd_bin MUST be undef (or empty) or a full absolute path
311 sshd_bin_bad=
312 case "$cfg_sshd_bin" in *"/../"*) sshd_bin_bad=1;; ""|/?*) :;; *) sshd_bin_bad=1;; esac
313 [ -z "$sshd_bin_bad" ] || {
314 echo "*** Error: invalid Config::sshd_bin $cfg_sshd_bin" >&2
315 echo "*** Error: if set, sshd_bin must be an absolute path" >&2
316 exit 1
318 sshd_bin="$cfg_sshd_bin"
319 [ -n "$sshd_bin" ] || sshd_bin="$(find_std_utility "sshd")" || {
320 echo "*** Error: Config::sshd_bin is not set and no sshd could be found" >&2
321 echo "*** Error: please set Config::sshd_bin to an absolute path to sshd" >&2
322 exit 1
324 [ -x "$sshd_bin" ] && [ -r "$sshd_bin" ] && [ -f "$sshd_bin" ] || {
325 echo "*** Error: the selected sshd ('$sshd_bin') was not found, not readable or not executable" >&2
326 exit 1
329 # Set the user and group on the top of the chroot before creating anything else
330 chown 0:0 "$chroot"
332 # When we create a fork, the alternates always have an absolute path.
333 # If reporoot is not --bind mounted at the same location in chroot we must
334 # create a suitable symlink so the absolute path alternates continue to work
335 # in the ssh chroot or else forks will be broken in there.
336 if [ "$reporoot" != "/$jailreporoot" ]; then
337 mkdirp="$(dirname "${reporoot#/}")"
338 [ "$mkdirp" = "." ] && mkdirp=
339 lnback=
340 [ -z "$mkdirp" ] || lnback="$(echo "$mkdirp/" | sed -e 's,[^/]*/,../,g')"
341 [ -z "$mkdirp" ] || mkdir -p "$chroot/$mkdirp"
342 (umask 0; ln -s -f -n "$lnback$jailreporoot" "$chroot$reporoot")
343 [ $? -eq 0 ] || exit 1
346 # First, setup basic platform-independent directory structure
347 mkdir -p bin dev etc lib sbin var/empty var/run "$jailreporoot"
348 chmod 0555 var/empty
349 rm -rf usr local
350 ln -s . usr
351 ln -s . local
353 # Now source the platform-specific script that is responsible for dev device
354 # setup, proc setup (if needed), lib64 setup (if needed) and basic library
355 # installation to make a chroot operational. Additionally it will define a
356 # pull_in_bin function that can be used to add executables and their library
357 # dependencies to the chroot and finally will install a suitable nc.openbsd
358 # compatible version of netcat that supports connections to unix sockets.
359 . "$chrootsetup"
361 # Now, bring in sshd, sh etc.
362 # The $chrootsetup script should have already provided a suitable nc.openbsd
363 install -p "$cfg_basedir/bin/git-shell-verify" bin/git-shell-verify.new
364 install -p "$cfg_basedir/bin/git-askpass-password" bin/git-askpass-password.new
365 perl -i -p \
366 -e 's|^#!.*|#!/bin/sh| if $. == 1;' \
367 -e 'close ARGV if eof;' \
368 bin/git-shell-verify.new bin/git-askpass-password.new
369 mv -f bin/git-askpass-password.new bin/git-askpass-password
370 mv -f bin/git-shell-verify.new bin/git-shell-verify
371 pull_in_bin "$cfg_basedir/bin/can_user_push" bin
372 pull_in_bin "$cfg_basedir/bin/list_packs" bin
373 pull_in_bin "$var_sh_bin" bin/sh
374 # be paranoid since these are going into the chroot and make sure
375 # that we get the "standard" versions of them (they are all standard "POSIX"
376 # utilities) not some wayward version picked up by a haphazard PATH
377 pull_in_bin "$(find_std_utility chmod )" bin
378 pull_in_bin "$(find_std_utility date )" bin
379 pull_in_bin "$(find_std_utility find )" bin
380 pull_in_bin "$(find_std_utility mkdir )" bin
381 pull_in_bin "$(find_std_utility mv )" bin
382 pull_in_bin "$(find_std_utility rm )" bin
383 pull_in_bin "$(find_std_utility sleep )" bin
384 pull_in_bin "$(find_std_utility touch )" bin
385 pull_in_bin "$(find_std_utility tr )" bin
386 pull_in_bin "$(find_std_utility wc )" bin
387 pull_in_bin "$(find_std_utility xargs )" bin
388 # this one's already been validated and might be in a non-standard location
389 pull_in_bin "$sshd_bin" sbin
391 # ...and the bits of git we need,
392 # being sure to use the configured git and its --exec-path to find the pieces
393 for i in git git-index-pack git-receive-pack git-shell git-update-server-info \
394 git-upload-archive git-upload-pack git-unpack-objects git-config \
395 git-for-each-ref git-rev-list git-rev-parse git-symbolic-ref; do
396 pull_in_bin "$var_git_exec_path/$i" bin git
397 done
399 # ...and any extras identified by install.sh
400 # these are also all standard "POSIX" utilities
401 # ones that a decent sh implementation would have built-in already...
402 if [ -n "$GIROCCO_CHROOT_EXTRA_INSTALLS" ]; then
403 for i in $GIROCCO_CHROOT_EXTRA_INSTALLS; do
404 pull_in_bin "$(find_std_utility "$(basename "$i")")" bin
405 done
408 # Note time of last jailsetup
409 > etc/sshactive/_jailsetup
411 # Update permissions on the database files
412 chown $cfg_cgi_user:$cfg_owning_group etc/passwd etc/group
413 chown -R $cfg_cgi_user:$cfg_owning_group etc/sshkeys etc/sshcerts etc/sshactive
414 chown $cfg_mirror_user:$cfg_owning_group etc etc/girocco etc/girocco/.gitconfig
416 # Set up basic sshd configuration:
417 if [ -n "$nosshdir" ]; then
418 rm -rf etc/ssh
419 ln -s . etc/ssh
420 [ ! -f /etc/moduli ] || { cp -p /etc/moduli etc/; chown 0:0 etc/moduli; }
421 else
422 [ ! -e etc/ssh -o -d etc/ssh ] || rm -rf etc/ssh
423 mkdir -p etc/ssh
424 [ ! -f /etc/ssh/moduli ] || { cp -p /etc/ssh/moduli etc/ssh/; chown 0:0 etc/ssh/moduli; }
426 mkdir -p var/run/sshd
427 if [ ! -s etc/ssh/sshd_config ]; then
428 cat >etc/ssh/sshd_config <<EOT
429 Protocol 2
430 Port $cfg_sshd_jail_port
431 UsePAM no
432 X11Forwarding no
433 AllowAgentForwarding no
434 AllowTcpForwarding no
435 PermitTunnel no
436 IgnoreUserKnownHosts yes
437 PrintLastLog no
438 PrintMotd no
439 UseDNS no
440 PermitRootLogin no
441 UsePrivilegeSeparation yes
443 HostKey /etc/ssh/ssh_host_rsa_key
445 if [ -z "$cfg_disable_dsa" ]; then
446 cat >>etc/ssh/sshd_config <<EOT
447 HostKey /etc/ssh/ssh_host_dsa_key
450 cat >>etc/ssh/sshd_config <<EOT
451 AuthorizedKeysFile /etc/sshkeys/%u
452 StrictModes no
454 # mob and git users:
455 PermitEmptyPasswords yes
456 ChallengeResponseAuthentication no
457 PasswordAuthentication yes
460 if [ ! -s etc/ssh/ssh_host_rsa_key ]; then
461 bits=2048
462 if [ "$cfg_rsakeylength" -gt "$bits" ] 2>/dev/null; then
463 bits="$cfg_rsakeylength"
465 yes | ssh-keygen -b "$bits" -t rsa -N "" -C Girocco -f etc/ssh/ssh_host_rsa_key
467 if [ -z "$cfg_disable_dsa" -a ! -s etc/ssh/ssh_host_dsa_key ]; then
468 # ssh-keygen can only create 1024 bit DSA keys
469 yes | ssh-keygen -b 1024 -t dsa -N "" -C Girocco -f etc/ssh/ssh_host_dsa_key
472 # Set the final permissions on the binaries and perform any final twiddling
473 chroot_update_permissions
475 # Change the owner of the sshd-related files
476 chown 0:0 etc/ssh/ssh_* etc/ssh/sshd_*
478 echo "--- Add to your boot scripts: mount --bind $reporoot $chroot/$jailreporoot"
479 echo "--- Add to your boot scripts: mount --bind /proc $chroot/proc"
480 echo "--- Add to your syslog configuration: listening on socket $chroot/dev/log"
481 echo "--- To restart a running jail's sshd: sudo kill -HUP \`cat $chroot/var/run/sshd.pid\`"