Tomcat 6: First Impression of the New Remote IP Valve
First introduced for production use in the recently released 6.0.24 version, the Remote IP Valve provides tomcat with built-in support for pulling the client IP from the X-Forwarded-For header field and making that IP address available to applications. Prior to this, if you wanted to grab the client IP address from the X-Forwarded-For header, you needed to hard-code it into your applications yourself. If you weren't too careful, you could wind up with cases where you were getting the IP address only *most* of the time.
Setting up this valve is very straightforward. Edit your server.xml file and insert the following lines, (which can reside within the context
, host
, or engine
levels):
<Valve className="org.apache.catalina.valves.RemoteIpValve" />
If you place the valve higher than the Access Log Valve within server.xml, you won't need to utilize a custom log file pattern to log the client ip address but I did notice some unexpected behavior with access logs when combining the Remote IP Valve with the Remote Address filter. I don't know if it is necessarily a bug, a configuration error, or if it is a lack of understanding of the behavior of the Remote Address filter on my part. In my test container, which is a non-custom, straight-from-a-mirror binary download running on Snow Leopard, I modified server.xml to include the following:
<Valve className="org.apache.catalina.valves.RemoteIpValve" /> <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="10.11.12.13" deny="127.0.0.1"/> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="combined" resolveHosts="false"/> </Host> </Engine> </Service>
My test script is a basic HTTP Get written in Groovy, which I'm running from within groovyConsole so that I can enable and disable the custom request header at will:
def url = new URL("http://localhost:8080/docs/introduction.html") def connection = url.openConnection() connection.setRequestProperty("X-Forwarded-For","10.11.12.13") connection.setRequestProperty("User-Agent","benevolent-bot/blog.techstacks.com") if(connection.responseCode == 200){ println "Connection successful" } else{ println "Ouchy! Error! ${connection.responseCode}" }
When tailing the tomcat access log and executing the test with the X-Forwarded-For property enabled, everything works as expected. I see my successful request in the logs using the ip address 10.11.12.13
. However, when disabling this property and re-sending the request along, I do see the 403 status code returned from tomcat, which is expected, but nothing gets logged in the access log, which was not expected. I was expecting to see a request logged in the access log from ip address 127.0.0.1
for /introduction.html
with a 403 status code. Logging request attempts from forbidden remote IP's seems like it should be useful.
Logging issues aside, (which, again could be my fault), the new Remote IP Valve is a useful addition to apache tomcat and I encourage everyone to start making use of it. Even if you don't have a reverse proxy device sitting in front of your tomcat servers, chances are very good that your customers do go through one to get to you and the Remote IP Valve will give you the actual customer IP address, without the need for custom coding within your servlets.