Level: Intermediate Nick Maynard (nick.maynard@uk.ibm.com), Scenario Analyst,
IBM
29 Nov 2006 In this article, Nick Maynard outlines a method for you to improve the security of a LAMP setup by using Apache's mod_proxy module. This article is specific to Linux; however, you can also apply some of the principles to other operating systems.
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.
PHP and Apache: The security conundrum
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.
Running Apache under multiple user IDs
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.)
A solution: mod_proxy
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.
Example scenario
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
Recommended Apache versions
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
| Element | Apache version | Reasoning |
|---|
| 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. |
Configuration for back-end Apache instances
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 9.20.1.1:80
# Using name-based virtual hosting allows you to host multiple sites per IP/port combo
NameVirtualHost 9.20.1.1:80
# Configuration to forward requests for startup.tld
<VirtualHost 9.20.1.1: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 9.20.1.1: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.
Running the example configurations
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
|
Limitations of the mod_proxy approach
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.
Summary
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.
Resources Learn
- 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
Discuss
About the author  | 
|  | 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 nick.maynard@uk.ibm.com. |
Rate this page
|