#!/bin/sh ### This script can help create chrooted environment. ### It was tested ONLY WITH APACHE (with mod_ssl) AND PERL, ### but at the end of ### it you will find big section "apllication specific". ### So if you will chroot something else (proftpd, ldap, ### or what you want) you can add aplication specific ### part to the end of this script (if needed). ### In example apache needs /etc/mime.types to be copied ### but this is NOT a part of apache package. So at the ### end script just tests if package name is apache, and copies ### this file to new chroot. ### ### It works ONLY WITH DEBIAN (because of package system), ### and was tested ONLY WITH DEBIAN 3.0 (because I have no other :-). ### Use it on your own risk. ### ### What it does. It takes two arguments: ### debian package name; ### chroot directory. ### It makes some tests and asks you about configuration. ### Then it copies some files, libraries and devices to ### new chroot (you can add or remove if needed [lines 302-309]). ### When package files are copied, script looks what libraries are ### needed. After libraries are copied it goes to application ### specific section. For apache it can create startup script ### which you may copy to /etc/init.d/apache *(real path, not ### a chroot)*, so your chrooted apache can start at startup. ### ### USAGE NOTES ### ### Usage: this_script package_name chroot_dir ### I.e. if you need chrooted apache, do: ### dpkg -l | grep apache ### and look what packages you need. ### I took apache, apache-common, libapache-mod-ssl. ### (./mkchroot apache /var/chroot/apache ### ./mkchroot apache-common /var/chroot/apache ### ./mkchroot libapache-mod-ssl /var/chroot/apache ) ### ### So you MUST run this scipt for EACH package once. ### For perl I took perl-base and perl. ### ### UPGRADES OF CHROOTED APPLICATIONS ### ### You can upgrade (apt-get upgrade package) package you need ### BUT BE SURE, YOU HAVE EXACT WORKING CONFIGURATION OUTSIDE ### THE JAIL. In other case, configuration, ssl-keys, etc. will ### be OVERWRITTEN! ### ### LICENCE. You can use, modify, distribute this script, ### but leave my name in authors section. ### ### ### Written by Martynas Domarkas (OTPABA) md@hansa.lt 2003 01 05. ### ### I DO NOT SUPPORT ANY SOFTWARE, so I'm not sure I'll answer ### your questions :-) ### ### Last note: this script does NOT check any package dependencies. ### The point is, that i.e. if you want a chrooted apache, debian package ### depends on perl. But if you find perl buggy or just hate it (like I do) ### you will not use cgi's written in perl, and no mod_perl of course. ### Then you need no perl package in your chroot! ;-) ### 2003 09 19 changed function file_list_for_tar(). There was a bug when copying ### targets of symlinks. ### Reported by Marc Lubarsky. ### 2003 09 19 added copying of /etc/hosts. CHRPACK=$1 CHROOTD=$2 clear ### Function "Print how to use" ### usage() { echo " Usage: $0 package_name chroot_dir " } ################################# ### Check for command invocation syntax ### if [ "$CHRPACK" == "" ] || [ "$CHROOTD" == "" ]; then usage # Function from above exit 1 fi ########################################### ### Ask some stupid questions, but it's better than README ;-)) ### echo " 1) Did you read what is written in head of this file? 2) Package you are chrooting is installed using debian tools and *CONFIGURED* ? (if you upgrade your system and try mkchroot again without actual configuration you currently have in chroot YOU WILL LOSE YOUR CONFIGURATION!) 3) Is this machine running Linux Debian 3.0 ? 4) Do you know what does it mean \"chroot\" at all? 5) Do you have dpkg, gawk (awk), tar, file, grep, egrep, ldd, mkdir in your PATH? If all 5 answers was \"YES!!!!\", then press ENTER. If not, better press ctrl-C and read beggining of this script. " read junk ############################################################## ### Check for user ### if [ `id -u` -ne 0 ]; then echo " You are not root. It is possible, that you will not be able access some files." echo -n " Do you want to continue? [y/n] : " read ANS if [ "$ANS" != "y" ]; then echo "OK, exiting..." exit 0 fi fi ##################### #################### FUNCTIONS #################### ### Function for checking of command existence ### ### It will be used to be sure that we can continue running this script ### cmd_exist() { which $1 > /dev/null if [ $? -ne 0 ]; then echo "Command \"$1\" not found" echo "Exiting..." exit 1 fi } ### Function for getting needed libraries for package ### ### It uses list of files given by "dpkg -L $PACKAGE" looks for executables and prints out ### file names of needed libraries get_libs_for_pack() { dpkg -L $CHRPACK | while read GLFP do if [ ! -d $GLFP ]; then file $GLFP|egrep "(ELF.*executable|ELF.*shared obj)" > /dev/null if [ $? -eq 0 ]; then ldd $GLFP | $AWK '{ print $3 }'; fi; fi; done | sort -u } ######################################################### ### Function for getting needed libraries for file ### (same as above but this takes as argument just one file get_libs_for_file() { file $1 | egrep "(ELF.*executable|ELF.*shared obj)" > /dev/null if [ $? -eq 0 ]; then ldd $1 | $AWK '{ print $3 }'; fi } ###################################################### ### Function for checking if library does NOT exist in chroot ### check_lib_for_exsist() { while read CLFN do if [ ! -e $CHROOTD/$CLFN ]; then echo $CLFN fi done } ################################################################# ### Function to prepare list of files for tar ### ### It checks for symlinks, targets and it's output is one single space separated line ### file_list_for_tar() { while read FLFT do # Check for accidental newline if [ ! -z $FLFT ]; then # You will ask me why I cycle again. It is because I like "break" more than "else" :-) file $FLFT | while read LO do # Check if the file exist if [ ! -f "$FLFT" ]; then break fi # Output filename to stdout (other functions will read this) echo -n "$FLFT " # Is it a symlink? echo $LO | grep "sym.*link" > /dev/null if [ $? -eq 0 ]; then # Symlinks often are made with realative path. So we need know WHERE the symlink is. prefdir=`dirname $FLFT` TG=`echo $LO | $AWK '{ print $NF }'` # Is the path absolute (absolute paths allways begisn with a "/") echo "$TG" | grep "^/" > /dev/null if [ $? -eq 0 ]; then TGF=$TG else # Path to target was realative - adding directory prefix. TGF=$prefdir/$TG fi # Shit happens... :-) if [ ! -f "$FLFT" ]; then break fi echo -n "$TGF " fi done fi done } ### Old one. Buggy. #file_list_for_tar() { #while read FLFT #do #file $FLFT | $AWK '{ gsub(/:$/, "", $1); print }' | while read LO # do # prefdir=`echo $LO | $AWK -F"/" '{ ORS=""; for(i=2; i /dev/null # if [ $? -eq 0 ]; then # echo $LO | $AWK '{ ORS=" "; print $1 }' # TG=`echo $LO | $AWK '{ print $NF }'` # TGF=`find "$prefdir" -name "$TG"` # echo -n "$TGF " # else # echo $LO | $AWK '{ ORS=" "; print $1 }' # fi # done #done #} ################################################################################### ### Function for copy given files to chroot ### cp_to_chroot() { read CTC if [ ! -z "$CTC" ]; then cd $CHROOTD tar cvf - $CTC | tar xf - fi } ############################################## ### Function to copy single commands (binaries) with libraries to chroot ### cp_cmd_to_chroot() { CMD=$1 if [ ! -z "$CMD" ]; then if [ -f "$CMD" ]; then get_libs_for_file $CMD | check_lib_for_exsist | file_list_for_tar | cp_to_chroot if [ ! -f $CHROOTD/$CMD ]; then cd $CHROOTD tar cvf - "$CMD" | tar xf - fi else echo "$CMD does not exist" exit 1 fi else echo "Error" exit 1 fi } ############### END OF FUNCTIONS #################### ### OK lets begin! ### ### Looking for needed software ### ### GAWK: ### which gawk > /dev/null if [ $? -ne 0 ]; then which awk > /dev/null if [ $? -ne 0 ]; then echo "gawk or awk not found in your PATH" echo "Exiting..." exit 1 else echo "Found awk instead of gawk. I'll try to use it..." AWK=`which awk` fi else #echo "gawk found. Excelent!" AWK=`which gawk` fi cmd_exist file cmd_exist ldd cmd_exist dpkg cmd_exist grep cmd_exist egrep cmd_exist mkdir cmd_exist tar cmd_exist dirname ### At this point we should be sure, that we found all software needed ### ### Looking for package beeing chrooted ### dpkg -L $CHRPACK > /dev/null 2>&1 if [ $? -ne 0 ]; then echo " dpkg can not find package \"$CHRPACK\". If you mistyped command, start $0 again with correct arguments Be sure you installed it using usual Debian way (apt-get, dpkg -i, tasksel, dselect...) Exiting..." exit 1 fi ########################################### ### Create new chroot or use an existing directory? ### if [ -e $CHROOTD ]; then if [ ! -d $CHROOTD ]; then echo "$CHROOTD exist and it is NOT a directory" echo "Exiting..." exit 1 else echo -n "$CHROOTD exist. Should I use it? [y/n] : " read ANS if [ "$ANS" != "y" ]; then echo "Exiting..." exit 1 fi fi else mkdir -p $CHROOTD fi ######################################################## ### Minimal set of files so you can do 'chroot /your/dir' ### ### (ls, cat, tail, strace, less is also added for debuging) ### echo " Creating minimal chroot environment... " cp_cmd_to_chroot /bin/sh cp_cmd_to_chroot /bin/bash cp_cmd_to_chroot /bin/ls cp_cmd_to_chroot /bin/cat cp_cmd_to_chroot /usr/bin/strace cp_cmd_to_chroot /usr/bin/less cp_cmd_to_chroot /usr/bin/tail ### Little stupid hack. Without "libnss_files.so" apache can not read /etc/passwd for "User" directive ### but no binaries or modules ar linked with it... echo /lib/libnss_files.so.2 | check_lib_for_exsist | file_list_for_tar | cp_to_chroot ### Some directories and files that we will need ### mkdir -p $CHROOTD/var/run mkdir -p $CHROOTD/var/lock mkdir -p $CHROOTD/var/log mkdir -p $CHROOTD/proc mkdir -p $CHROOTD/dev mkdir -p $CHROOTD/etc cp -apR /dev/null $CHROOTD/dev cp -apR /dev/random $CHROOTD/dev cp -apR /dev/urandom $CHROOTD/dev cp -apR /dev/zero $CHROOTD/dev cp -ap /etc/hosts $CHROOTD/etc ############## END OF MINIMAL CHROOT #################### ### We also need an /etc/passwd and /etc/group ### ### At this moment we add user root to $CHROOTD/etc/passwd ### and group root to $CHROOTD/etc/group ### You will be asked later for application specific user and group if [ ! -f $CHROOTD/etc/passwd ]; then grep "^root:" /etc/passwd > $CHROOTD/etc/passwd else grep "^root:" $CHROOTD/etc/passwd > /dev/null if [ $? -ne 0 ]; then echo "No entry for user root found in your chrooted /etc/passwd" echo -n "Do you want me to add one? [y/n] : " read ANS if [ "$ANS" != "y" ]; then echo "OK, no root - no problems" else grep "^root:" /etc/passwd >> $CHROOTD/etc/passwd fi fi fi if [ ! -f $CHROOTD/etc/group ]; then grep "^root:" /etc/group > $CHROOTD/etc/group else grep "^root:" $CHROOTD/etc/group > /dev/null if [ $? -ne 0 ]; then echo "No entry for user root found in your chrooted /etc/group" echo -n "Do you want me to add one? [y/n] : " read ANS if [ "$ANS" != "y" ]; then echo "OK, no root - no problems" else grep "^root:" /etc/group >> $CHROOTD/etc/group fi fi fi ################################################## ### We have passwd and group files. Now we need copy some system users and groups to jail ### while [ true ] do sleep 1 clear echo " You will need a part of /etc/passwd for your software to run. In most cases apache run under www-data, bind under bind. But I have no idea what is your setup. So, now you can enter username you need in chroot and press ENTER. If you DO NOT need any user enter NoMore. " echo -n "User to add to $CHROOTD/etc/passwd : " read USRN if [ ! -z "$USRN" ]; then if [ "$USRN" != "NoMore" ]; then grep "^$USRN:" /etc/passwd > /dev/null if [ $? -eq 0 ]; then grep "^$USRN:" $CHROOTD/etc/passwd 1>/dev/null 2>/dev/null if [ $? -ne 0 ]; then grep "^$USRN:" /etc/passwd >> $CHROOTD/etc/passwd echo "" echo -e "\tUser $USRN added" echo "" echo -e "\tPress ENTER to continue" read junk else echo "" echo -e "\t$USRN already is in your $CHROOTD/etc/passwd" echo "" echo -e "\tPress ENTER to continue" read junk fi else echo "" echo -e "\t$USRN does not exist in your system" echo "" echo -e "\tPress ENTER to continue" read junk fi else echo "OK... Continuing" break fi else echo -e " \tEnter an existing system user or NoMore if you wan't quit adding users to chroot" echo -e " \tPress ENTER to continue" read junk fi done while [ true ] do sleep 1 clear echo " OK, for we have user file. Now repeat the same with groups. Now you can enter groupname you need in chroot and press ENTER. If you DO NOT need any group enter NoMore. " echo -n "Group to add to $CHROOTD/etc/group : " read GRPN if [ ! -z "$GRPN" ]; then if [ "$GRPN" != "NoMore" ]; then grep "^$GRPN:" /etc/group > /dev/null if [ $? -eq 0 ]; then grep "^$GRPN:" $CHROOTD/etc/group 1>/dev/null 2>/dev/null if [ $? -ne 0 ]; then grep "^$GRPN:" /etc/group >> $CHROOTD/etc/group echo "" echo -e "\tGroup $GRPN added" echo "" echo -e "\tPress ENTER to continue" read junk else echo "" echo -e "\t$GRPN already is in your $CHROOTD/etc/group" echo "" echo -e "\tPress ENTER to continue" read junk fi else echo "" echo -e "\t$GRPN does not exist in your system" echo "" echo -e "\tPress ENTER to continue" read junk fi else echo "OK... Continuing" break fi else echo -e " \tEnter an existing system group or NoMore if you wan't quit adding groups to chroot" echo -e " \tPress ENTER to continue" read junk fi done ################################################################################## ### Package files. Taken from dpkg -L $PACKAGE without doc's and manuals ### echo " Copying package files to $CHROOTD: " cd $CHROOTD LIST=`dpkg -L $CHRPACK | grep -v "share/[man|doc]" | while read F do if [ ! -d $F ]; then echo -n "$F " else if [ ! -d $CHROOTD/$F ]; then mkdir -p $CHROOTD/$F > /dev/null 2>&1 fi fi done` tar cvf - $LIST| tar xf - ######################################### echo " Copying libraries needed by $CHRPACK... " get_libs_for_pack | check_lib_for_exsist | file_list_for_tar | cp_to_chroot ### Package specific... APACHE ### if [ "$CHRPACK" = "apache" ]; then if [ ! -d $CHROOTD/etc/apache ]; then cd $CHROOTD tar cvf - /etc/apache | tar xf - else cp -apuRi /etc/apache/* $CHROOTD/etc/apache fi if [ ! -f $CHROOTD/etc/mime.types ]; then cp -ap /etc/mime.types $CHROOTD/etc fi echo " If you are running Debian 3.0, I can try provide startup script for you. It will be placed as /tmp/chrooted.apache.spartup.sh. You will need only chmod 750 /tmp/chrooted.apache.spartup.sh and /tmp/chrooted.apache.spartup.sh start " echo -n "Do you want try this script? [y/n] : " read ANS if [ "$ANS" = "y" ]; then STARTUPFILE=/tmp/chrooted.apache.spartup.sh rm $STARTUPFILE echo "#!/bin/bash" >> $STARTUPFILE echo "#" >> $STARTUPFILE echo "# apache Start the apache HTTP server." >> $STARTUPFILE echo "#" >> $STARTUPFILE echo "" >> $STARTUPFILE echo "CHRDIR=$CHROOTD" >> $STARTUPFILE echo "" >> $STARTUPFILE echo "NAME=apache" >> $STARTUPFILE echo "PATH=/bin:/usr/bin:/sbin:/usr/sbin" >> $STARTUPFILE echo "DAEMON=/usr/sbin/apache" >> $STARTUPFILE echo "SUEXEC=/usr/lib/apache/suexec" >> $STARTUPFILE echo "PIDFILE=/var/run/\$NAME.pid" >> $STARTUPFILE echo "CONF=/etc/apache/httpd.conf" >> $STARTUPFILE echo "APACHECTL=/usr/sbin/apachectl " >> $STARTUPFILE echo "" >> $STARTUPFILE echo "trap \"\" 1" >> $STARTUPFILE echo "export LANG=C" >> $STARTUPFILE echo "export PATH" >> $STARTUPFILE echo "" >> $STARTUPFILE echo "test -f \$DAEMON || exit 0" >> $STARTUPFILE echo "test -f \$APACHECTL || exit 0" >> $STARTUPFILE echo "" >> $STARTUPFILE echo "# ensure we don't leak environment vars into apachectl" >> $STARTUPFILE echo "APACHECTL=\"env -i LANG=\${LANG} PATH=\${PATH} \$APACHECTL\"" >> $STARTUPFILE echo "" >> $STARTUPFILE echo "if egrep -q -i \"^[[:space:]]*ServerType[[:space:]]+inet\" \$CONF" >> $STARTUPFILE echo "then" >> $STARTUPFILE echo " exit 0" >> $STARTUPFILE echo "fi" >> $STARTUPFILE echo "" >> $STARTUPFILE echo "case \"\$1\" in" >> $STARTUPFILE echo " start)" >> $STARTUPFILE echo " echo -n \"Starting web server: \$NAME\"" >> $STARTUPFILE echo " mount -t proc proc $CHROOTD/proc" >> $STARTUPFILE echo " start-stop-daemon --start --pidfile \$PIDFILE --exec \$DAEMON --chroot \$CHRDIR" >> $STARTUPFILE echo " ;;" >> $STARTUPFILE echo "" >> $STARTUPFILE echo " stop)" >> $STARTUPFILE echo " echo -n \"Stopping web server: \$NAME\"" >> $STARTUPFILE echo " start-stop-daemon --stop --pidfile \$CHRDIR/\$PIDFILE --oknodo" >> $STARTUPFILE echo " umount $CHROOTD/proc" >> $STARTUPFILE echo " ;;" >> $STARTUPFILE echo "" >> $STARTUPFILE echo " reload)" >> $STARTUPFILE echo " echo -n \"Reloading \$NAME configuration\"" >> $STARTUPFILE echo " start-stop-daemon --stop --pidfile \$CHRDIR/\$PIDFILE --signal USR1 --exec \$DAEMON --chroot \$CHRDIR" >> $STARTUPFILE echo " ;;" >> $STARTUPFILE echo "" >> $STARTUPFILE echo " reload-modules)" >> $STARTUPFILE echo " echo -n \"Reloading \$NAME modules\"" >> $STARTUPFILE echo " start-stop-daemon --stop --pidfile \$CHRDIR/\$PIDFILE --oknodo --retry 30" >> $STARTUPFILE echo " start-stop-daemon --start --pidfile \$PIDFILE --exec \$DAEMON --chroot \$CHRDIR" >> $STARTUPFILE echo " ;;" >> $STARTUPFILE echo "" >> $STARTUPFILE echo " restart)" >> $STARTUPFILE echo " \$0 reload-modules" >> $STARTUPFILE echo " exit \$?" >> $STARTUPFILE echo " ;;" >> $STARTUPFILE echo "" >> $STARTUPFILE echo " force-reload)" >> $STARTUPFILE echo " \$0 reload-modules" >> $STARTUPFILE echo " exit \$?" >> $STARTUPFILE echo " ;;" >> $STARTUPFILE echo "" >> $STARTUPFILE echo " *)" >> $STARTUPFILE echo " echo \"Usage: /etc/init.d/\$NAME {start|stop|reload|reload-modules|force-reload|restart}\"" >> $STARTUPFILE echo " exit 1" >> $STARTUPFILE echo " ;;" >> $STARTUPFILE echo "esac" >> $STARTUPFILE echo "" >> $STARTUPFILE echo "if [ \$? == 0 ]; then" >> $STARTUPFILE echo " echo ." >> $STARTUPFILE echo " exit 0" >> $STARTUPFILE echo "else" >> $STARTUPFILE echo " echo failed" >> $STARTUPFILE echo " exit 1" >> $STARTUPFILE echo "fi" >> $STARTUPFILE fi fi