Centos7 - Apache Chroot Jail
One method of hardening apache in centos7 is by running apache in a chroot jail.
We are going to use modsecurity to enforce the chroot jail, using the SecChrootDir command. Then, where possible, we will use read-only directory bindmounts to expose the system files apache needs to run to the jail.
#first we're going to install and configure apache 2.4 in centos7 yum install httpd mod_security -y; #disable some apache modules sed -i "s/^/### X ###/" /etc/httpd/conf.d/autoindex.conf sed -i "s/^/### X ###/" /etc/httpd/conf.d/userdir.conf sed -i "s/^/### X ###/" /etc/httpd/conf.d/welcome.conf sed -i "s/^/### X ###/" /etc/httpd/conf.d/mod_security.conf cat << EOT >> /etc/httpd/conf.d/mod_security.conf <IfModule mod_security2.c> SecChrootDir "/var/chroot" </IfModule> EOT #disable references to /var/www sed -i '/^<Directory ".var.www.html">/,/^<\/Directory>/ s/^/### X ###/' /etc/httpd/conf/httpd.conf sed -i '/^<IfModule alias_module>/,/^<\/IfModule>/ s/^/### X ###/' /etc/httpd/conf/httpd.conf sed -i '/^<Directory ".var.www.cgi-bin">/,/^<\/Directory>/ s/^/### X ###/' /etc/httpd/conf/httpd.conf sed -i '/^<Directory ".var.www">/,/^<\/Directory>/ s/^/### X ###/' /etc/httpd/conf/httpd.conf sed -i 's|^DocumentRoot.*|### X ###DocumentRoot "/var/www/html/|' /etc/httpd/conf/httpd.conf cat << EOT >> /etc/httpd/conf/httpd.conf ServerName localhost <Directory "/var/chroot"> AllowOverride None Require all granted </Directory> TraceEnable off ServerSignature Off Include /etc/httpd/sites.d/*.conf EOT #setup an example virtualhost mkdir -p /etc/httpd/sites.d/ cat << 'EOT' >> /etc/httpd/sites.d/www.example.com.conf <VirtualHost *:80> ServerName www.example.com ServerAlias example.com DocumentRoot /var/chroot/www.example.com ErrorLog /var/log/httpd/error.log CustomLog /var/log/httpd/access.log combined </VirtualHost> EOT mkdir -p /var/chroot/www.example.com #for selinux management, if needed yum install policycoreutils-python -y; semanage fcontext -a -s system_u -t httpd_config_t '/etc/httpd/sites.d(/.*)?' restorecon -R /etc/httpd/sites.d semanage fcontext -a -s system_u -t httpd_sys_content_t '/var/chroot/www.example.com(/.*)?' restorecon -R /var/chroot/www.example.com ##many websites need these selinux options #setsebool -P httpd_can_network_connect_db 1 #setsebool -P httpd_can_network_connect 1 #setsebool -P httpd_can_sendmail 1 ##you may also want to install mod_ssl #yum install mod_ssl -y;
#And now... lets build the chroot mkdir -p /var/chroot/etc/ cat /etc/passwd |egrep 'root:x|apache|nobody' > /var/chroot/etc/passwd cat /etc/group |egrep 'root:x|apache|nobody' |sed 's|bin/bash|sbin/nologin|' > /var/chroot/etc/group cat /etc/shadow |egrep 'root:x|apache|nobody' > /var/chroot/etc/shadow chmod 000 /var/chroot/etc/shadow mkdir -p /var/chroot/etc/httpd/ mkdir -p /var/chroot/etc/pki/ mkdir -p /var/chroot/var/log/httpd mkdir -p /var/chroot/run/httpd ln -s ../../run/httpd /var/chroot/etc/httpd/run ln -s ../../var/log/httpd /var/chroot/etc/httpd/logs mkdir -p /var/chroot/{dev,etc,tmp,usr,log,var} mkdir -p /var/chroot/usr/{bin,lib,lib64,libexec,local,sbin,src,share} mkdir -p /var/chroot/usr/share/misc/ mkdir -p /var/chroot/usr/lib/locale mkdir -p /var/chroot/run/{httpd,systemd} ln -s usr/lib /var/chroot/lib ln -s usr/lib64 /var/chroot/lib64 ln -s usr/sbin /var/chroot/sbin ln -s usr/bin /var/chroot/bin ln -s .. /var/chroot/var/chroot ln -s ../tmp /var/chroot/var/tmp chmod 1777 /var/chroot/tmp mkdir -p /var/chroot/dev mknod -m 666 /var/chroot/dev/null c 1 3 mknod -m 666 /var/chroot/dev/urandom c 1 9 mknod -m 666 /var/chroot/dev/zero c 1 5 #add more /dev paths here if your application requires them cat << EOF >> /etc/fstab ##systemd doesn't like the readonly bindmount here, so just do a regular bindmount for now /run/systemd /var/chroot/run/systemd none bind 0 0 /etc/pki /var/chroot/etc/pki none bind 0 0 /usr/lib/locale /var/chroot/usr/lib/locale none bind 0 0 /usr/lib64 /var/chroot/usr/lib64 none bind 0 0 /usr/share /var/chroot/usr/share none bind 0 0 ##if you add an entry here, also add in /root/bin/chroot-init.sh EOF #will run on boot cat << EOF > /etc/systemd/system/multi-user.target.wants/chroot.service [Unit] Description=chroot init service [Service] Type=Idle ExecStart=/root/bin/chroot-init.sh [Install] WantedBy=multi-user.target EOF #script to remount bindmounts as read-only mkdir -p /root/bin/ cat << 'EOF' > /root/bin/chroot-init.sh #!/bin/bash # used by /etc/systemd/system/chroot.service mount -a mount -o remount,ro /var/chroot/run/systemd mount -o remount,ro /var/chroot/etc/pki mount -o remount,ro /var/chroot/usr/lib/locale mount -o remount,ro /var/chroot/usr/lib64 mount -o remount,ro /var/chroot/usr/share #cp files instead of readonly bind mount for i in {hosts,resolv.conf,nsswitch.conf,localtime}; do cp -f /etc/$i /var/chroot/etc/$i done #if you need to add files to you chroot jail, add more cp commands here EOF chmod 755 /root/bin/chroot-init.sh /root/bin/chroot-init.sh systemctl stop httpd systemctl start httpd systemctl enable httpd systemctl restart httpd #if you're running selinux you may want to run these... semanage fcontext -a -s system_u -t etc_t '/var/chroot/etc(/.*)?' semanage fcontext -a -s system_u -t cert_t '/var/chroot/etc/pki(/.*)?' semanage fcontext -a -s system_u -t httpd_config_t -f f '/var/chroot/etc/httpd' semanage fcontext -a -s system_u -t shadow_t -f f '/var/chroot/etc/shadow' semanage fcontext -a -s system_u -t passwd_file_t -f f '/var/chroot/etc/passwd' semanage fcontext -a -s system_u -t passwd_file_t -f f '/var/chroot/etc/group' semanage fcontext -a -s system_u -t net_conf_t -f f '/var/chroot/etc/hosts' semanage fcontext -a -s system_u -t net_conf_t -f f '/var/chroot/etc/resolv.conf' restorecon -R /var/chroot/etc semanage fcontext -a -s system_u -t device_t '/var/chroot/dev(/.*)?' semanage fcontext -a -s system_u -t urandom_device_t -f c '/var/chroot/dev/urandom' semanage fcontext -a -s system_u -t null_device_t -f c '/var/chroot/dev/null' semanage fcontext -a -s system_u -t zero_device_t -f c '/var/chroot/dev/zero' restorecon -R /var/chroot/dev semanage fcontext -a -s system_u -t httpd_var_run_t '/var/chroot/run/httpd(/.*)?' semanage fcontext -a -s system_u -t bin_t '/var/chroot/usr/bin(/.*)?' semanage fcontext -a -s system_u -t var_log_t '/var/chroot/log(/.*)?' semanage fcontext -a -s system_u -t tmp_t '/var/chroot/tmp(/.*)?' semanage fcontext -a -s system_u -t httpd_var_run_t '/var/chroot/run/httpd(/.*)?' restorecon -R /var/chroot/run/httpd restorecon -R /var/chroot/log restorecon -R /var/chroot/tmp restorecon -R /var/chroot/usr/bin semanage fcontext -l getsebool -a getenforce
#now, lets prove the jail is working.... yum install php curl -y systemctl restart httpd cat << EOF > /var/chroot/www.example.com/chroot.php <?php print_r(glob("/*")); ?> EOF curl 127.0.0.1/chroot.php #should output something like... Array ( [0] => /bin [1] => /dev [2] => /etc [3] => /lib [4] => /lib64 [5] => /log [6] => /run [7] => /sbin [8] => /tmp [9] => /usr [10] => /var [11] => /www.example.com )
By using read-only bindmounts it allows the chroot jail libraries to stay in sync with the system libraries whenever the host OS is patched or updated. Depending on what apache modules or php extensions you use, you may find you need to add additional system files to your chroot jail. For DIRs the simplest way is to add the dirs to /etc/fstab and /root/bin/chroot-init.sh. For files, add cp commands to /root/bin/chroot-init.sh, and then. For instance, php exec() uses /bin/sh internally, so if your application requires that... you'd need to cp /bin/sh to /var/chroot/bin/sh.
code snippets are licensed under Creative Commons CC-By-SA 3.0 (unless otherwise specified)