Hello Elli,
From what I understand of your architecture, I would configure
RemoteIpValve with :
<Valve
className = "org.apache.catalina.connector.RemoteIpValve"
internalProxies = "@load-balancer-ip"
trustedProxies = "@the-trusted-proxy-that-is-not-the-load-balancer"
protocolHeader = "x-forwarded-proto" />
Note :
- do not forget to "regexp escape' the ip adresses in internalProxies
and trustedProxies attributes.
- do not declare protocolHeader="x-forwarded-proto" if your load
balancer do not manage it
- more docs at http://code.google.com/p/xebia-france/wiki/RemoteIpValve
Thanks to this,
- the 99% direct requests will reach Tomcat with x-forwarded-for="@clientIp"
- 1% proxyfied requests will reach Tomcat with
x-forwarded-for="@clientIp,
@the-trusted-proxy-that-is-not-the-load-balancer"
Does it make sense ?
Cyrille
--
Cyrille Le Clerc
[email protected] [email protected]
http://blog.xebia.fr
On Wed, Oct 21, 2009 at 6:57 AM, Elli Albek <[email protected]> wrote:
>
> A question: How do you know that a proxy is trusted? Is it by providing a
> list of trusted IPs in the configuration of the filter?
>
> Our load balancer is always adding the client IP as the first in the list,
> and it does not add its own IP to the list. The header
> has one IP +99% of the times, the other times there is an additional IP of a
> proxy that is not our load balancer (reverse proxy).
>
> So in that case, we can check that the request comes from a trusted IP list
> (known load balancers), and only then try to change the
> IP. If the client IP does not come from the load balancer, it is basically a
> pass through.
>
> For our system the first IP is what the load balancer sees, and the only way
> to spoof it is to access the server not through the
> load balancer. If you send a request with a spoofed header to the laod
> balancer, it will still add the IP of the spoofer in the
> beginning of the list.
>
> This may not be the general case for proxies, it is only for this case.
>
> E
>
> -----Original Message-----
> From: Cyrille Le Clerc [mailto:[email protected]]
> Sent: Thursday, October 08, 2009 1:04 AM
> To: Tomcat Users List
> Subject: Re: Cannot set remote address in valve (Tomcat 5.5)
>
> Hello Elli,
>
> I am afraid there may be a flaw in the algorythm looking for the
> first IP of the coma delimited x-forwarded-for header without
> ensuring that this first IP has been set by a trusted proxy and not by
> the requester ( getFirstIP(xforwardedForHeaderValue) ). Such spoofing
> can easily be achieved with tools like Firefox add-ons Modify Headers
> (1) and X-Forwarded-For Spoofer (2) .
>
> The forthcoming version of Apache Httpd will offer a secure
> mechanism to handle X-Forwarded-For with a module called mod_remoteip
> (3). It relies on the concept of trusted proxies which IP address can
> be 'swallowed'. The first IP of the list that is not a trusted proxy
> is seen as the real remote ip. mod_remoteip would not have been
> tricked by such x-forwarded-for header spoofing.
>
> Here are two java ports of mod_remoteip to handle X-Forwarded-For
> at the Tomcat level with a valve and at the WAR level with a servlet
> filter : RemoteIpValve (4) and XForwardedFilter (5). In addition to
> handle X-Forwarded-For, they also integrate X-Forwarded-Proto (ssl).
> These java ports integrate the same trusted proxies concept to prevent
> spoofing.
>
> Cyrille
> --
> Cyrille Le Clerc
> [email protected] [email protected]
> http://blog.xebia.fr
>
>
> (1) https://addons.mozilla.org/en-US/firefox/addon/967
> (2) https://addons.mozilla.org/en-US/firefox/addon/5948
> (3) http://httpd.apache.org/docs/trunk/mod/mod_remoteip.html
> (4) http://code.google.com/p/xebia-france/wiki/RemoteIpValve
> (5) http://code.google.com/p/xebia-france/wiki/XForwardedFilter
>
>
> On Mon, Oct 5, 2009 at 11:19 PM, Elli Albek <[email protected]> wrote:
> >
> > Hi,
> >
> > We can add the header to the custom valves, but then in addition we have to
> > change a few log file configurations, create a servlet filter and maybe
> > something else I cant think of now. Basically doing the same thing a few
> > times and keeping track of all the places that depend on the header. Ideally
> > this would all be corrected once in the beginning of the request processing
> > pipeline, so log file configuration, other valves and the war files will
> > remain unchanged.
> >
> >
> >
> > Attached a Valve that does that. This is the minimum code necessary, so it
> > should not have any significant performance impact.
> >
> > Feel free to use as is, not guaranteed to work, no expressed on implied
> > warranties, not FDIC insured and may loose value.
> >
> >
> >
> > To configure Tomcat add to server.xml:
> >
> >
> >
> > <Service name="Catalina">
> >
> > <Connector port="8080" .../>
> >
> > <Engine defaultHost="localhost" name="Catalina">
> >
> > <!-- This should precede all other configuration in the engine
> > -->
> >
> > <Valve className="org.apache.catalina.connector.RemoteIPValve"/>
> >
> >
> >
> > Java class/jar should be placed in /server/lib or /server/classes
> >
> >
> >
> > E
> >
> >
> >
> >
> >
> >
> >
> > package org.apache.catalina.connector;
> >
> >
> >
> > import java.io.IOException;
> >
> > import java.util.regex.Matcher;
> >
> > import java.util.regex.Pattern;
> >
> >
> >
> > import javax.servlet.ServletException;
> >
> >
> >
> > import org.apache.catalina.connector.Request;
> >
> > import org.apache.catalina.connector.Response;
> >
> > import org.apache.catalina.valves.ValveBase;
> >
> >
> >
> > /**
> >
> > * A valve that extracts the remote IP of the client from an HTTP header
> > field
> >
> > * passed by the proxy, and set it in the request as the original client IP.
> >
> > * This valve should be the first valve in the engine, so log valves (and
> >
> > * others) will see the real client IP without requiring the same code
> > again.
> >
> > *
> >
> > * @author Elli Albek, www.sustainlane.com
> >
> > */
> >
> > public class RemoteIPValve extends ValveBase {
> >
> >
> >
> > private static final Pattern ipExpr =
> > Pattern.compile("^[\\da-fA-F]+(\\.[\\da-fA-F]+)+");
> >
> >
> >
> > private String forwardedForHeader = "X-Forwarded-For";
> >
> >
> >
> > public void invoke(Request request, Response response) throws
> > IOException, ServletException {
> >
> >
> >
> > String header = request.getHeader(forwardedForHeader);
> >
> > String forwardedIP = getFirstIP(header);
> >
> > if (forwardedIP != null)
> >
> > request.remoteAddr = forwardedIP;
> >
> >
> >
> > next.invoke(request, response);
> >
> > }
> >
> >
> >
> > /**
> >
> > * Return the first IP address in a string that may contain an IP list
> >
> > */
> >
> > static final String getFirstIP(String header) {
> >
> > if (header == null)
> >
> > return null;
> >
> > Matcher m = ipExpr.matcher(header);
> >
> > if (m.find()) {
> >
> > return m.group();
> >
> > }
> >
> > return null;
> >
> > }
> >
> >
> >
> > public void setForwardedForHeader(String forwardedForHeader) {
> >
> > this.forwardedForHeader = forwardedForHeader;
> >
> > }
> >
> >
> >
> > public String getInfo() {
> >
> > return "RemoteIPValve";
> >
> > }
> >
> > }
> >
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]