I have been investigating how I can isolate the various sites I host on my VPS box. Ideally I would have my own dedicated server with each site in it’s own VPS, this may be an option if I roll my own dedicated server in the near future but for now the next best option appears to be a chroot jail for each site proxied behind the primary Apache instance.
First step ensure Apache2 and mod_chroot are installed on your box, for Ubuntu this is:
apt-get install apache2 libapache2-mod-chroot
Then for each domain, create a virtual host config like /etc/apache2/sites-available/example.com:
<VirtualHost *:80>
ServerAdmin test@example.com
ServerName example.com
ServerAlias *.example.com
ProxyRequests Off
ProxyPreserveHost On
ProxyPass / http://localhost:8000/
ProxyPassReverse / http://localhost:8000/
<Proxy *>
Allow from all
</Proxy>
ErrorLog /home/example/www/example.com/log/error.log
LogLevel warn
CustomLog /home/example/www/example.com/log/access.log combined
</VirtualHost>
This will be used by the Apache proxy, which is the normal system apache2 running as the default “www-data” user. This proxy handles name-based virtual hosts, and proxies the requests to a second process, running at localhost on port 8080.
Note that the logs are configured here and not in the user’s Apache process, for two reasons:
- Keep logs pristine in the event of a break-in on a user site (for example via a buggy or malicious PHP script)
- Single system-wide log file analysis process instead of per-user
Next, create a user account for each domain:
mkdir -p /home/example/www/example.com
cd /home/example/www/example.com
mkdir htdocs logs conf
chown www-data:www-data logs
chown example:example htdocs/
Create the following in /home/example/www/example.com/conf/apache2.conf:
ServerRoot "/home/example/www/example.com/"
LockFile /home/example/www/example.com/conf/accept.lock
PidFile /home/example/www/example.com/conf/apache2.pid
Timeout 300
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 15
User example
Group example
AccessFileName .htaccess
<Files ~ "^\.ht">
Order allow,deny
Deny from all
</Files>
Options -Indexes
DefaultType text/plain
HostnameLookups Off
ErrorLog /home/example/www/example.com/conf/error.log
LogLevel warn
LoadFile /lib/libnss_dns.so.2
LoadModule chroot_module /usr/lib/apache2/modules/mod_chroot.so
Include /etc/apache2/mods-enabled/*.load
Include /etc/apache2/mods-enabled/*.conf
Listen 8000
DocumentRoot /home/example/www/example.com/htdocs
ChrootDir /home/example/www/example.com
Once the chroot has been applied the root directory as far as the Apache process is concerned is /home/example/www/example.com
– this creates a problem whereby the DocumentRoot is actually now /htdocs
. The way round this is to use the –bind option of mount shown here:
mkdir -p /home/example/www/example.com/home/example/www/example.com
mount --bind /home/example/www/example.com /home/example/www/example.com/home/example/www/example.com
Now launch the new Apache process:
apache2 -f /home/example/www/example.com/conf/apache2.conf -k start
Everything should now work, but you may notice some problems, particularly with scripts like PHP, CGI etc. Generally the way round this is to make parts of the system available in the chroot and by ammending paths using the apache2.conf file in the jail.
This is as far as I have got, I have been working on getting startup right with an init script and automating the mount –bind by editing the /etc/fstab entries. I will cover my success (or otherwise) in another post.