jailsetup: refactor jailsetup.sh chroot setup
authorKyle J. McKay <mackyle@gmail.com>
Sat, 24 Aug 2013 01:49:27 +0000 (23 18:49 -0700)
committerKyle J. McKay <mackyle@gmail.com>
Sat, 24 Aug 2013 01:49:27 +0000 (23 18:49 -0700)
Move the system-specific portion of the chroot setup into a separate
script with a system-specific name and then call it from the normal
jailsetup.sh script.  This permits chroot setups for different system
types to be more easily supported in the future.

chrootsetup_linux.sh [new file with mode: 0644]
jailsetup.sh

diff --git a/chrootsetup_linux.sh b/chrootsetup_linux.sh
new file mode 100644 (file)
index 0000000..f58523d
--- /dev/null
@@ -0,0 +1,69 @@
+# chrootsetup_linux.sh
+
+# This file SHOULD NOT be executable!  It is sourced by jailsetup.sh and
+# SHOULD NOT be executed directly!
+
+# On entry the current directory will be set to the top of the chroot
+# This script must perform platform-specific chroot setup which includes
+# creating any dev device entries, setting up proc (if needed), setting
+# up lib64 (if needed) as well as installing a basic set of whatever libraries
+# are needed for a chroot to function on this platform.
+
+# This script must also define a pull_in_bin function that may be called to
+# install an executable together with any libraries it depends on into the
+# chroot.
+
+# Finally this script must install a suitable nc.openbsd compatible version of
+# netcat into the chroot jail that's available as nc.openbsd and which supports
+# connects to unix sockets.
+
+# We are designed to set up the chroot based on binaries from
+# amd64 Debian lenny; some things may need slight modifications if
+# being run on a different distribution.
+
+mkdir -p dev proc selinux
+chown 0:0 proc selinux
+rm -f lib64
+ln -s lib lib64
+
+# Seed up /dev:
+rm -f dev/null dev/zero dev/random dev/urandom
+mknod dev/null c 1 3
+mknod dev/zero c 1 5
+mknod dev/random c 1 8
+mknod dev/urandom c 1 9
+chmod a+rw dev/null dev/zero dev/random dev/urandom
+
+# Extra directories
+mkdir -p var/empty var/run/sshd
+chmod 0444 var/empty
+
+# Bring in basic libraries:
+rm -f lib/*
+# ld.so:
+cp -p -t lib /lib/ld-linux.so.2
+[ ! -d /lib64 ] || cp -p -t lib /lib64/ld-linux-x86-64.so.2
+# libc:
+cp -p -t lib /lib/libc.so.6 /lib/libcrypt.so.1 /lib/libutil.so.1 /lib/libnsl.so.1 /lib/libnss_compat.so.2 /lib/libresolv.so.2 /lib/libdl.so.2 /lib/libgcc_s.so.1
+
+# pull_in_bin takes two arguments:
+# 1: the full path to a binary to pull in (together with any library dependencies)
+# 2: the destination directory relative to the current directory to copy it to
+# for example, "pull_in_bin /bin/sh bin" will install the shell into the chroot bin directory
+# IMPORTANT: argument 1 must be a machine binary, NOT a shell script or other interpreted text
+# IMPORTANT: text scripts can simply be copied in or installed as they don't have libraries to copy
+# NOTE: it's expected that calling this function on a running chroot may cause temporary disruption
+pull_in_bin() {
+       bin="$1"; dst="$2"
+       cp -p -t "$dst" "$bin"
+       # ...and all the dependencies.
+       ldd "$bin" | grep -v linux-gate | grep -v linux-vdso | grep -v ld-linux | grep '=>' | awk '{print $3}' | xargs -r -- cp -p -u -t lib
+}
+
+# A catch all that needs to be called after everything's been pulled in
+chroot_update_permissions() {
+       chown -R 0:0 bin dev lib sbin var
+}
+
+# nothing special here, the nc.openbsd compatible utility is nc.openbsd
+pull_in_bin /bin/nc.openbsd bin
index 309260b..37582cd 100755 (executable)
@@ -3,21 +3,43 @@
 #
 # If the first parameter is "dbonly", setup the database only
 #
-# We are designed to set up the chroot based on binaries from
-# amd64 Debian lenny; some things may need slight modifications if
-# being run on a different distribution.
+# We are designed to set up the chroot based on the output of
+# `uname -s` by sourcing a suitable system-specific script.
+# Unrecognized systems will generate an error.  When using
+# "dbonly" the setup of the chroot binaries is skipped so the
+# output of `uname -s` does not matter in that case.
 
 set -e
 
+curdir="`pwd`"
+srcdir="$curdir/src"
+getent="$srcdir/getent"
 . ./shlib.sh
 
 dbonly=''
 [ "$1" != "dbonly" ] || dbonly=1
 
-reserved_users="root sshd mob $cfg_cgi_user $cfg_mirror_user"
+reserved_users="root sshd _sshd mob $cfg_cgi_user $cfg_mirror_user"
+
+# Require either sshd or _sshd user unless "dbonly"
+sshd_user=sshd
+if ! "$getent" passwd sshd >/dev/null && ! "$getent" passwd _sshd >/dev/null; then
+       if [ -n "$dbonly" ]; then
+               if [ ! -s etc/passwd ]; then
+                       # Only complain on initial etc/passwd creation
+                       echo "WARNING: no sshd or _sshd user, omitting entries from chroot etc/passwd"
+               fi
+               sshd_user=
+       else
+               echo "*** Error: You do not have required sshd or _sshd user in system." >&2
+               exit 1
+       fi
+else
+       "$getent" passwd sshd >/dev/null || sshd_user=_sshd
+fi
 
-# Verify we have all we neeed.
-if ! getent passwd "$cfg_mirror_user" >/dev/null; then
+# Verify we have all we need
+if ! "$getent" passwd "$cfg_mirror_user" >/dev/null; then
        echo "*** Error: You do not have \"$cfg_mirror_user\" user in system yet." >&2
        exit 1
 fi
@@ -38,19 +60,29 @@ cd "$cfg_chroot"
 chmod 755 "$cfg_chroot" ||
        echo "WARNING: Cannot chmod $cfg_chroot"
 
-# Set up basic user/group configuration; if there is any already,
-# we hope it's the same numbers and users.
-
+# Set up basic user/group configuration; if there isn't any already
 mobpass=''
 [ -n "$cfg_mob" ] || mobpass='x'
 mkdir -p etc
 if [ ! -s etc/passwd ]; then
        cat >etc/passwd <<EOT
-sshd:x:101:65534:priviledge separation:/var/run/sshd:/bin/false
-$cfg_cgi_user:x:$(getent passwd "$cfg_cgi_user" | cut -d : -f 3-5):/:/bin/true
-$cfg_mirror_user:x:$(getent passwd "$cfg_mirror_user" | cut -d : -f 3-5):/:/bin/true
-mob:$mobpass:65538:$(getent group "$cfg_owning_group" | cut -d : -f 3):the mob:/:/bin/git-shell-verify
+root:x:0:0:system administrator:/var/empty:/bin/false
+EOT
+       [ -z "$sshd_user" ] || cat >>etc/passwd <<EOT
+sshd:x:$("$getent" passwd $sshd_user | cut -d : -f 3-4):privilege separation:/var/empty:/bin/false
+_sshd:x:$("$getent" passwd $sshd_user | cut -d : -f 3-4):privilege separation:/var/empty:/bin/false
 EOT
+       cat >>etc/passwd <<EOT
+$cfg_cgi_user:x:$("$getent" passwd "$cfg_cgi_user" | cut -d : -f 3-5):/:/bin/true
+$cfg_mirror_user:x:$("$getent" passwd "$cfg_mirror_user" | cut -d : -f 3-5):/:/bin/true
+mob:$mobpass:65538:$("$getent" group "$cfg_owning_group" | cut -d : -f 3):the mob:/:/bin/git-shell-verify
+EOT
+elif [ -z "$dbonly" ]; then
+       # Make sure an sshd entry is present
+       if ! grep -q '^sshd:' etc/passwd; then
+               echo "*** Error: chroot etc/passwd exists but lacks sshd entry." >&2
+               exit 1
+       fi
 fi
 
 if [ ! -s etc/group ]; then
@@ -61,7 +93,7 @@ fi
 
 mkdir -p etc/sshkeys etc/sshcerts
 for ruser in $reserved_users; do
-  touch etc/sshkeys/$ruser
+       touch etc/sshkeys/$ruser
 done
 chgrp $cfg_owning_group etc etc/sshkeys etc/sshcerts ||
        echo "WARNING: Cannot chgrp $cfg_owning_group the etc directories"
@@ -80,28 +112,79 @@ chmod -R g+w etc/sshkeys etc/sshcerts 2>/dev/null ||
 
 [ -z "$dbonly" ] || exit 0
 
-chown root "$cfg_chroot"
-chown $cfg_cgi_user etc etc/passwd etc/group
-chown -R $cfg_cgi_user etc/sshkeys etc/sshcerts
+# Make sure the system type is supported for chroot
+sysname="$(uname -s | tr A-Z a-z || :)"
+: ${sysname:=linux}
+nosshdir=
+# These equivalents may need to be expanded at some point
+case "$sysname" in
+       *kfreebsd*)
+               sysname=linux;;
+       *darwin*)
+               sysname=darwin;;
+       *freebsd*)
+               sysname=freebsd;;
+       *linux*)
+               sysname=linux;;
+esac
+
+chrootsetup="$curdir/chrootsetup_$sysname.sh"
+if ! [ -r "$chrootsetup" -a -s "$chrootsetup" ]; then
+       echo "*** Error: $chrootsetup not found"
+       echo "*** Error: creating a chroot for a `uname -s` system is not supported"
+       exit 1
+fi
 
-# First, setup basic directory structure
-mkdir -p bin dev etc lib sbin ${cfg_jailreporoot#/} var/run proc
-rm -f usr lib64
+# Set the user and group on the top of the chroot before creating anything else
+chown 0:0 "$cfg_chroot"
+
+# First, setup basic platform-independent directory structure
+mkdir -p bin dev etc lib sbin var/empty var/run ${cfg_jailreporoot#/}
+rm -rf usr
 ln -s . usr
-ln -s lib lib64
 
-# Seed up /dev:
-rm -f dev/null dev/zero dev/random dev/urandom
-mknod dev/null c 1 3
-mknod dev/zero c 1 5
-mknod dev/random c 1 8
-mknod dev/urandom c 1 9
-chmod a+rw dev/null dev/zero dev/random dev/urandom
+# Now source the platform-specific script that is responsible for dev device
+# setup, proc setup (if needed), lib64 setup (if needed) and basic library
+# installation to make a chroot operational.  Additionally it will define a
+# pull_in_bin function that can be used to add executables and their library
+# dependencies to the chroot and finally will install a suitable nc.openbsd
+# compatible version of netcat that supports connections to unix sockets.
+. "$chrootsetup"
 
-# Set up sshd configuration:
-mkdir -p var/run/sshd
+# Now, bring in sshd, sh etc.
+# The $chrootsetup script should have already provided a suitable nc.openbsd
+install -p "$cfg_basedir/bin/git-shell-verify" bin
+pull_in_bin "$cfg_basedir/bin/can_user_push" bin
+pull_in_bin /bin/sh bin
+pull_in_bin /bin/date bin
+pull_in_bin /bin/mv bin
+pull_in_bin /bin/rm bin
+# If /sbin/sshd is already running within the chroot, we get Text file busy
+pull_in_bin /usr/sbin/sshd sbin || :
 
-mkdir -p etc/ssh
+# ...and the bits of git we need,
+# being sure to use the configured git and its --exec-path to find the pieces
+git_exec_path="$("$cfg_git_bin" --exec-path)"
+for i in git git-index-pack git-receive-pack git-shell git-update-server-info git-upload-archive \
+       git-upload-pack git-unpack-objects git-show-ref git-config git-for-each-ref; do
+       pull_in_bin "$git_exec_path/$i" bin
+done
+
+# Update permissions on the database files
+chown $cfg_cgi_user:$cfg_owning_group etc etc/passwd etc/group
+chown -R $cfg_cgi_user:$cfg_owning_group etc/sshkeys etc/sshcerts
+
+# Set up basic sshd configuration:
+if [ -n "$nosshdir" ]; then
+       rm -rf etc/ssh
+       ln -s . etc/ssh
+       [ ! -f /etc/moduli ] || { cp -p /etc/moduli etc/; chown 0:0 etc/moduli; }
+else
+       [ ! -e etc/ssh -o -d etc/ssh ] || rm -rf etc/ssh
+       mkdir -p etc/ssh
+       [ ! -f /etc/ssh/moduli ] || { cp -p /etc/ssh/moduli etc/ssh/; chown 0:0 etc/ssh/moduli; }
+fi
+mkdir -p var/run/sshd
 if [ ! -s etc/ssh/sshd_config ]; then
        cat >etc/ssh/sshd_config <<EOT
 Protocol       2
@@ -127,44 +210,11 @@ if [ ! -s etc/ssh/ssh_host_rsa_key ]; then
         yes | ssh-keygen -N "" -C Girocco -t rsa -f etc/ssh/ssh_host_rsa_key
 fi
 
-# Bring in basic libraries:
-rm -f lib/*
-# ld.so:
-cp -p -t lib /lib/ld-linux.so.2
-[ ! -d /lib64 ] || cp -p -t lib /lib64/ld-linux-x86-64.so.2
-# libc:
-cp -p -t lib /lib/libc.so.6 /lib/libcrypt.so.1 /lib/libutil.so.1 /lib/libnsl.so.1 /lib/libnss_compat.so.2 /lib/libresolv.so.2 /lib/libdl.so.2 /lib/libgcc_s.so.1
-
-# Now, bring in sshd and sh.
-
-pull_in_bin() {
-       bin="$1"; dst="$2"
-       cp -p -t "$dst" "$bin"
-       # ...and all the dependencies.
-       ldd "$bin" | grep -v linux-gate | grep -v linux-vdso | grep -v ld-linux | grep '=>' | awk '{print $3}' | xargs -r -- cp -p -u -t lib
-}
-
-install -p "$cfg_basedir/bin/git-shell-verify" bin
-install -p "$cfg_basedir/bin/can_user_push" bin
-pull_in_bin /bin/sh bin
-pull_in_bin /bin/nc.openbsd bin
-pull_in_bin /bin/date bin
-pull_in_bin /bin/mv
-pull_in_bin /bin/rm
-# If /sbin/sshd is already running within the chroot, we get Text file busy.
-pull_in_bin /usr/sbin/sshd sbin || :
+# Set the final permissions on the binaries and perform any final twiddling
+chroot_update_permissions
 
-# ...and the bits of git we need.
-for i in git git-index-pack git-receive-pack git-shell git-update-server-info git-upload-archive \
-       git-upload-pack git-unpack-objects git-show-ref git-config git-for-each-ref; do
-       if [ -e /usr/lib/git-core/$i ]; then
-               pull_in_bin /usr/lib/git-core/$i bin
-       elif [ -e /usr/libexec/git-core/$i ]; then
-               pull_in_bin /usr/libexec/git-core/$i bin
-       else
-               pull_in_bin /usr/bin/$i bin
-       fi
-done
+# Change the owner of the sshd-related files
+chown 0:0 etc/ssh/ssh_* etc/ssh/sshd_*
 
 echo "--- Add to your boot scripts: mount --bind $cfg_reporoot $cfg_chroot/$cfg_jailreporoot"
 echo "--- Add to your boot scripts: mount --bind /proc $cfg_chroot/proc"