gitweb: pick up updates
[girocco.git] / jailsetup.sh
blob37391f799c45a89327452c18c026578d238e1567
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 dbonly=''
20 [ "$1" != "dbonly" ] || dbonly=1
22 reserved_users="root sshd _sshd mob git lock bundle nobody everyone $cfg_cgi_user $cfg_mirror_user"
24 # Require either sshd or _sshd user unless "dbonly"
25 sshd_user=sshd
26 if ! "$getent" passwd sshd >/dev/null && ! "$getent" passwd _sshd >/dev/null; then
27 if [ -n "$dbonly" ]; then
28 if [ ! -s etc/passwd ]; then
29 # Only complain on initial etc/passwd creation
30 echo "WARNING: no sshd or _sshd user, omitting entries from chroot etc/passwd"
32 sshd_user=
33 else
34 echo "*** Error: You do not have required sshd or _sshd user in system." >&2
35 exit 1
37 else
38 "$getent" passwd sshd >/dev/null || sshd_user=_sshd
41 # Verify we have all we need
42 if ! "$getent" passwd "$cfg_mirror_user" >/dev/null; then
43 echo "*** Error: You do not have \"$cfg_mirror_user\" user in system yet." >&2
44 exit 1
46 if ! "$getent" passwd "$cfg_cgi_user" >/dev/null; then
47 echo "*** Error: You do not have \"$cfg_cgi_user\" user in system yet." >&2
48 exit 1
50 if [ -n "$dbonly" -a -z "$cfg_owning_group" ]; then
51 cfg_owning_group="$("$getent" passwd "$cfg_mirror_user" | cut -d : -f 4)"
52 elif ! "$getent" group "$cfg_owning_group" >/dev/null; then
53 echo "*** Error: You do not have \"$cfg_owning_group\" group in system yet." >&2
54 exit 1
57 # One last paranoid check before we go writing all over everything
58 if [ -z "$cfg_chroot" -o "$cfg_chroot" = "/" ]; then
59 echo "*** Error: chroot location is not set or is invalid." >&2
60 echo "*** Error: perhaps you have an incorrect Config.pm?" >&2
61 exit 1
64 umask 022
65 mkdir -p "$cfg_chroot"
66 cd "$cfg_chroot"
67 chmod 755 "$cfg_chroot" ||
68 echo "WARNING: Cannot chmod $cfg_chroot"
70 mkdir -p var/empty
71 chmod 0555 var/empty ||
72 echo "WARNING: Cannot chmod a=rx $cfg_chroot/var/empty"
74 # Set up basic user/group configuration; if there isn't any already
75 mobpass=''
76 [ -n "$cfg_mob" ] || mobpass='x'
77 mkdir -p etc
78 if [ ! -s etc/passwd ]; then
79 cat >etc/passwd <<EOT
80 root:x:0:0:system administrator:/var/empty:/bin/false
81 nobody:x:$("$getent" passwd nobody | cut -d : -f 3-4):unprivileged user:/var/empty:/bin/false
82 EOT
83 [ -z "$sshd_user" ] || cat >>etc/passwd <<EOT
84 sshd:x:$("$getent" passwd $sshd_user | cut -d : -f 3-4):privilege separation:/var/empty:/bin/false
85 _sshd:x:$("$getent" passwd $sshd_user | cut -d : -f 3-4):privilege separation:/var/empty:/bin/false
86 EOT
87 [ "$cfg_cgi_user" = "$cfg_mirror_user" ] || cat >>etc/passwd <<EOT
88 $cfg_cgi_user:x:$("$getent" passwd "$cfg_cgi_user" | cut -d : -f 3-5):/:/bin/true
89 EOT
90 cat >>etc/passwd <<EOT
91 $cfg_mirror_user:x:$("$getent" passwd "$cfg_mirror_user" | cut -d : -f 3-5):/:/bin/true
92 everyone:x:65537:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):every user:/:/bin/false
93 mob:$mobpass:65538:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):the mob:/:/bin/git-shell-verify
94 git::65539:$("$getent" passwd nobody | cut -d : -f 4):read-only access:/:/bin/git-shell-verify
95 EOT
96 elif [ -z "$dbonly" ]; then
97 # Make sure an sshd entry is present
98 if ! grep -q '^sshd:' etc/passwd; then
99 echo "*** Error: chroot etc/passwd exists but lacks sshd entry." >&2
100 exit 1
104 if [ ! -s etc/group ]; then
105 cat >etc/group <<EOT
106 _repo:x:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):$cfg_mirror_user
110 # Set up basic default Git configuration
111 # Initialize one if none exists or update critical variables for an existing one
112 mkdir -p etc/girocco
113 didchmod=
114 if [ -e etc/girocco/.gitconfig ] && ! [ -f etc/girocco/.gitconfig ]; then
115 echo "*** Error: chroot etc/girocco/.gitconfig exists but is not a file." >&2
116 exit 1
118 if [ -f etc/girocco/.gitconfig ]; then
119 gcerr=0
120 x="$(git config --file etc/girocco/.gitconfig --get "no--such--section.no such subsection.no--such--key")" || gcerr=$?
121 if [ $gcerr -gt 1 ]; then
122 echo "*** Error: chroot etc/girocco/.gitconfig exists but is corrupt." >&2
123 echo "*** Error: either remove it or edit it to correct the problem." >&2
124 exit 1
127 if [ ! -s etc/girocco/.gitconfig ]; then
128 chmod u+w etc/girocco
129 didchmod=1
130 cat >etc/girocco/.gitconfig <<EOT
131 # Any values set here will take effect whenever Girocco runs a git command
135 # $1 => name, $2 => value, $3 => overwrite_flag
136 update_config_item() {
137 _existsnot=
138 _oldval=
139 _oldval="$(git config --file etc/girocco/.gitconfig --get "$1")" || _existsnot=1
140 if [ -z "$_existsnot" ]; then
141 [ -n "$3" ] || return 0
142 [ "$_oldval" != "$2" ] || return 0
144 [ -n "$didchmod" ] || { chmod u+w etc/girocco; didchmod=1; }
145 git config --file etc/girocco/.gitconfig "$1" "$2"
146 if [ -n "$_existsnot" ]; then
147 echo "chroot: etc/girocco/.gitconfig: config $1: (created) \"$2\""
148 else
149 echo "chroot: etc/girocco/.gitconfig: config $1: \"$_oldval\" -> \"$2\""
152 if [ -n "$cfg_git_no_mmap" ]; then
153 update_config_item core.packedGitWindowSize 1m 1
154 else
155 update_config_item core.packedGitWindowSize 32m 1
157 if [ -n "$var_window_memory" ]; then
158 update_config_item pack.windowMemory "$var_window_memory" 1
160 if [ -n "$cfg_jgit_compatible_bitmaps" ]; then
161 update_config_item pack.writeBitmapHashCache false 1
162 else
163 update_config_item pack.writeBitmapHashCache true 1
165 update_config_item http.lowSpeedLimit 1
166 update_config_item http.lowSpeedTime 600
167 [ -z "$didchmod" ] || chmod a-w etc/girocco
169 mkdir -p etc/sshkeys etc/sshcerts etc/sshactive
170 for ruser in $reserved_users; do
171 touch etc/sshkeys/$ruser
172 done
173 chgrp $cfg_owning_group etc etc/sshkeys etc/sshcerts etc/sshactive ||
174 echo "WARNING: Cannot chgrp $cfg_owning_group the etc directories"
175 chgrp $cfg_owning_group etc/passwd ||
176 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/passwd"
177 chgrp $cfg_owning_group etc/group ||
178 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/group"
179 chgrp $cfg_owning_group etc/girocco etc/girocco/.gitconfig ||
180 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/girocco"
181 chmod g+s etc etc/sshkeys etc/sshcerts etc/sshactive ||
182 echo "WARNING: Cannot chmod g+s the etc directories"
183 chmod g+w etc etc/sshkeys etc/sshcerts etc/sshactive ||
184 echo "WARNING: Cannot chmod g+w the etc directories"
185 chmod g+w etc/passwd etc/group ||
186 echo "WARNING: Cannot chmod g+w the etc/passwd and/or etc/group files"
187 chmod go-w etc/passwd etc/girocco etc/girocco/.gitconfig ||
188 echo "WARNING: Cannot chmod go-w etc/girocco and/or etc/girocco/.gitconfig"
189 chmod a-w etc/girocco ||
190 echo "WARNING: Cannot chmod a-w etc/girocco"
191 chmod -R g+w etc/sshkeys etc/sshcerts etc/sshactive 2>/dev/null ||
192 echo "WARNING: Cannot chmod g+w the sshkeys, sshcerts and/or sshactive files"
194 # Note time of last install
195 > etc/sshactive/_install
197 [ -z "$dbonly" ] || exit 0
199 # Make sure the system type is supported for chroot
200 sysname="$(uname -s | tr A-Z a-z || :)"
201 : ${sysname:=linux}
202 nosshdir=
203 # These equivalents may need to be expanded at some point
204 case "$sysname" in
205 *kfreebsd*)
206 sysname=linux;;
207 *darwin*)
208 sysname=darwin;;
209 *dragonfly*)
210 sysname=dragonfly;;
211 *freebsd*)
212 sysname=freebsd;;
213 *linux*)
214 sysname=linux;;
215 esac
217 chrootsetup="$curdir/chrootsetup_$sysname.sh"
218 if ! [ -r "$chrootsetup" -a -s "$chrootsetup" ]; then
219 echo "*** Error: $chrootsetup not found" >&2
220 echo "*** Error: creating a chroot for a `uname -s` system is not supported" >&2
221 exit 1
224 # validate reporoot, chroot and jailreporoot before doing anything more
226 # validates the passed in dir if a second argument is not empty dir must NOT
227 # start with / otherwise it must. A trailing '/' is removed and any duplicated
228 # // are removed and a sole / or empty is disallowed.
229 make_valid_dir() {
230 _check="$(echo "$1" | tr -s /)"
231 _check="${_check%/}"
232 [ -z "$_check" -o "$_check" = "/" ] && return 1
233 if [ -z "$2" ]; then
234 # must start with '/'
235 case "$_check" in /*) :;; *) return 1; esac
236 else
237 # must NOT start with '/'
238 case "$_check" in /*) return 1; esac
240 echo "$_check"
243 if ! reporoot="$(make_valid_dir "$cfg_reporoot")"; then
244 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
245 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
246 exit 1
248 if ! chroot="$(make_valid_dir "$cfg_chroot")"; then
249 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
250 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
251 exit 1
253 if ! jailreporoot="$(make_valid_dir "$cfg_jailreporoot" 1)"; then
254 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
255 echo "*** Error: MUST NOT start with '/' and MUST NOT be ''" >&2
256 exit 1
259 # chroot MUST NOT be reporoot
260 if [ "$chroot" = "$reporoot" ]; then
261 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
262 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
263 echo "*** Error: reporoot and chroot MUST NOT be the same" >&2
264 exit 1
267 # chroot MUST NOT be a subdirectory of reporoot
268 case "$chroot" in "$reporoot"/*)
269 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
270 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
271 echo "*** Error: chroot MUST NOT be a subdirectory of reporoot" >&2
272 exit 1
273 esac
275 # chroot/jailreporoot MUST NOT be a subdirectory of reporoot
276 case "$chroot/$jailreporoot" in "$reporoot"/*)
277 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
278 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
279 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
280 echo "*** Error: chroot/jailreporoot MUST NOT be a subdirectory of reporoot" >&2
281 exit 1
282 esac
284 # reporoot MUST NOT be a subdirectory of chroot/jailreporoot
285 case "$reporoot" in "$chroot/$jailreporoot"/*)
286 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
287 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
288 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
289 echo "*** Error: reporoot MUST NOT be a subdirectory of chroot/jailreporoot" >&2
290 exit 1
291 esac
293 # Set the user and group on the top of the chroot before creating anything else
294 chown 0:0 "$chroot"
296 # When we create a fork, the alternates always have an absolute path.
297 # If reporoot is not --bind mounted at the same location in chroot we must
298 # create a suitable symlink so the absolute path alternates continue to work
299 # in the ssh chroot or else forks will be broken in there.
300 if [ "$reporoot" != "/$jailreporoot" ]; then
301 mkdirp="$(dirname "${reporoot#/}")"
302 [ "$mkdirp" = "." ] && mkdirp=
303 lnback=
304 [ -z "$mkdirp" ] || lnback="$(echo "$mkdirp/" | sed -e 's,[^/]*/,../,g')"
305 [ -z "$mkdirp" ] || mkdir -p "$chroot/$mkdirp"
306 (umask 0; ln -s -f -n "$lnback$jailreporoot" "$chroot$reporoot")
307 [ $? -eq 0 ] || exit 1
310 # First, setup basic platform-independent directory structure
311 mkdir -p bin dev etc lib sbin var/empty var/run "$jailreporoot"
312 chmod 0555 var/empty
313 rm -rf usr local
314 ln -s . usr
315 ln -s . local
317 # Now source the platform-specific script that is responsible for dev device
318 # setup, proc setup (if needed), lib64 setup (if needed) and basic library
319 # installation to make a chroot operational. Additionally it will define a
320 # pull_in_bin function that can be used to add executables and their library
321 # dependencies to the chroot and finally will install a suitable nc.openbsd
322 # compatible version of netcat that supports connections to unix sockets.
323 . "$chrootsetup"
325 # Now, bring in sshd, sh etc.
326 # The $chrootsetup script should have already provided a suitable nc.openbsd
327 install -p "$cfg_basedir/bin/git-shell-verify" bin/git-shell-verify.new
328 install -p "$cfg_basedir/bin/git-askpass-password" bin/git-askpass-password.new
329 perl -i -p \
330 -e 's|^#!.*|#!/bin/sh| if $. == 1;' \
331 -e 'close ARGV if eof;' \
332 bin/git-shell-verify.new bin/git-askpass-password.new
333 mv -f bin/git-askpass-password.new bin/git-askpass-password
334 mv -f bin/git-shell-verify.new bin/git-shell-verify
335 pull_in_bin "$cfg_basedir/bin/can_user_push" bin
336 pull_in_bin "$cfg_basedir/bin/list_packs" bin
337 pull_in_bin "$var_sh_bin" bin/sh
338 pull_in_bin /bin/chmod bin
339 pull_in_bin /bin/date bin
340 pull_in_bin /bin/mkdir bin
341 pull_in_bin /bin/mv bin
342 pull_in_bin /bin/rm bin
343 pull_in_bin /bin/sleep bin
344 pull_in_bin /usr/bin/find bin
345 pull_in_bin /usr/bin/touch bin
346 pull_in_bin /usr/bin/wc bin
347 pull_in_bin /usr/bin/xargs bin
348 pull_in_bin /usr/sbin/sshd sbin
350 # ...and the bits of git we need,
351 # being sure to use the configured git and its --exec-path to find the pieces
352 for i in git git-index-pack git-receive-pack git-shell git-update-server-info git-upload-archive \
353 git-upload-pack git-unpack-objects git-config git-for-each-ref git-rev-list; do
354 pull_in_bin "$var_git_exec_path/$i" bin git
355 done
357 # Note time of last jailsetup
358 > etc/sshactive/_jailsetup
360 # Update permissions on the database files
361 chown $cfg_cgi_user:$cfg_owning_group etc/passwd etc/group
362 chown -R $cfg_cgi_user:$cfg_owning_group etc/sshkeys etc/sshcerts etc/sshactive
363 chown $cfg_mirror_user:$cfg_owning_group etc etc/girocco etc/girocco/.gitconfig
365 # Set up basic sshd configuration:
366 if [ -n "$nosshdir" ]; then
367 rm -rf etc/ssh
368 ln -s . etc/ssh
369 [ ! -f /etc/moduli ] || { cp -p /etc/moduli etc/; chown 0:0 etc/moduli; }
370 else
371 [ ! -e etc/ssh -o -d etc/ssh ] || rm -rf etc/ssh
372 mkdir -p etc/ssh
373 [ ! -f /etc/ssh/moduli ] || { cp -p /etc/ssh/moduli etc/ssh/; chown 0:0 etc/ssh/moduli; }
375 mkdir -p var/run/sshd
376 if [ ! -s etc/ssh/sshd_config ]; then
377 cat >etc/ssh/sshd_config <<EOT
378 Protocol 2
379 Port $cfg_sshd_jail_port
380 UsePAM no
381 X11Forwarding no
382 AllowAgentForwarding no
383 AllowTcpForwarding no
384 PermitTunnel no
385 IgnoreUserKnownHosts yes
386 PrintLastLog no
387 PrintMotd no
388 UseDNS no
389 PermitRootLogin no
390 UsePrivilegeSeparation yes
392 HostKey /etc/ssh/ssh_host_rsa_key
394 if [ -z "$cfg_disable_dsa" ]; then
395 cat >>etc/ssh/sshd_config <<EOT
396 HostKey /etc/ssh/ssh_host_dsa_key
399 cat >>etc/ssh/sshd_config <<EOT
400 AuthorizedKeysFile /etc/sshkeys/%u
401 StrictModes no
403 # mob and git users:
404 PermitEmptyPasswords yes
405 ChallengeResponseAuthentication no
406 PasswordAuthentication yes
409 if [ ! -s etc/ssh/ssh_host_rsa_key ]; then
410 bits=2048
411 if [ "$cfg_rsakeylength" -gt "$bits" ] 2>/dev/null; then
412 bits="$cfg_rsakeylength"
414 yes | ssh-keygen -b "$bits" -t rsa -N "" -C Girocco -f etc/ssh/ssh_host_rsa_key
416 if [ -z "$cfg_disable_dsa" -a ! -s etc/ssh/ssh_host_dsa_key ]; then
417 # ssh-keygen can only create 1024 bit DSA keys
418 yes | ssh-keygen -b 1024 -t dsa -N "" -C Girocco -f etc/ssh/ssh_host_dsa_key
421 # Set the final permissions on the binaries and perform any final twiddling
422 chroot_update_permissions
424 # Change the owner of the sshd-related files
425 chown 0:0 etc/ssh/ssh_* etc/ssh/sshd_*
427 echo "--- Add to your boot scripts: mount --bind $reporoot $chroot/$jailreporoot"
428 echo "--- Add to your boot scripts: mount --bind /proc $chroot/proc"
429 echo "--- Add to your syslog configuration: listening on socket $chroot/dev/log"
430 echo "--- To restart a running jail's sshd: sudo kill -HUP \`cat $chroot/var/run/sshd.pid\`"