bitmaps: include a bitmap hash cache by default
[girocco.git] / jailsetup.sh
blob1ea0dd907557b07438f6a4eaf2cf3f26038928ed
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 EOT
95 elif [ -z "$dbonly" ]; then
96 # Make sure an sshd entry is present
97 if ! grep -q '^sshd:' etc/passwd; then
98 echo "*** Error: chroot etc/passwd exists but lacks sshd entry." >&2
99 exit 1
103 if [ ! -s etc/group ]; then
104 cat >etc/group <<EOT
105 _repo:x:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):$cfg_mirror_user
109 # Set up basic default Git configuration
110 # Initialize one if none exists or update critical variables for an existing one
111 mkdir -p etc/girocco
112 didchmod=
113 if [ -e etc/girocco/.gitconfig ] && ! [ -f etc/girocco/.gitconfig ]; then
114 echo "*** Error: chroot etc/girocco/.gitconfig exists but is not a file." >&2
115 exit 1
117 if [ -f etc/girocco/.gitconfig ]; then
118 gcerr=0
119 x="$(git config --file etc/girocco/.gitconfig --get "no--such--section.no such subsection.no--such--key")" || gcerr=$?
120 if [ $gcerr -gt 1 ]; then
121 echo "*** Error: chroot etc/girocco/.gitconfig exists but is corrupt." >&2
122 echo "*** Error: either remove it or edit it to correct the problem." >&2
123 exit 1
126 if [ ! -s etc/girocco/.gitconfig ]; then
127 chmod u+w etc/girocco
128 didchmod=1
129 cat >etc/girocco/.gitconfig <<EOT
130 # Any values set here will take effect whenever Girocco runs a git command
134 # $1 => name, $2 => value, $3 => overwrite_flag
135 update_config_item() {
136 _existsnot=
137 _oldval=
138 _oldval="$(git config --file etc/girocco/.gitconfig --get "$1")" || _existsnot=1
139 if [ -z "$_existsnot" ]; then
140 [ -n "$3" ] || return 0
141 [ "$_oldval" != "$2" ] || return 0
143 [ -n "$didchmod" ] || { chmod u+w etc/girocco; didchmod=1; }
144 git config --file etc/girocco/.gitconfig "$1" "$2"
145 if [ -n "$_existsnot" ]; then
146 echo "chroot: etc/girocco/.gitconfig: config $1: (created) \"$2\""
147 else
148 echo "chroot: etc/girocco/.gitconfig: config $1: \"$_oldval\" -> \"$2\""
151 if [ -n "$cfg_git_no_mmap" ]; then
152 update_config_item core.packedGitWindowSize 1m 1
153 else
154 update_config_item core.packedGitWindowSize 32m 1
156 if [ -n "$var_window_memory" ]; then
157 update_config_item pack.windowMemory "$var_window_memory" 1
159 if [ -n "$cfg_jgit_compatible_bitmaps" ]; then
160 update_config_item pack.writeBitmapHashCache false 1
161 else
162 update_config_item pack.writeBitmapHashCache true 1
164 update_config_item http.lowSpeedLimit 1
165 update_config_item http.lowSpeedTime 600
166 [ -z "$didchmod" ] || chmod a-w etc/girocco
168 mkdir -p etc/sshkeys etc/sshcerts etc/sshactive
169 for ruser in $reserved_users; do
170 touch etc/sshkeys/$ruser
171 done
172 chgrp $cfg_owning_group etc etc/sshkeys etc/sshcerts etc/sshactive ||
173 echo "WARNING: Cannot chgrp $cfg_owning_group the etc directories"
174 chgrp $cfg_owning_group etc/passwd ||
175 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/passwd"
176 chgrp $cfg_owning_group etc/group ||
177 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/group"
178 chgrp $cfg_owning_group etc/girocco etc/girocco/.gitconfig ||
179 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/girocco"
180 chmod g+s etc etc/sshkeys etc/sshcerts etc/sshactive ||
181 echo "WARNING: Cannot chmod g+s the etc directories"
182 chmod g+w etc etc/sshkeys etc/sshcerts etc/sshactive ||
183 echo "WARNING: Cannot chmod g+w the etc directories"
184 chmod g+w etc/passwd etc/group ||
185 echo "WARNING: Cannot chmod g+w the etc/passwd and/or etc/group files"
186 chmod go-w etc/passwd etc/girocco etc/girocco/.gitconfig ||
187 echo "WARNING: Cannot chmod go-w etc/girocco and/or etc/girocco/.gitconfig"
188 chmod a-w etc/girocco ||
189 echo "WARNING: Cannot chmod a-w etc/girocco"
190 chmod -R g+w etc/sshkeys etc/sshcerts etc/sshactive 2>/dev/null ||
191 echo "WARNING: Cannot chmod g+w the sshkeys, sshcerts and/or sshactive files"
193 # Note time of last install
194 > etc/sshactive/_install
196 [ -z "$dbonly" ] || exit 0
198 # Make sure the system type is supported for chroot
199 sysname="$(uname -s | tr A-Z a-z || :)"
200 : ${sysname:=linux}
201 nosshdir=
202 # These equivalents may need to be expanded at some point
203 case "$sysname" in
204 *kfreebsd*)
205 sysname=linux;;
206 *darwin*)
207 sysname=darwin;;
208 *dragonfly*)
209 sysname=dragonfly;;
210 *freebsd*)
211 sysname=freebsd;;
212 *linux*)
213 sysname=linux;;
214 esac
216 chrootsetup="$curdir/chrootsetup_$sysname.sh"
217 if ! [ -r "$chrootsetup" -a -s "$chrootsetup" ]; then
218 echo "*** Error: $chrootsetup not found" >&2
219 echo "*** Error: creating a chroot for a `uname -s` system is not supported" >&2
220 exit 1
223 # validate reporoot, chroot and jailreporoot before doing anything more
225 # validates the passed in dir if a second argument is not empty dir must NOT
226 # start with / otherwise it must. A trailing '/' is removed and any duplicated
227 # // are removed and a sole / or empty is disallowed.
228 make_valid_dir() {
229 _check="$(echo "$1" | tr -s /)"
230 _check="${_check%/}"
231 [ -z "$_check" -o "$_check" = "/" ] && return 1
232 if [ -z "$2" ]; then
233 # must start with '/'
234 case "$_check" in /*) :;; *) return 1; esac
235 else
236 # must NOT start with '/'
237 case "$_check" in /*) return 1; esac
239 echo "$_check"
242 if ! reporoot="$(make_valid_dir "$cfg_reporoot")"; then
243 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
244 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
245 exit 1
247 if ! chroot="$(make_valid_dir "$cfg_chroot")"; then
248 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
249 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
250 exit 1
252 if ! jailreporoot="$(make_valid_dir "$cfg_jailreporoot" 1)"; then
253 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
254 echo "*** Error: MUST NOT start with '/' and MUST NOT be ''" >&2
255 exit 1
258 # chroot MUST NOT be reporoot
259 if [ "$chroot" = "$reporoot" ]; then
260 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
261 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
262 echo "*** Error: reporoot and chroot MUST NOT be the same" >&2
263 exit 1
266 # chroot MUST NOT be a subdirectory of reporoot
267 case "$chroot" in "$reporoot"/*)
268 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
269 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
270 echo "*** Error: chroot MUST NOT be a subdirectory of reporoot" >&2
271 exit 1
272 esac
274 # chroot/jailreporoot MUST NOT be a subdirectory of reporoot
275 case "$chroot/$jailreporoot" in "$reporoot"/*)
276 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
277 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
278 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
279 echo "*** Error: chroot/jailreporoot MUST NOT be a subdirectory of reporoot" >&2
280 exit 1
281 esac
283 # reporoot MUST NOT be a subdirectory of chroot/jailreporoot
284 case "$reporoot" in "$chroot/$jailreporoot"/*)
285 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
286 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
287 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
288 echo "*** Error: reporoot MUST NOT be a subdirectory of chroot/jailreporoot" >&2
289 exit 1
290 esac
292 # Set the user and group on the top of the chroot before creating anything else
293 chown 0:0 "$chroot"
295 # When we create a fork, the alternates always have an absolute path.
296 # If reporoot is not --bind mounted at the same location in chroot we must
297 # create a suitable symlink so the absolute path alternates continue to work
298 # in the ssh chroot or else forks will be broken in there.
299 if [ "$reporoot" != "/$jailreporoot" ]; then
300 mkdirp="$(dirname "${reporoot#/}")"
301 [ "$mkdirp" = "." ] && mkdirp=
302 lnback=
303 [ -z "$mkdirp" ] || lnback="$(echo "$mkdirp/" | sed -e 's,[^/]*/,../,g')"
304 [ -z "$mkdirp" ] || mkdir -p "$chroot/$mkdirp"
305 (umask 0; ln -s -f -n "$lnback$jailreporoot" "$chroot$reporoot")
306 [ $? -eq 0 ] || exit 1
309 # First, setup basic platform-independent directory structure
310 mkdir -p bin dev etc lib sbin var/empty var/run "$jailreporoot"
311 chmod 0555 var/empty
312 rm -rf usr local
313 ln -s . usr
314 ln -s . local
316 # Now source the platform-specific script that is responsible for dev device
317 # setup, proc setup (if needed), lib64 setup (if needed) and basic library
318 # installation to make a chroot operational. Additionally it will define a
319 # pull_in_bin function that can be used to add executables and their library
320 # dependencies to the chroot and finally will install a suitable nc.openbsd
321 # compatible version of netcat that supports connections to unix sockets.
322 . "$chrootsetup"
324 # Now, bring in sshd, sh etc.
325 # The $chrootsetup script should have already provided a suitable nc.openbsd
326 install -p "$cfg_basedir/bin/git-shell-verify" bin/git-shell-verify.new
327 install -p "$cfg_basedir/bin/git-askpass-password" bin/git-askpass-password.new
328 perl -i -p \
329 -e 's|^#!.*|#!/bin/sh| if $. == 1;' \
330 -e 'close ARGV if eof;' \
331 bin/git-shell-verify.new bin/git-askpass-password.new
332 mv -f bin/git-askpass-password.new bin/git-askpass-password
333 mv -f bin/git-shell-verify.new bin/git-shell-verify
334 pull_in_bin "$cfg_basedir/bin/can_user_push" bin
335 pull_in_bin "$cfg_basedir/bin/list_packs" bin
336 pull_in_bin "$var_sh_bin" bin/sh
337 pull_in_bin /bin/chmod bin
338 pull_in_bin /bin/date bin
339 pull_in_bin /bin/mkdir bin
340 pull_in_bin /bin/mv bin
341 pull_in_bin /bin/rm bin
342 pull_in_bin /bin/sleep bin
343 pull_in_bin /usr/bin/find bin
344 pull_in_bin /usr/bin/touch bin
345 pull_in_bin /usr/bin/wc bin
346 pull_in_bin /usr/bin/xargs bin
347 pull_in_bin /usr/sbin/sshd sbin
349 # ...and the bits of git we need,
350 # being sure to use the configured git and its --exec-path to find the pieces
351 for i in git git-index-pack git-receive-pack git-shell git-update-server-info git-upload-archive \
352 git-upload-pack git-unpack-objects git-config git-for-each-ref git-rev-list; do
353 pull_in_bin "$var_git_exec_path/$i" bin git
354 done
356 # Note time of last jailsetup
357 > etc/sshactive/_jailsetup
359 # Update permissions on the database files
360 chown $cfg_cgi_user:$cfg_owning_group etc/passwd etc/group
361 chown -R $cfg_cgi_user:$cfg_owning_group etc/sshkeys etc/sshcerts etc/sshactive
362 chown $cfg_mirror_user:$cfg_owning_group etc etc/girocco etc/girocco/.gitconfig
364 # Set up basic sshd configuration:
365 if [ -n "$nosshdir" ]; then
366 rm -rf etc/ssh
367 ln -s . etc/ssh
368 [ ! -f /etc/moduli ] || { cp -p /etc/moduli etc/; chown 0:0 etc/moduli; }
369 else
370 [ ! -e etc/ssh -o -d etc/ssh ] || rm -rf etc/ssh
371 mkdir -p etc/ssh
372 [ ! -f /etc/ssh/moduli ] || { cp -p /etc/ssh/moduli etc/ssh/; chown 0:0 etc/ssh/moduli; }
374 mkdir -p var/run/sshd
375 if [ ! -s etc/ssh/sshd_config ]; then
376 cat >etc/ssh/sshd_config <<EOT
377 Protocol 2
378 Port $cfg_sshd_jail_port
379 UsePAM no
380 X11Forwarding no
381 AllowAgentForwarding no
382 AllowTcpForwarding no
383 PermitTunnel no
384 IgnoreUserKnownHosts yes
385 PrintLastLog no
386 PrintMotd no
387 UseDNS no
388 PermitRootLogin no
389 UsePrivilegeSeparation yes
391 HostKey /etc/ssh/ssh_host_rsa_key
393 if [ -z "$cfg_disable_dsa" ]; then
394 cat >>etc/ssh/sshd_config <<EOT
395 HostKey /etc/ssh/ssh_host_dsa_key
398 cat >>etc/ssh/sshd_config <<EOT
399 AuthorizedKeysFile /etc/sshkeys/%u
400 StrictModes no
402 # mob user:
403 PermitEmptyPasswords yes
404 ChallengeResponseAuthentication no
405 PasswordAuthentication yes
408 if [ ! -s etc/ssh/ssh_host_rsa_key ]; then
409 bits=2048
410 if [ "$cfg_rsakeylength" -gt "$bits" ] 2>/dev/null; then
411 bits="$cfg_rsakeylength"
413 yes | ssh-keygen -b "$bits" -t rsa -N "" -C Girocco -f etc/ssh/ssh_host_rsa_key
415 if [ -z "$cfg_disable_dsa" -a ! -s etc/ssh/ssh_host_dsa_key ]; then
416 # ssh-keygen can only create 1024 bit DSA keys
417 yes | ssh-keygen -b 1024 -t dsa -N "" -C Girocco -f etc/ssh/ssh_host_dsa_key
420 # Set the final permissions on the binaries and perform any final twiddling
421 chroot_update_permissions
423 # Change the owner of the sshd-related files
424 chown 0:0 etc/ssh/ssh_* etc/ssh/sshd_*
426 echo "--- Add to your boot scripts: mount --bind $reporoot $chroot/$jailreporoot"
427 echo "--- Add to your boot scripts: mount --bind /proc $chroot/proc"
428 echo "--- Add to your syslog configuration: listening on socket $chroot/dev/log"
429 echo "--- To restart a running jail's sshd: sudo kill -HUP \`cat $chroot/var/run/sshd.pid\`"