9 posts categorized "x-forwarded-for"

08/20/2009

Updated X-Forwarded-For ISAPI Filter from F5

A new version of Joe Pruitt's (from F5) X-Forwarded-For ISAPI Filter was made available recently.  This update targets both 32 and 64 bit versions of Windows 2003 and 2008, has been tested under IIS6 and IIS7, and adds some new features like Proxy Chain Support and Alternate Header Names. 

With the old filter, if your request passed through multiple proxies that supported the X-Forwarded-For header, instead of actually seeing the IP address of the end-user, you'd see the IP address of the second to last proxy that this particular request went through.  This updated filter addresses this issue.

Alternate header name support is if you are using a header name other than X-Forwarded-For to store the client IP address, you can specify that header name in an .ini file so that the filter knows what to look for. 

I've just downloaded it but have not tested it yet myself, (let alone deployed) it but I'll post updates when I do.

The source code is included.  Joe would appreciate hearing about any updates that you might have made.  There does not appear to be a restrictive license in place.

12/17/2008

Using Groovy to Verify the X-Forwarded-For Header

I love and hate scripting languages. Mostly, it is due to me not knowing which one to really spend a lot of time getting proficient with. Perl, Python, Ruby, Groovy? They all have their appeal and that's half the problem for me. I can't decide. PowerShell aside, my life as an IIS administrator prior to moving to apache, tomcat, jboss, etc. in addition to IIS, meant not having to drop down to the command line all that often—usually just to register DLLs. Sure, VBScript was there but that wasn't all that sexy enough to get me to use it. Also, what I'm looking for in a scripting language is the ability to quickly write things that I can use to pull stats, parse logs, scrape data from web pages, automate deployments—things like that but I don't want to have to store binaries across hundreds of different servers. I just want to run them all locally.

Any how, one common question that comes up at work a lot involves testing applications that reside behind proxies that insert X-Forwarded-For headers. I don't really know how much a BigIP LTM costs but we don't utilize them across all environments—there is little point load-balancing development boxes when those boxes could be developer workstations or single servers. Since the BigIP is, however, inserting headers into the request in order to record the IP address of the client machine, developers do need a way to mimic this behavior in order to be able to test their own applications as well as to simply understand what is going on when the BigIP does this. I've been surprised by the amount of traffic google has referred to this site by folks searching for "X-Forwarded-For". 350 visits over the past 4 months for the search queries containing "x-forwarded-for".

The X-Forwarded-For header is a mechanism for proxies that mask the incoming IP address of the client to provide that IP address of the client to the upstream server. Proxies make requests on behalf of the client that is requesting access to a particular resource—a client does not pass-through a proxy. The trouble with proxies in the past prior to creation of the X-Forwarded-For header was that the web or application server sitting behind the proxy only saw the IP address of the proxy—not the browser. This makes it tough to restrict access to a particular resource if your tens of thousands of users scattered all over the world all share the same IP of your local proxy or load-balancing proxy. It also makes it difficult to geo-locate the IP address for that credit card transaction.

The trouble with proxies after development of the X-Forwarded-For header is that the web server still only sees the IP address of the proxy machine. The web or application server needs modifications made in order to pull the IP address out of the request header. Take something simple such as logging a URI request in an access log. In a previous article, I detailed changes that need to be made to the Tomcat access log valve in order to replace the host IP with the IP address stored in the X-Forwarded-For header. Similar changes need to be made to the Apache web server CustomLog format but this article will stick with tomcat because that is probably what you are running on your local workstation.

The following groovy script can be used to mimic the behavior of a BigIP that is inserting an X-Forwarded-For header in a browser request. The script and tomcat access log valve changes below are only used for illustrative purposes only and you will need to use your imagination to some extent because all I'm really trying to accomplish here is an attempt to convey what actually occurs in this type of infrastructure.

If your tomcat server is currently utilizing a combined or common log file pattern, you'll change it to utilize the following pattern instead, (which is the combined log file pattern with the addition of x-forwarded-for logging).

  pattern="%h %{X-Forwarded-For}i %l %u %{User-Agent}i %t %r %s %b %{Referer}i"

What this pattern would provide on a production server is the IP address of the requesting host (in this case the BigIP) and the IP address(es) stored in the X-Forwarded-For header that the BigIP inserts into the request. For our testing purposes, we're going to pretend that your local machine address (127.0.0.1) is the "BigIP proxy" and that the IP address in the groovy script is your actual machine address. Again, this is simply for illustrative purposes only but it does convey what is actually occurring in production. Now for the script:


#!/usr/bin/env groovy
// setup connection
def url = new URL("http://localhost:8080/")
def connection = url.openConnection()
connection.setRequestProperty("X-Forwarded-For","192.168.254.100")
connection.setRequestProperty("User-Agent","benevolent-robot/allsortsandnotions.blogspot.com")

if(connection.responseCode == 200){
println "Connection successful"
}
else{
println "Ouchy! Error!"
}


As you can see, all this script does is send a single HTTP GET to my local tomcat instance and inserts an X-Forwarded-For header into the HTTP Request. In this case, the IP address that I'm pretending is my actual machine address is 192.168.254.100. Tail your tomcat access logs (assuming your running linux, OS X, or cygwin locally), and you'll see what gets recorded in the log files:


$ tail -f localhost_access_log.2008-12-16.txt
127.0.0.1 192.168.254.100 - - benevolent-robot/allsortsandnotions.blogspot.com [
16/Dec/2008:19:43:41 -0500] GET / HTTP/1.1 200 7857 null


The addresses showing up in the log file are 127.0.0.1, which I am pretending represents my proxy server/bigip address in this example and 192.168.254.100, which is the X-Forwarded-For proxy inserted IP address. Using similar code, you can script your own tests inserting any address into the X-Forwarded-For section in order to validate whatever it is that your code needs to validate. If you were to apply the same pattern to your production tomcat access log setting, the first IP address you would see would be the IP address of the BigIP. The second IP address you would see would be the IP Address of your customer.

09/09/2008

Java Code to Read the X-Forwarded-For Header

I am not a software developer...I'm a systems administrator.  That being said, I feel badly that by rolling out a BigIP LTM load-balancer I may have broken some web applications--specifically credit card applications that need to be able to look up the originating client IP address.

Sample code for a servlet to look up a client ip address might look something like this:

public void service(HttpServletRequest req, HttpServletResponse res) 
throws IOException {
String IP = req.getRemoteAddr();
}

The code that is broken by the BigIP is getRemoteAddr(). Typically, what this code does is grab the IP address from the HTTP environment variable "REMOTE_ADDR" but because the BigIP proxies requests, the only request you will see in the HTTP Header is the IP address of the unit that forwarded the request. The basic code below is intended to replace getRemoteAddr() with something that will pull the IP address out of X-Forwarded-For HTTP Header instead. Please note though that you are probably NOT going to want to use this code verbatim but it should serve as a trinket to get you started on some larger class that should look for the existence of X-Forwarded-For, handle errors, and also handle the possibility of handling multiple IPs in this header.  This can occur if your customers wind up passing through multiple proxies that support this header.

public void service(HttpServletRequest req, HttpServletResponse res) 
throws IOException {
String IP = req.getHeader("X-Forwarded-For");
}

Let me know how things go with this.