jailsetup.sh: always create $chroot/var/empty
[girocco.git] / jailsetup.sh
bloba06f27d051870f742243a4fd3bfa9930e8634b8c
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 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 cat >>etc/passwd <<EOT
88 $cfg_cgi_user:x:$("$getent" passwd "$cfg_cgi_user" | cut -d : -f 3-5):/:/bin/true
89 $cfg_mirror_user:x:$("$getent" passwd "$cfg_mirror_user" | cut -d : -f 3-5):/:/bin/true
90 everyone:x:65537:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):every user:/:/bin/false
91 mob:$mobpass:65538:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):the mob:/:/bin/git-shell-verify
92 EOT
93 elif [ -z "$dbonly" ]; then
94 # Make sure an sshd entry is present
95 if ! grep -q '^sshd:' etc/passwd; then
96 echo "*** Error: chroot etc/passwd exists but lacks sshd entry." >&2
97 exit 1
101 if [ ! -s etc/group ]; then
102 cat >etc/group <<EOT
103 _repo:x:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):$cfg_mirror_user
107 # Set up basic default Git configuration; if there isn't any already
108 mkdir -p etc/girocco
109 if [ ! -s etc/girocco/.gitconfig ]; then
110 cat >etc/girocco/.gitconfig <<EOT
111 [http]
112 lowSpeedLimit = 1
113 lowSpeedTime = 600
117 mkdir -p etc/sshkeys etc/sshcerts etc/sshactive
118 for ruser in $reserved_users; do
119 touch etc/sshkeys/$ruser
120 done
121 chgrp $cfg_owning_group etc etc/sshkeys etc/sshcerts etc/sshactive ||
122 echo "WARNING: Cannot chgrp $cfg_owning_group the etc directories"
123 chgrp $cfg_owning_group etc/passwd ||
124 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/passwd"
125 chgrp $cfg_owning_group etc/group ||
126 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/group"
127 chgrp $cfg_owning_group etc/girocco etc/girocco/.gitconfig ||
128 echo "WARNING: Cannot chgrp $cfg_owning_group $cfg_chroot/etc/girocco"
129 chmod g+s etc etc/sshkeys etc/sshcerts etc/sshactive ||
130 echo "WARNING: Cannot chmod g+s the etc directories"
131 chmod g+w etc etc/sshkeys etc/sshcerts etc/sshactive ||
132 echo "WARNING: Cannot chmod g+w the etc directories"
133 chmod g+w etc/passwd etc/group ||
134 echo "WARNING: Cannot chmod g+w the etc/passwd and/or etc/group files"
135 chmod go-w etc/passwd etc/girocco etc/girocco/.gitconfig ||
136 echo "WARNING: Cannot chmod go-w etc/girocco and/or etc/girocco/.gitconfig"
137 chmod a-w etc/girocco ||
138 echo "WARNING: Cannot chmod a-w etc/girocco"
139 chmod -R g+w etc/sshkeys etc/sshcerts etc/sshactive 2>/dev/null ||
140 echo "WARNING: Cannot chmod g+w the sshkeys, sshcerts and/or sshactive files"
142 # Note time of last install
143 > etc/sshactive/_install
145 [ -z "$dbonly" ] || exit 0
147 # Make sure the system type is supported for chroot
148 sysname="$(uname -s | tr A-Z a-z || :)"
149 : ${sysname:=linux}
150 nosshdir=
151 # These equivalents may need to be expanded at some point
152 case "$sysname" in
153 *kfreebsd*)
154 sysname=linux;;
155 *darwin*)
156 sysname=darwin;;
157 *freebsd*)
158 sysname=freebsd;;
159 *linux*)
160 sysname=linux;;
161 esac
163 chrootsetup="$curdir/chrootsetup_$sysname.sh"
164 if ! [ -r "$chrootsetup" -a -s "$chrootsetup" ]; then
165 echo "*** Error: $chrootsetup not found" >&2
166 echo "*** Error: creating a chroot for a `uname -s` system is not supported" >&2
167 exit 1
170 # validate reporoot, chroot and jailreporoot before doing anything more
172 # validates the passed in dir if a second argument is not empty dir must NOT
173 # start with / otherwise it must. A trailing '/' is removed and any duplicated
174 # // are removed and a sole / or empty is disallowed.
175 make_valid_dir() {
176 _check="$(echo "$1" | tr -s /)"
177 _check="${_check%/}"
178 [ -z "$_check" -o "$_check" = "/" ] && return 1
179 if [ -z "$2" ]; then
180 # must start with '/'
181 case "$_check" in /*) :;; *) return 1; esac
182 else
183 # must NOT start with '/'
184 case "$_check" in /*) return 1; esac
186 echo "$_check"
189 if ! reporoot="$(make_valid_dir "$cfg_reporoot")"; then
190 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
191 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
192 exit 1
194 if ! chroot="$(make_valid_dir "$cfg_chroot")"; then
195 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
196 echo "*** Error: MUST start with '/' and MUST NOT be '/'" >&2
197 exit 1
199 if ! jailreporoot="$(make_valid_dir "$cfg_jailreporoot" 1)"; then
200 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
201 echo "*** Error: MUST NOT start with '/' and MUST NOT be ''" >&2
202 exit 1
205 # chroot MUST NOT be reporoot
206 if [ "$chroot" = "$reporoot" ]; then
207 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
208 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
209 echo "*** Error: reporoot and chroot MUST NOT be the same" >&2
210 exit 1
213 # chroot MUST NOT be a subdirectory of reporoot
214 case "$chroot" in "$reporoot"/*)
215 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
216 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
217 echo "*** Error: chroot MUST NOT be a subdirectory of reporoot" >&2
218 exit 1
219 esac
221 # chroot/jailreporoot MUST NOT be a subdirectory of reporoot
222 case "$chroot/$jailreporoot" in "$reporoot"/*)
223 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
224 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
225 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
226 echo "*** Error: chroot/jailreporoot MUST NOT be a subdirectory of reporoot" >&2
227 exit 1
228 esac
230 # reporoot MUST NOT be a subdirectory of chroot/jailreporoot
231 case "$reporoot" in "$chroot/$jailreporoot"/*)
232 echo "*** Error: invalid Config::reporoot: $cfg_reporoot" >&2
233 echo "*** Error: invalid Config::chroot: $cfg_chroot" >&2
234 echo "*** Error: invalid Config::jailreporoot: $cfg_jailreporoot" >&2
235 echo "*** Error: reporoot MUST NOT be a subdirectory of chroot/jailreporoot" >&2
236 exit 1
237 esac
239 # Set the user and group on the top of the chroot before creating anything else
240 chown 0:0 "$chroot"
242 # When we create a fork, the alternates always have an absolute path.
243 # If reporoot is not --bind mounted at the same location in chroot we must
244 # create a suitable symlink so the absolute path alternates continue to work
245 # in the ssh chroot or else forks will be broken in there.
246 if [ "$reporoot" != "/$jailreporoot" ]; then
247 mkdirp="$(dirname "${reporoot#/}")"
248 [ "$mkdirp" = "." ] && mkdirp=
249 lnback=
250 [ -z "$mkdirp" ] || lnback="$(echo "$mkdirp/" | sed -e 's,[^/]*/,../,g')"
251 [ -z "$mkdirp" ] || mkdir -p "$chroot/$mkdirp"
252 (umask 0; ln -s -f -n "$lnback$jailreporoot" "$chroot$reporoot")
253 [ $? -eq 0 ] || exit 1
256 # First, setup basic platform-independent directory structure
257 mkdir -p bin dev etc lib sbin var/empty var/run "$jailreporoot"
258 chmod 0555 var/empty
259 rm -rf usr
260 ln -s . usr
262 # Now source the platform-specific script that is responsible for dev device
263 # setup, proc setup (if needed), lib64 setup (if needed) and basic library
264 # installation to make a chroot operational. Additionally it will define a
265 # pull_in_bin function that can be used to add executables and their library
266 # dependencies to the chroot and finally will install a suitable nc.openbsd
267 # compatible version of netcat that supports connections to unix sockets.
268 . "$chrootsetup"
270 # Now, bring in sshd, sh etc.
271 # The $chrootsetup script should have already provided a suitable nc.openbsd
272 install -p "$cfg_basedir/bin/git-shell-verify" bin
273 install -p "$cfg_basedir/bin/git-askpass-password" bin
274 pull_in_bin "$cfg_basedir/bin/can_user_push" bin
275 pull_in_bin /bin/sh bin
276 pull_in_bin /bin/date bin
277 pull_in_bin /bin/mv bin
278 pull_in_bin /bin/rm bin
279 pull_in_bin /usr/sbin/sshd sbin
281 # ...and the bits of git we need,
282 # being sure to use the configured git and its --exec-path to find the pieces
283 git_exec_path="$("$cfg_git_bin" --exec-path)"
284 for i in git git-index-pack git-receive-pack git-shell git-update-server-info git-upload-archive \
285 git-upload-pack git-unpack-objects git-config git-for-each-ref git-rev-list; do
286 pull_in_bin "$git_exec_path/$i" bin git
287 done
289 # Note time of last jailsetup
290 > etc/sshactive/_jailsetup
292 # Update permissions on the database files
293 chown $cfg_cgi_user:$cfg_owning_group etc/passwd etc/group
294 chown -R $cfg_cgi_user:$cfg_owning_group etc/sshkeys etc/sshcerts etc/sshactive
295 chown $cfg_mirror_user:$cfg_owning_group etc etc/girocco etc/girocco/.gitconfig
297 # Set up basic sshd configuration:
298 if [ -n "$nosshdir" ]; then
299 rm -rf etc/ssh
300 ln -s . etc/ssh
301 [ ! -f /etc/moduli ] || { cp -p /etc/moduli etc/; chown 0:0 etc/moduli; }
302 else
303 [ ! -e etc/ssh -o -d etc/ssh ] || rm -rf etc/ssh
304 mkdir -p etc/ssh
305 [ ! -f /etc/ssh/moduli ] || { cp -p /etc/ssh/moduli etc/ssh/; chown 0:0 etc/ssh/moduli; }
307 mkdir -p var/run/sshd
308 if [ ! -s etc/ssh/sshd_config ]; then
309 cat >etc/ssh/sshd_config <<EOT
310 Protocol 2
311 Port $cfg_sshd_jail_port
312 UsePAM no
313 X11Forwarding no
314 AllowAgentForwarding no
315 AllowTcpForwarding no
316 PermitTunnel no
317 IgnoreUserKnownHosts yes
318 PrintLastLog no
319 PrintMotd no
320 UseDNS no
321 PermitRootLogin no
322 UsePrivilegeSeparation yes
324 HostKey /etc/ssh/ssh_host_rsa_key
326 if [ -z "$cfg_disable_dsa" ]; then
327 cat >>etc/ssh/sshd_config <<EOT
328 HostKey /etc/ssh/ssh_host_dsa_key
331 cat >>etc/ssh/sshd_config <<EOT
332 AuthorizedKeysFile /etc/sshkeys/%u
333 StrictModes no
335 # mob user:
336 PermitEmptyPasswords yes
337 ChallengeResponseAuthentication no
338 PasswordAuthentication yes
341 if [ ! -s etc/ssh/ssh_host_rsa_key ]; then
342 bits=2048
343 if [ "$cfg_rsakeylength" -gt "$bits" ] 2>/dev/null; then
344 bits="$cfg_rsakeylength"
346 yes | ssh-keygen -b "$bits" -t rsa -N "" -C Girocco -f etc/ssh/ssh_host_rsa_key
348 if [ -z "$cfg_disable_dsa" -a ! -s etc/ssh/ssh_host_dsa_key ]; then
349 # ssh-keygen can only create 1024 bit DSA keys
350 yes | ssh-keygen -b 1024 -t dsa -N "" -C Girocco -f etc/ssh/ssh_host_dsa_key
353 # Set the final permissions on the binaries and perform any final twiddling
354 chroot_update_permissions
356 # Change the owner of the sshd-related files
357 chown 0:0 etc/ssh/ssh_* etc/ssh/sshd_*
359 echo "--- Add to your boot scripts: mount --bind $reporoot $chroot/$jailreporoot"
360 echo "--- Add to your boot scripts: mount --bind /proc $chroot/proc"
361 echo "--- Add to your syslog configuration: listening on socket $chroot/dev/log"
362 echo "--- To restart a running jail's sshd: sudo kill -HUP \`cat $chroot/var/run/sshd.pid\`"