Easily get the correct client IP with mod_rpaf

Let's say you run a front end system like a reverse proxy, load balancer or http accelerator (e.g. big-ip, squid, pound, varnish, apache, nginx, etc), which pass dynamic requests to back end application servers, then you're likely to have to deal with your applications seeing client requests as coming from the IP(s) of your front end boxes.

This presents a slew of quality of life reducing issues, unless of course your back end system is Apache, in which case you can use mod_rpaf to make your life pleasant again.

Without it, you'd have to do painful things like patch your applications, which grab the client IP from the REMOTE_ADDR server variable, to use the X_FORWARDED_FOR header, if present, instead. For example:

1
2
3
4
5
6
// If set, use IP provided by our front end rproxy
if ($_SERVER["HTTP_X_FORWARDED_FOR"]) {
     $realclientip = $_SERVER["HTTP_X_FORWARDED_FOR"];
} else {
     $realclientip = $_SERVER["REMOTE_ADDR"];
}

mod_rpaf solves this much more easily, it works as follows:

It changes the remote address of the client visible to other Apache modules when two conditions are satisfied. First condition is that the remote client is actually a proxy that is defined in httpd.conf. Secondly if there is an incoming X-Forwarded-For header and the proxy is in its list of known proxies it takes the last IP from the incoming X-Forwarded-For header and changes the remote address of the client in the request structure.

If you're on Debian (or one of its derivatives -- Ubuntu, etc), get it with:

ak@gd:~$ aptitude install libapache2-mod-rpaf

Modify "/etc/apache2/mods-available/rpaf.conf" to include IP(s) of your front end boxes and enable with:

ak@gd:~$ a2enmod rpaf
ak@gd:~$ apache2ctl -t
ak@gd:~$ apache2ctl restart

Now, mod_rpaf will grab IP from X_FORWARDED_FOR header and place it in REMOTE_ADDR server variable. You can verify with:

1
2
3
4
5
6
7
8
<?php
$clientip = $_SERVER['HTTP_CLIENT_IP'];
$remoteaddr = $_SERVER['REMOTE_ADDR'];
$xforwardedfor = $_SERVER['HTTP_X_FORWARDED_FOR'];
echo " <li>HTTP_CLIENT_IP:  $clientip</li>\n";
echo " <li>REMOTE_ADDR:  $remoteaddr</li>\n";
echo " <li>HTTP_X_FORWARDED_FOR:  $xforwardedfor</li>\n";
?>

Geesus, that was easy. Gotta love Apache with its plethora of delicious modules. Using a distro like Debian that makes them super easy to get is nice too. Now, I can stop patching apps and go ride a board.

3 Comments

Leave a comment

NOTE: Enclose quotes in <blockquote></blockquote>. Enclose code in <pre lang="LANG"></pre> (where LANG is one of these).