refs: and we're logging...
[girocco.git] / jailsetup.sh
blob2e66ac3e217ace31d698d435e2a415dfa9616a25
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 update_config_item http.lowSpeedLimit 1
160 update_config_item http.lowSpeedTime 600
161 [ -z "$didchmod" ] || chmod a-w etc/girocco
163 mkdir -p etc/sshkeys etc/sshcerts etc/sshactive
164 for ruser in $reserved_users; do
165 touch etc/sshkeys/$ruser
166 done
167 chgrp $cfg_owning_group etc etc/sshkeys etc/sshcerts etc/sshactive ||
168 echo "WARNING: Cannot chgrp $cfg_owning_group the etc directories"
169 chgrp $cfg_owning_group etc/passwd ||
170 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/passwd"
171 chgrp $cfg_owning_group etc/group ||
172 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/group"
173 chgrp $cfg_owning_group etc/girocco etc/girocco/.gitconfig ||
174 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/girocco"
175 chmod g+s etc etc/sshkeys etc/sshcerts etc/sshactive ||
176 echo "WARNING: Cannot chmod g+s the etc directories"
177 chmod g+w etc etc/sshkeys etc/sshcerts etc/sshactive ||
178 echo "WARNING: Cannot chmod g+w the etc directories"
179 chmod g+w etc/passwd etc/group ||
180 echo "WARNING: Cannot chmod g+w the etc/passwd and/or etc/group files"
181 chmod go-w etc/passwd etc/girocco etc/girocco/.gitconfig ||
182 echo "WARNING: Cannot chmod go-w etc/girocco and/or etc/girocco/.gitconfig"
183 chmod a-w etc/girocco ||
184 echo "WARNING: Cannot chmod a-w etc/girocco"
185 chmod -R g+w etc/sshkeys etc/sshcerts etc/sshactive 2>/dev/null ||
186 echo "WARNING: Cannot chmod g+w the sshkeys, sshcerts and/or sshactive files"
188 # Note time of last install
189 > etc/sshactive/_install
191 [ -z "$dbonly" ] || exit 0
193 # Make sure the system type is supported for chroot
194 sysname="$(uname -s | tr A-Z a-z || :)"
195 : ${sysname:=linux}
196 nosshdir=
197 # These equivalents may need to be expanded at some point
198 case "$sysname" in
199 *kfreebsd*)
200 sysname=linux;;
201 *darwin*)
202 sysname=darwin;;
203 *freebsd*)
204 sysname=freebsd;;
205 *linux*)
206 sysname=linux;;
207 esac
209 chrootsetup="$curdir/chrootsetup_$sysname.sh"
210 if ! [ -r "$chrootsetup" -a -s "$chrootsetup" ]; then
211 echo "*** Error: $chrootsetup not found" >&2
212 echo "*** Error: creating a chroot for a `uname -s` system is not supported" >&2
213 exit 1
216 # validate reporoot, chroot and jailreporoot before doing anything more
218 # validates the passed in dir if a second argument is not empty dir must NOT
219 # start with / otherwise it must. A trailing '/' is removed and any duplicated
220 # // are removed and a sole / or empty is disallowed.
221 make_valid_dir() {
222 _check="$(echo "$1" | tr -s /)"
223 _check="${_check%/}"
224 [ -z "$_check" -o "$_check" = "/" ] && return 1
225 if [ -z "$2" ]; then
226 # must start with '/'
227 case "$_check" in /*) :;; *) return 1; esac
228 else
229 # must NOT start with '/'
230 case "$_check" in /*) return 1; esac
232 echo "$_check"
235 if ! reporoot="$(make_valid_dir "$cfg_reporoot")"; then
236 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
237 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
238 exit 1
240 if ! chroot="$(make_valid_dir "$cfg_chroot")"; then
241 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
242 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
243 exit 1
245 if ! jailreporoot="$(make_valid_dir "$cfg_jailreporoot" 1)"; then
246 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
247 echo "*** Error: MUST NOT start with '/' and MUST NOT be ''" >&2
248 exit 1
251 # chroot MUST NOT be reporoot
252 if [ "$chroot" = "$reporoot" ]; then
253 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
254 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
255 echo "*** Error: reporoot and chroot MUST NOT be the same" >&2
256 exit 1
259 # chroot MUST NOT be a subdirectory of reporoot
260 case "$chroot" in "$reporoot"/*)
261 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
262 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
263 echo "*** Error: chroot MUST NOT be a subdirectory of reporoot" >&2
264 exit 1
265 esac
267 # chroot/jailreporoot MUST NOT be a subdirectory of reporoot
268 case "$chroot/$jailreporoot" in "$reporoot"/*)
269 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
270 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
271 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
272 echo "*** Error: chroot/jailreporoot MUST NOT be a subdirectory of reporoot" >&2
273 exit 1
274 esac
276 # reporoot MUST NOT be a subdirectory of chroot/jailreporoot
277 case "$reporoot" in "$chroot/$jailreporoot"/*)
278 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
279 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
280 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
281 echo "*** Error: reporoot MUST NOT be a subdirectory of chroot/jailreporoot" >&2
282 exit 1
283 esac
285 # Set the user and group on the top of the chroot before creating anything else
286 chown 0:0 "$chroot"
288 # When we create a fork, the alternates always have an absolute path.
289 # If reporoot is not --bind mounted at the same location in chroot we must
290 # create a suitable symlink so the absolute path alternates continue to work
291 # in the ssh chroot or else forks will be broken in there.
292 if [ "$reporoot" != "/$jailreporoot" ]; then
293 mkdirp="$(dirname "${reporoot#/}")"
294 [ "$mkdirp" = "." ] && mkdirp=
295 lnback=
296 [ -z "$mkdirp" ] || lnback="$(echo "$mkdirp/" | sed -e 's,[^/]*/,../,g')"
297 [ -z "$mkdirp" ] || mkdir -p "$chroot/$mkdirp"
298 (umask 0; ln -s -f -n "$lnback$jailreporoot" "$chroot$reporoot")
299 [ $? -eq 0 ] || exit 1
302 # First, setup basic platform-independent directory structure
303 mkdir -p bin dev etc lib sbin var/empty var/run "$jailreporoot"
304 chmod 0555 var/empty
305 rm -rf usr local
306 ln -s . usr
307 ln -s . local
309 # Now source the platform-specific script that is responsible for dev device
310 # setup, proc setup (if needed), lib64 setup (if needed) and basic library
311 # installation to make a chroot operational. Additionally it will define a
312 # pull_in_bin function that can be used to add executables and their library
313 # dependencies to the chroot and finally will install a suitable nc.openbsd
314 # compatible version of netcat that supports connections to unix sockets.
315 . "$chrootsetup"
317 # Now, bring in sshd, sh etc.
318 # The $chrootsetup script should have already provided a suitable nc.openbsd
319 install -p "$cfg_basedir/bin/git-shell-verify" bin/git-shell-verify.new
320 install -p "$cfg_basedir/bin/git-askpass-password" bin/git-askpass-password.new
321 perl -i -p \
322 -e 's|^#!.*|#!/bin/sh| if $. == 1;' \
323 -e 'close ARGV if eof;' \
324 bin/git-shell-verify.new bin/git-askpass-password.new
325 mv -f bin/git-askpass-password.new bin/git-askpass-password
326 mv -f bin/git-shell-verify.new bin/git-shell-verify
327 pull_in_bin "$cfg_basedir/bin/can_user_push" bin
328 pull_in_bin "$cfg_basedir/bin/list_packs" bin
329 pull_in_bin "${cfg_posix_sh_bin:-/bin/sh}" bin/sh
330 pull_in_bin /bin/chmod bin
331 pull_in_bin /bin/date bin
332 pull_in_bin /bin/mkdir bin
333 pull_in_bin /bin/mv bin
334 pull_in_bin /bin/rm bin
335 pull_in_bin /bin/sleep bin
336 pull_in_bin /usr/bin/find bin
337 pull_in_bin /usr/bin/touch bin
338 pull_in_bin /usr/bin/wc bin
339 pull_in_bin /usr/bin/xargs bin
340 pull_in_bin /usr/sbin/sshd sbin
342 # ...and the bits of git we need,
343 # being sure to use the configured git and its --exec-path to find the pieces
344 for i in git git-index-pack git-receive-pack git-shell git-update-server-info git-upload-archive \
345 git-upload-pack git-unpack-objects git-config git-for-each-ref git-rev-list; do
346 pull_in_bin "$var_git_exec_path/$i" bin git
347 done
349 # Note time of last jailsetup
350 > etc/sshactive/_jailsetup
352 # Update permissions on the database files
353 chown $cfg_cgi_user:$cfg_owning_group etc/passwd etc/group
354 chown -R $cfg_cgi_user:$cfg_owning_group etc/sshkeys etc/sshcerts etc/sshactive
355 chown $cfg_mirror_user:$cfg_owning_group etc etc/girocco etc/girocco/.gitconfig
357 # Set up basic sshd configuration:
358 if [ -n "$nosshdir" ]; then
359 rm -rf etc/ssh
360 ln -s . etc/ssh
361 [ ! -f /etc/moduli ] || { cp -p /etc/moduli etc/; chown 0:0 etc/moduli; }
362 else
363 [ ! -e etc/ssh -o -d etc/ssh ] || rm -rf etc/ssh
364 mkdir -p etc/ssh
365 [ ! -f /etc/ssh/moduli ] || { cp -p /etc/ssh/moduli etc/ssh/; chown 0:0 etc/ssh/moduli; }
367 mkdir -p var/run/sshd
368 if [ ! -s etc/ssh/sshd_config ]; then
369 cat >etc/ssh/sshd_config <<EOT
370 Protocol 2
371 Port $cfg_sshd_jail_port
372 UsePAM no
373 X11Forwarding no
374 AllowAgentForwarding no
375 AllowTcpForwarding no
376 PermitTunnel no
377 IgnoreUserKnownHosts yes
378 PrintLastLog no
379 PrintMotd no
380 UseDNS no
381 PermitRootLogin no
382 UsePrivilegeSeparation yes
384 HostKey /etc/ssh/ssh_host_rsa_key
386 if [ -z "$cfg_disable_dsa" ]; then
387 cat >>etc/ssh/sshd_config <<EOT
388 HostKey /etc/ssh/ssh_host_dsa_key
391 cat >>etc/ssh/sshd_config <<EOT
392 AuthorizedKeysFile /etc/sshkeys/%u
393 StrictModes no
395 # mob user:
396 PermitEmptyPasswords yes
397 ChallengeResponseAuthentication no
398 PasswordAuthentication yes
401 if [ ! -s etc/ssh/ssh_host_rsa_key ]; then
402 bits=2048
403 if [ "$cfg_rsakeylength" -gt "$bits" ] 2>/dev/null; then
404 bits="$cfg_rsakeylength"
406 yes | ssh-keygen -b "$bits" -t rsa -N "" -C Girocco -f etc/ssh/ssh_host_rsa_key
408 if [ -z "$cfg_disable_dsa" -a ! -s etc/ssh/ssh_host_dsa_key ]; then
409 # ssh-keygen can only create 1024 bit DSA keys
410 yes | ssh-keygen -b 1024 -t dsa -N "" -C Girocco -f etc/ssh/ssh_host_dsa_key
413 # Set the final permissions on the binaries and perform any final twiddling
414 chroot_update_permissions
416 # Change the owner of the sshd-related files
417 chown 0:0 etc/ssh/ssh_* etc/ssh/sshd_*
419 echo "--- Add to your boot scripts: mount --bind $reporoot $chroot/$jailreporoot"
420 echo "--- Add to your boot scripts: mount --bind /proc $chroot/proc"
421 echo "--- Add to your syslog configuration: listening on socket $chroot/dev/log"
422 echo "--- To restart a running jail's sshd: sudo kill -HUP \`cat $chroot/var/run/sshd.pid\`"