Shared webhosting
Contents
The problem
Note: this is my first try to create an Wiki article. Please modify :-)
One of the problems with shared webhosting (i.e. different people with each his/her own webpages) is that modern script languages such as PHP, Python or Perl are to powerfull. For example take the following PHP script:
<?php function get_content($filename) { $handle = fopen($filename, 'r'); echo fread($handle, filesize($filename)); fclose($handle); } get_content('/home/ppuk34/www/forum/config.inc.php'); ?>
With PHP you could use open_basedir to prevent this, but there are more ways. Another example is PHP Shell, a script that is often mis-used by people with not-so-good intentions. Or think about the Santy-worm which mis-used phpBB. Again there is a solution in the form of safe_mode, but lots of PHP scripts break if you enable this. For Python, Perl or CGI-scripts there are no easy ways and you have to use wrappers or other tricks to chroot these. Most users don't want to hear about security (unless you show them how easy it is) and just want there scripts to work. Some do care, but his/her own server is much to expensive. And finally we didn't talk about hidden bugs in almost every security measure we mentioned. A knowledgeable person can almost certain find backdoors because of the vast amount of possibilities these scripting languages offer.
The solution
Instead of wasting time to secure all the possible things you don't want as a webhoster and in the process frustrate your clients, it is far better, easier and more flexible to give every account its own environment. OpenVZ is ideal for this. In this article we describe how shared webhosting with OpenVZ could be implemented.
Minimal server
Create an VEx with your favorite distro. Give it an internal IP-address in one of the ranges 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16. Then strip away all unessary init.d scripts so only the bare minimum is started. That means as a minimum syslogd and ssh so the account holder can upload his/her files through SCP/SFTP in his/her own minimal server. For this to work you need to set up destination NAT on VE0 from high numbered ports to port 22 on the given private IP address:
dnat="-j DNAT --to-destination" iptables -t nat -P PREROUTING ACCEPT iptables -t nat -A PREROUTING -p TCP --dport 10122 $dnat 192.168.13.101:22 iptables -t nat -A PREROUTING -p TCP --dport 10222 $dnat 192.168.13.102:22 ...
The other thing you want for webhosting is of course a webserver as well. To minimize the amount of needed memory, we choose Lighttpd instead of the common Apache. Then configure the scripting language of your choice to run under this webserver. It is possible to use different languages/setups for different accounts as well. Also problematic CGI-scripts are not problematic anymore...
MySQL server
Most webhosting accounts use MySQL, but if you prefer another database server, go ahead. Create a new VEx with a lot more resources and again an internal IP-address. Now configure the accounts. As an extra security measure you can use the internal IP-address as well.
Proxy webserver
Because we have only one public IP-address, we need an trick to access every minimal server based on the hostname in the HTTP request. For SSH we used different ports, but that is not an option for websites. Again we create an VEx with an internal IP-address. On this server we install Lighttpd as well, because the proxying is very simple. If someone has an working example with Apache, please add. First we must forward port 80 to this server:
dnat="-j DNAT --to-destination" iptables -t nat -P PREROUTING ACCEPT iptables -t nat -A PREROUTING -p TCP -d <external IP-address> --dport 80 $dnat 192.168.13.11:80 iptables -t nat -A PREROUTING -p TCP --dport 10122 $dnat 192.168.13.101:22 iptables -t nat -A PREROUTING -p TCP --dport 10222 $dnat 192.168.13.102:22 ...
Then we create for every website an section in /etc/lighttpd/lighttpd.conf as follows:
$HTTP["host"] == "ve101.armorica.tk" { proxy.server = ( "" => ( ( "host" => "192.168.13.101" ) ) ) }
You can map more names to the same IP-address if needed. The last step is to add mod_proxy to the server.modules section.
Other applications
Create for other applications as mail, make sure that the minimal servers use this one for sending mail from webpages, DNS etc. VEx as needed. The resulting sever looks something lik this: