The Apache Software Foundation's HTTP server project, commonly known as Apache, is the dominant Web server on the Internet today, accounting for over 60 percent of market share. Increasingly, the Apache serve is part of a set of free software programs in a configuration known as LAMP, a Web platform built on the open source technologies of Linux®, Apache, MySQL, and PHP. In this article, you'll explore a method that improves the security of your LAMP installation by using the mod_proxy module and multiple back-end servers. I'll discuss the merits and drawbacks of such an approach, and you'll see an example configuration of a working setup.
One challenge that faces LAMP administrators is that of providing all the features of a full PHP installation while ensuring a secure environment for all users of the system. Use of PHP's safe mode is one technique for meeting this goal, but it can also unduly limit users, and some PHP applications simply don't function with this facility enabled.
The root of the PHP security problem lies in the way most Apache servers are configured. Because most Apache installations run under a special www-data user ID, all the users hosting a Web site must by default ensure that their files are readable by this user. Consequently, it is possible that all of a user's Web-accessible files will be accessible by all of the other users on the system, and as such could be subject to attack by an otherwise unrelated compromise on the system. The situation becomes even more untenable when files or directories must be set as writable by the www-data user.
With CGI programs, such as those written in the popular languages Perl and Python, you avoid some of the impact of this problem when you use the suEXEC mechanism. In short, suEXEC uses a special intermediary program to execute the CGI program as the user ID that owns the program. (See Resources for links to articles with more details.) This is a very effective mechanism and has been commonplace for many years.
However, PHP pages, when hosted using the mod_php module, are executed as part of the main Apache process. As such, they inherit all the credentials of the Apache process, and so any work they perform on the filesystem must be performed as the www-data user.
The obvious solution to the problem described above is to host all the requests for a user's domains from an Apache instance that has only that user's credentials. You can configure Apache to take on any user's credentials on startup. This can solve the problem for simple setups where each user is assigned an individual IP address/port combination visible to the Internet.
For more complex setups, where IP addresses are at a premium, this method will not work. You can only use virtual hosting, a technique widely used in Apache installations, when a single Apache instance has control of a particular IP address/port combination. This precludes the possibility of hosting multiple domains belonging to multiple users from the same IP address/port combination.
Apache 2.0 introduced the concept of multiprocessing modules, or MPMs. Among the MPMs provided with the basic Apache 2.0 package was an experimental module named perchild, which allowed virtual hosting under multiple user IDs by assigning a distributor thread to the IP address/port combination, and passing requests onto satellite threads that run under individual users' credentials. Sadly, perchild remained experimental, functioning only for the fortunate, and was eventually removed from the official Apache distribution with the release of Apache 2.2. Before this, realizing the continued need for a stable and functioning perchild-like MPM, the wider Apache community started work on a number of MPMs to fill the gap. MetuxMPM, and its process-oriented fork, peruser, continue to work towards this goal. (See Resources for more information on the MetuxMPM and peruser MPMs.)
Although no official Apache MPM is available to directly provide virtual hosting under multiple user IDs, you can still build an Apache system that behaves in this way through some intelligent configuration and administration. The core concept of the method is the use of the mod_proxy module, which, among other functionality, allows Apache to forward requests for pages onto other servers, and pass the response back to the original requesting client.
Listing 1. Sample reverse proxy configuration for basic request forwarding
ProxyRequests Off ProxyPass /foo http://foo.example.com/bar ProxyPassReverse /foo http://foo.example.com/bar
The code in Listing 1 is a simple example that forwards requests for any page under a host's /foo hierarchy to the corresponding page hosted at http://foo.example.com/bar. For example, the page /foo/index.htm would be forwarded to http://foo.example.com/bar/index.htm. You can use this principle to solve the problem.
Consider a scenario in which an Apache administrator must host two domains for two separate customers. One customer is a startup online business that is concerned about online security. The other is an individual customer who is lax about site security and is known to upload insecure code to his site. Given these factors, the Apache administrator must take steps to isolate the two sites from each other.
The administrator therefore has two domains: www.startup.tld, owned by the startup online business (user ID startup), and www.reckless.tld, owned by the individual (user ID nimrod). To solve the problem, the administrator decides to use the mod_proxy solution. The administrator gives each user his own Apache instance running under his own user ID on a private IP address/port combination, and uses the mod_proxy solution to provide access to both users' domains through a facade server running as www-data on a public IP address/port combination. The complete scenario is illustrated in Figure 1.
Figure 1. Example scenario
For each element of the configuration of our sample application, the Apache administrator should use the versions of Apache outlined in Table 1.
Table 1. Versions of Apache used in sample application
|Facade server||Apache 2, running the worker or event MPM||Apache 2 features important enhancements to the mod_proxy module. The worker and event MPMs are threaded and help reduce the memory overhead of the facade server.|
|Back-end servers||Apache 1.3, or Apache 2 running the prefork MPM||The Apache administrator must be aware that the PHP module should not run under a threaded environment. These two solutions offer process-based environments for the PHP module.|
The snippets in Listings 2 and 3 illustrate the essential configuration points that vary from a standard Apache configuration. They should be added to as required -- the example configuration omits PHP functionality, for example.
Listing 2. Apache config for startup online business
# Stuff every Apache configuration needs ServerType standalone LockFile /var/lock/apache/accept.startup.lock PidFile /var/run/apache.startup.pid ServerName necessaryevil.startup.tld DocumentRoot "/home/startup/web" # Essential modules LoadModule access_module /usr/lib/apache/1.3/mod_access.so # Which user to run this Apache configuration as User startup Group startup # This must be off else the host isn't passed correctly UseCanonicalName Off # The IP/port combination to listen on Listen 127.0.0.2:10000 # Using name-based virtual hosting allows you to host multiple sites per IP/port combo NameVirtualHost 127.0.0.2:10000 <VirtualHost 127.0.0.2:10000> ServerName www.startup.tld # You can add aliases so long as the facade server is aware of them! ServerAlias startup.tld DocumentRoot "/home/startup/web/www.startup.tld" <Directory /home/startup/web/www.startup.tld/> Options Indexes FollowSymLinks MultiViews ExecCGI Includes AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost>
Listing 3. Apache config for individual customer
# Stuff every Apache configuration needs ServerType standalone LockFile /var/lock/apache/accept.nimrod.lock PidFile /var/run/apache.nimrod.pid ServerName necessaryevil.nimrod.tld DocumentRoot "/home/nimrod/web" # Essential modules LoadModule access_module /usr/lib/apache/1.3/mod_access.so # Which user to run this Apache configuration as User nimrod Group nimrod # This must be off else the host isn't passed correctly UseCanonicalName Off # The IP/port combination to listen on Listen 127.0.0.2:10001 # Using name-based virtual hosting allows you to host multiple sites per IP/port combo NameVirtualHost 127.0.0.2:10001 <VirtualHost 127.0.0.2:10001> ServerName www.reckless.tld # You can add aliases so long as the facade server is aware of them! ServerAlias reckless.tld DocumentRoot "/home/nimrod/web/www.reckless.tld" <Directory /home/nimrod/web/www.reckless.tld/> Options Indexes FollowSymLinks MultiViews ExecCGI Includes AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost>
Listing 4 illustrates the configuration for the facade Apache instance.
Listing 4. Apache config for the facade Apache instance
# Stuff every Apache configuration needs LockFile /var/lock/apache/accept.www-data.lock PidFile /var/run/apache.www-data.pid ServerName necessaryevil.facade.server DocumentRoot "/home/www-data" # Essential modules LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so # Which user to run this Apache configuration as User www-data Group www-data # These must be set else the host isn't passed correctly UseCanonicalName Off ProxyVia On ProxyRequests Off # This must also be set, though it's only an option in Apache2 ProxyPreserveHost On # The IP/port combination to listen on Listen 184.108.40.206:80 # Using name-based virtual hosting allows you to host multiple sites per IP/port combo NameVirtualHost 220.127.116.11:80 # Configuration to forward requests for startup.tld <VirtualHost 18.104.22.168:80> ServerName www.startup.tld ServerAlias startup.tld ProxyPass / http://127.0.0.2:10000/ ProxyPassReverse / http://127.0.0.2:10000/ ProxyPassReverse / http://www.startup.tld:10000/ ProxyPassReverse / http://startup.tld:10000/ </VirtualHost> # Configuration to forward requests for reckless.tld <VirtualHost 22.214.171.124:80> ServerName www.reckless.tld ServerAlias reckless.tld ProxyPass / http://127.0.0.2:10001/ ProxyPassReverse / http://127.0.0.2:10001/ ProxyPassReverse / http://www.reckless.tld:10001/ ProxyPassReverse / http://reckless.tld:10001/ </VirtualHost>
It is important to note the
ProxyPreserveHost directive here. This directive came with the release of Apache 2, and solved some problems with forwarding the correct HTTP headers to the back-end servers. It is highly recommended that you use an Apache 2 instance as the facade server.
The root user should run each of the configurations. Apache will take on the privileges indicated in the configuration file for all hosting-related processes. Listing 5 illustrates how you'd start things up.
Listing 5. Starting the example servers
/usr/sbin/apache -f /etc/apache/startup.tld.conf /usr/sbin/apache -f /etc/apache/nimrod.tld.conf /usr/sbin/apache2 -f /etc/apache2/facade.tld.conf
It is critical to note that the method outlined in this article will not work for domains that require SSL connections. This is because the SSL protocol does not allow for virtual hosting of domains. Due to this limitation, any SSL hosting must be performed in a manner whereby each SSL domain is hosted on its own IP/port combination. This is a limitation of all Apache configurations, not exclusively those utilizing this solution. You can still run SSL domains under their owner's user ID.
In this article, you used the capabilities of Apache's mod_proxy module to construct an environment consisting of a facade server forwarding requests to two back-end servers. You can use the same principle for a series of back-end servers. The approach allows system administrators to isolate potential security risks while maintaining the flexibility that tools like PHP provide.
- suEXEC support: Get the documentation straight from Apache and learn how to reduce security risks when your users develop and run private CGI or SSI programs.
- MetuxMPM Wiki: Learn about this module and contribute to the Multi Platform Module (MPM) for the Apache Web server (and successor to the Perchild MPM).
- Introduction to LAMP technology (Jono Bacon, developerWorks, May 2005): Delve into this tutorial, a good intro to the Linux-Apache-MySQL-PHP, or LAMP, Web development framework as you solve common business problems.
- developerWorks Web development zone: See more articles from developerWorks on LAMP and other Web development technologies.
- Linux zone: Check out this developerWorks resource for up-to-date Linux technologies.
- Technology bookstore: Browse for books on these and other technical topics.
- developerWorks technical events and webcasts: Stay current with jam-packed technical sessions that shorten your learning curve, and improve the quality and results of your most difficult software projects.
Get products and technologies
- peruser MPM: Find out more about this MPM, an Apache 2 module based on metuxmpm, a working implementation of the perchild MPM.
- IBM trial software: Build your next development project with these tools, available for download directly from developerWorks.
- developerWorks Web Development Downloads and products area: Find more free downloads.
Nick Maynard has worked in the Scenario Analysis Lab at IBM Hursley since joining IBM UK Ltd. as a graduate in 2003 from the Imperial College of Science, Technology, and Medicine. He specializes in Linux, Web services, and business integration technologies. Contact Nick at email@example.com.