====== Isolated Nginx & PHP installation script ====== {{tag>nginx php bash debian}} This script install a chrooted nginx and php environment on a specific folder of the system. Is has been developped and tested on Debian Squeeze 64 bits. It requires that the nginx, php5-cgi, spawn-fcgi packages are installed. In addition, if you want mysql support, install php5-mysql before launching the script. If you need help, want to report bug or submit improvements, please post a comment at the bottom of the page. #! /bin/bash set -e #### # Isolated Nginx & PHP Installation script # Julien Vehent - 20110418 # http://wiki.linuxwall.info # Distributed under GPL V2 licence #### # The main container. change it to match your configuration. chroot_container=/var/www/ # this function copies shared libraries from a binary to the chroot # stolen from nixcraft and then improved a bit ;) libs_to_chroot(){ local d="$1" # JAIL ROOT local pFILE="$2" # copy bin file libs local files="" local _cp="/bin/cp" # get rid of blanks and (0x00007fff0117f000) files="$(ldd $pFILE | awk '{ print $3 }' | sed -e '/^$/d' -e '/(*)$/d')" for i in $files do dcc="${i%/*}" # get dirname only [ ! -d ${d}${dcc} ] && mkdir -p ${d}${dcc} # create missing dirs [ ! -e ${d}${dcc}${i} ] && ${_cp} -f $i ${d}${dcc} # copy missing only # recursively get shared libraries of shared library libs_to_chroot "${d}" "${i}" done # Works with 32 and 64 bit ld-linux sldl="$(ldd $pFILE | grep 'ld-linux' | awk '{ print $1}')" sldlsubdir="${sldl%/*}" #[ ! -f ${d}${sldl} ] && ${_cp} -f ${sldl} ${d}${sldlsubdir} if [ ! -f ${d}${sldl} ]; then ${_cp} -f ${sldl} ${d}${sldlsubdir} fi } install_env() { local target="$1" mkdir -p $chroot_container$target/{etc/init.d,dev,var/log/nginx,var/run,var/www,var/tmp,var/lib/nginx,usr/sbin,usr/bin,usr/share,bin,sbin,tmp,lib/lsb,lib64,usr/lib} chmod 1777 $chroot_container$target/var/tmp/ mknod -m 0666 $chroot_container$target/dev/null c 1 3 mknod -m 0666 $chroot_container$target/dev/random c 1 8 mknod -m 0666 $chroot_container$target/dev/urandom c 1 9 mknod -m 0666 $chroot_container$target/dev/zero c 1 5 # create a user and a group that have the same name as target groupadd $target useradd -d $chroot_container$target/var/www -g $target -M -N -s /bin/false $target # copy startup program #cp -f /sbin/start-stop-daemon $chroot_container$target/sbin/ #cp -f /lib/lsb/init-functions $chroot_container$target/lib/lsb/ # copy ln to recreate symbolic links inside the chroot cp -f /bin/ln $chroot_container$target/bin/ } install_nginx(){ local target="$1" local nginx_bin=`which nginx` cp -f $nginx_bin $chroot_container$target$nginx_bin cp -fr /etc/nginx $chroot_container$target/etc/ #copy shared libs to target libs_to_chroot "${chroot_container}${target}" "${nginx_bin}" cp -f /lib/libnss_compat.so.2 $chroot_container$target/lib/ cp -f /lib/libnsl.so.1 $chroot_container$target/lib/ cp -f /lib/libnss_nis.so.2 $chroot_container$target/lib/ cp -f /lib/libnss_files.so.2 $chroot_container$target/lib/ cp -fr /etc/{nsswitch.conf,services,hosts*,localtime,protocols,ld.so.cache,ld.so.conf,ld.so.conf.d,host.conf} $chroot_container$target/etc/ #we just give the chroot the user it needs, that is root and its own head -n 1 /etc/passwd > $chroot_container$target/etc/passwd tail -n 1 /etc/passwd >> $chroot_container$target/etc/passwd head -n 1 /etc/group > $chroot_container$target/etc/group tail -n 1 /etc/group >> $chroot_container$target/etc/group # change user ownership chown -R $target:$target $chroot_container$target/var/www/ # install basic configuration echo "user $target $target; worker_processes 1; error_log /var/log/nginx/error.log; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; access_log /var/log/nginx/access.log; sendfile on; keepalive_timeout 65; tcp_nodelay on; gzip on; gzip_disable \"MSIE [1-6]\.(?!.*SV1)\"; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; } " > $chroot_container$target/etc/nginx/nginx.conf # install basic virtual host echo "Nginx can be configured to accept requests from 127.0.0.1 only (coming from a proxy or load balancer)" echo -n "Do you want to allow 127.0.0.1 only and deny all (can be changed in site config) ? y/n > " read response if [ $response == "y" ] then echo "server { listen 127.0.0.1:$nginxport;"> $chroot_container$target/etc/nginx/sites-available/default else echo "server { listen $nginxport;"> $chroot_container$target/etc/nginx/sites-available/default fi echo " server_name $fqdn; access_log /var/log/nginx/$fqdn.access.log; root /var/www; index index.cgi index.php index.html index.htm;">>$chroot_container$target/etc/nginx/sites-available/default if [ $phpsupport -eq 1 ] then echo " location ~ \.php$ { include fastcgi_params; fastcgi_pass 127.0.0.1:$phpport; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; }">> $chroot_container$target/etc/nginx/sites-available/default fi if [ $fcgiwrapsupport -eq 1 ] then echo " location ~ \.cgi { include fastcgi_params; fastcgi_pass unix:/var/run/fcgiwrap.sock; fastcgi_index index.cgi; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; }">>$chroot_container$target/etc/nginx/sites-available/default fi echo " }" >> $chroot_container$target/etc/nginx/sites-available/default # test the setup chroot $chroot_container$target/ $nginx_bin -t echo "nginx setup finished." } install_php(){ aptitude install php5-cgi spawn-fcgi local spawnfcgibin=`which spawn-fcgi` local php_bin=`which php5-cgi` if [ ! -x $chroot_container$target$spawnfcgibin ] then cp -f $spawnfcgibin $chroot_container$target$spawnfcgibin libs_to_chroot "${chroot_container}${target}" "$spawnfcgibin" fi cp -f $php_bin $chroot_container$target$php_bin cp -fr /etc/php5 $chroot_container$target/etc/ cp -fr /usr/lib/php5 $chroot_container$target/usr/lib/ cp -fr /usr/share/zoneinfo $chroot_container$target/usr/share/ libs_to_chroot "${chroot_container}${target}" "${php_bin}" echo "php setup finished." } install_mysql(){ aptitude update 1>/dev/null aptitude install php5-mysql cp -fr /etc/php5 $chroot_container$target/etc/ cp -fr /usr/lib/php5 $chroot_container$target/usr/lib/ local mysql_lib=`find /usr/lib/php5/ -iname mysql.so` libs_to_chroot "${chroot_container}${target}" "${mysql_lib}" find /lib -iname libgcc_s.so* -exec cp -f {} $chroot_container$target/lib/ \; echo "php5-mysql setup finished." } install_fcgiwrap(){ aptitude update 1>/dev/null aptitude install fcgiwrap spawn-fcgi fcgiwrap_bin=`which fcgiwrap` cp -f $fcgiwrap_bin $chroot_container$target$fcgiwrap_bin libs_to_chroot "${chroot_container}${target}" "${fcgiwrap_bin}" spawnfcgibin=`which spawn-fcgi` if [ ! -x $chroot_container$target$spawnfcgibin ] then cp -f $spawnfcgibin $chroot_container$target$spawnfcgibin libs_to_chroot "${chroot_container}${target}" "$spawnfcgibin" fi } install_perl(){ local perl_bin=`which perl` #copy perl binary cp -f $perl_bin $chroot_container$target$perl_bin echo "copying all included perl modules from @INC, be patient (~5/10min)" for INC in $(perl -e 'foreach(@INC){print "$_\n";}') do if [ "$INC" != "." ] then if [ -h "$INC" ] then DESTLINK=$(readlink -f $INC) mkdir -p $chroot_container$target$DESTLINK cp -fr $DESTLINK/* $chroot_container$target$DESTLINK/ chroot $chroot_container$target ln -s $DESTLINK $INC else if [ -d "$INC" ] then mkdir -p $chroot_container$target$INC cp -fr $INC $chroot_container$target$INC for perl_lib in $(find $INC -iname '*.so' -exec ls {} \;) do libs_to_chroot "${chroot_container}${target}" "${perl_lib}" done fi fi fi done #copy dynamic libs libs_to_chroot "${chroot_container}${target}" "${perl_bin}" echo "perl setup finished." } install_mojolicious(){ # install chroot env, then move there and issue mojolicious install cp $(which bash) ${chroot_container}${target}$(which bash) libs_to_chroot "${chroot_container}${target}" $(which bash) cp $(which env) ${chroot_container}${target}$(which env) libs_to_chroot "${chroot_container}${target}" $(which env) cp $(which curl) ${chroot_container}${target}$(which curl) libs_to_chroot "${chroot_container}${target}" $(which curl) curl -s -L cpanmin.us | perl - Mojolicious --local-lib=/tmp-${target}-mojolicious/ cp -r /tmp/${target}-mojolicious/lib/perl5/* ${chroot_container}${target}/usr/lib/perl5/ cp -r /tmp/${target}-mojolicious/bin/* ${chroot_container}${target}/usr/bin/ rm -rf /tmp/${target}-mojolicious/ echo "mojolicious setup finished." } create_supervised_config(){ aptitude update 1>/dev/null aptitude install daemontools daemontools-run mkdir -p /etc/sv/spawn-fcgi-$target echo "#! /bin/bash CHROOTBIN=/usr/sbin/chroot CHROOTPATH=$chroot_container$target SPAWNBIN=`which spawn-fcgi` PHP5BIN=`which php5-cgi` PHP5PORT=$phpport PHP5ADDR=127.0.0.1 USER=$target exec \$CHROOTBIN \$CHROOTPATH \$SPAWNBIN -n -a \$PHP5ADDR -p \$PHP5PORT -u \$USER -g \$USER -C 3 \$PHP5BIN " > /etc/sv/spawn-fcgi-$target/run chmod +x /etc/sv/spawn-fcgi-$target/run update-service --add /etc/sv/spawn-fcgi-$target spawn-fcgi-$target } create_control(){ echo "#! /bin/sh # ### BEGIN INIT INFO # Provides: control-$target # Required-Start: $syslog # Required-Stop: $syslog # Should-Start: $local_fs # Should-Stop: $local_fs # Default-Start: 2 # Default-Stop: 0 1 6 # Short-Description: starts/stops $target # Description: starts and stops $target # a webserver chroot for $fqdn # ### END INIT INFO CHROOTBIN=/usr/sbin/chroot CHROOTPATH=$chroot_container$target NGINXDAEMON=`which nginx` NAME=nginx PHP5SUPPORT=$phpsupport SPAWNBIN=`which spawn-fcgi` PHP5BIN=`which php5-cgi` PHP5PORT=$phpport PHP5ADDR=127.0.0.1 PHPSUPERVISE=$phpsupervise FCGIWRAPSUPPORT=$fcgiwrapsupport FCGIWRAPSOCK=/var/run/fcgiwrap.sock FCGIWRAPBIN=`which fcgiwrap` USER=$target DESC='chroot webserver (nginx/php) for $fqdn' set -e . /lib/lsb/init-functions test_nginx_config() { if \$CHROOTBIN \$CHROOTPATH -t \$NGINXDAEMON_OPTS >/dev/null 2>&1 then return 0 else \$CHROOTBIN \$CHROOTPATH \$NGINXDAEMON -t \$NGINXDAEMON_OPTS return \$? fi } case \"\$1\" in start) echo \"Starting \$DESC ---\" # launching nginx test_nginx_config start-stop-daemon --chroot \$CHROOTPATH --start --pidfile /var/run/nginx.pid \ --exec \$NGINXDAEMON -- \$NGINXDAEMON_OPTS || true echo -n \"nginx started with pid \"; cat \$CHROOTPATH/var/run/nginx.pid ; echo if [ \$PHP5SUPPORT -eq 1 ] then if [ \$PHPSUPERVISE -eq 1 ] then svc -u /etc/service/spawn-fcgi-$target echo \"spawn-fcgi and php5 started under supervise\" else start-stop-daemon --chroot \$CHROOTPATH --start \ --exec \$SPAWNBIN -- -a \$PHP5ADDR -p \$PHP5PORT \ -u \$USER -g \$USER -P /var/run/php5-cgi.pid \$PHP5BIN || true echo -n \"php5-cgi started with pid \"; cat \$CHROOTPATH/var/run/php5-cgi.pid ; echo fi fi if [ \$FCGIWRAPSUPPORT -eq 1 ] then start-stop-daemon --chroot \$CHROOTPATH --start \ --exec \$SPAWNBIN -- -s \$FCGIWRAPSOCK \ -u \$USER -g \$USER -P /var/run/fcgiwrap.pid \$FCGIWRAPBIN || true echo -n \"fcgiwrap started with pid \"; cat \$CHROOTPATH/var/run/fcgiwrap.pid ; echo fi ;; stop) echo \"Stopping \$DESC ---\" if [ -e \$CHROOTPATH/var/run/nginx.pid ] then kill \`cat \$CHROOTPATH/var/run/nginx.pid\` if [ \$? -ne 0 ] then echo -n \"couldn't kill nginx - found running pid \" ; \`cat \$CHROOTPATH/var/run/nginx.pid\`;echo else echo \"stopping nginx\" fi else echo \"nginx is not running, no pid file in /var/run/\" fi if [ \$PHP5SUPPORT -eq 1 ] then if [ \$PHPSUPERVISE -eq 1 ] then svc -d /etc/service/spawn-fcgi-$target echo \"spawn-fcgi and php5 stopped under supervise\" else if [ -e \$CHROOTPATH/var/run/php5-cgi.pid ] then ps -p \`cat \$CHROOTPATH/var/run/php5-cgi.pid\` 2>&1 1>/dev/null if [ $? -ne 0 ] then echo \"php5-cgi process not running\" else kill \`cat \$CHROOTPATH/var/run/php5-cgi.pid\` if [ \$? -ne 0 ] then echo -n \"couldn't kill php5-cgi - pid=\";\`cat \$CHROOTPATH/var/run/php5-cgi.pid\`;echo else echo \"php5-cgi has been stopped\" fi fi else echo \"no php5-cgi pid file in /var/run/\" fi fi fi if [ \$FCGIWRAPSUPPORT -eq 1 ] then if [ -e \$CHROOTPATH/var/run/fcgiwrap.pid ] then ps -p \`cat \$CHROOTPATH/var/run/fcgiwrap.pid\` 2>&1 1>/dev/null if [ $? -ne 0 ] then echo \"fcgiwrap process not running\" else kill \`cat \$CHROOTPATH/var/run/fcgiwrap.pid\` if [ $? -ne 0 ] then echo -n \"couldn't kill fcgiwrap - pid=\";\`cat \$CHROOTPATH/var/run/fcgiwrap.pid\`;echo else echo \"fcgiwrap has been stopped\" fi fi else echo \"no fcgiwrap.pid file in /var/run/\" fi fi ;; restart) echo -n \"Restarting \$DESC: \" \$0 stop sleep 1 \$0 start ;; configtest) echo -n \"Testing \$DESC configuration: \" if test_nginx_config then echo \"\$NAME.\" else exit \$? fi ;; status) status_of_proc -p \$CHROOTPATH/var/run/nginx.pid \"\$NGINXDAEMON\" nginx && exit 0 || exit $? " >> /etc/init.d/control-$target if [ $phpsupport -eq 1 ] then echo " status_of_proc -p \$CHROOTPATH/var/run/php5-cgi.pid \"\$PHP5BIN\" php5-cgi && exit 0 || exit $? " >> /etc/init.d/control-$target fi echo " ;; *) echo \"Usage: \$0 {start|stop|restart|status|configtest}\" >&2 exit 1 ;; esac exit 0 " >> /etc/init.d/control-$target if [ -e /etc/init.d/control-$target ] then chmod +x /etc/init.d/control-$target else echo "couldn't create control file /etc/init.d/control-$target" exit 1 fi } usage(){ echo "Chroot installation script" echo "Syntax: $0 " echo "example: $0 chroot-example" echo "this will initiate the creation of $chroot_container/chroot-example" exit 1 } [ $# -ne 1 ] && usage [ $UID -ne 0 ] && echo "You need to be root to launch this program" && usage target=$1 [ -d $chroot_container$target ] && echo "Target already exist !" && exit 1 echo -n "This is going to install a chroot environment in $chroot_container$target. Do you want to continue ? y/n > " read response if [ $response != "y" ] then exit 1 fi # create the environment install_env "${target}" # get some information from the administrator before we start port_available=$(netstat -taupen|grep :50|grep LISTEN|awk '{print $4}'|cut -d : -f 2|sort|tail -n 1) port_available=$(( $port_available + 1 )) echo -n "Nginx listening port ($port_available seems available) ? > " read nginxport echo -n "What will the FQDN of the server be ? (example.com) ? > " read fqdn echo -n "Do you want chrooted php support ? y/n > " read response if [ $response == "y" ] then phpsupport=1 echo -n "Which port should php cgi listen on ? > " read phpport echo -n "Would you like to install php5-mysql support in the chroot ? y/n > " read response if [ $response == "y" ] then install_mysql "${target}" fi else phpsupport=0 fi if [ $phpsupport -eq 1 ] then install_php "${target}" echo -n "Would you like to add to supervise php using daemon-tools 'supervise' ? y/n > " read response if [ $response == "y" ] then phpsupervise=1 create_supervised_config "${target}" else phpsupervise=0 fi fi echo -n "Do you want chrooted fcgiwrap support ? y/n > " read response if [ $response == "y" ] then fcgiwrapsupport=1 install_fcgiwrap "${target}" else fcgiwrapsupport=0 fi echo -n "Do you want perl support ? y/n > " read response if [ $response == "y" ] then install_perl "${target}" echo -n "Also want Mojolicious ? y/n > " read response if [ $response == "y" ] then install_mojolicious "${target}" fi fi # call installation functions install_nginx "${target}" create_control "${target}" echo -n "Would you like to add this server to startup runlevel 2 ? y/n > " read response if [ $response == "y" ] then SAVEPWD=`pwd` cd /etc/rc2.d ln -s ../init.d/control-$target S17control-$target cd /etc/rc6.d ln -s ../init.d/control-$target K01control-$target cd $SAVEPWD fi echo -n "Would you like to create a logrotate rule for this server ? y/n > " read response if [ $response == "y" ] then echo "$chroot_container$target/var/log/nginx/*.log { daily missingok rotate 7 compress delaycompress notifempty create 640 root adm sharedscripts postrotate [ ! -f $chroot_container$target/var/run/nginx.pid ] || kill -USR1 \`cat $chroot_container$target/var/run/nginx.pid\` endscript }" > /etc/logrotate.d/$target fi echo "The installation of the chroot environment is finished." echo "To control the environement, use the script located at /etc/init.d/control-$target (outside the chroot)" exit 0