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