Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package org.apache.catalina.valves;
 
 import static org.jboss.web.CatalinaMessages.MESSAGES;
 
 import java.util.List;
 
 

Tomcat port of mod_remoteip, this valve replaces the apparent client remote IP address and hostname for the request with the IP address list presented by a proxy or a load balancer via a request headers (e.g. "X-Forwarded-For").

Another feature of this valve is to replace the apparent scheme (http/https) and server port with the scheme presented by a proxy or a load balancer via a request header (e.g. "X-Forwarded-Proto").

This valve proceeds as follows:

If the incoming request.getRemoteAddr() matches the valve's list of internal proxies :

  • Loop on the comma delimited list of IPs and hostnames passed by the preceding load balancer or proxy in the given request's Http header named $remoteIPHeader (default value x-forwarded-for). Values are processed in right-to-left order.
  • For each ip/host of the list:
    • if it matches the internal proxies list, the ip/host is swallowed
    • if it matches the trusted proxies list, the ip/host is added to the created proxies header
    • otherwise, the ip/host is declared to be the remote ip and looping is stopped.
  • If the request http header named $protocolHeader (e.g. x-forwarded-for) equals to the value of protocolHeaderHttpsValue configuration parameter (default https) then request.isSecure = true, request.scheme = https and request.serverPort = 443. Note that 443 can be overwritten with the $httpsServerPort configuration parameter.

Configuration parameters:

RemoteIpValve propertyDescriptionEquivalent mod_remoteip directiveFormatDefault Value
remoteIPHeaderName of the Http Header read by this valve that holds the list of traversed IP addresses starting from the requesting clientRemoteIPHeaderCompliant http header namex-forwarded-for
internalProxiesList of internal proxies ip adress. If they appear in the remoteIpHeader value, they will be trusted and will not appear in the proxiesHeader valueRemoteIPInternalProxyComma delimited list of regular expressions (in the syntax supported by the java.util.regex.Pattern library)10\.\d{1,3}\.\d{1,3}\.\d{1,3}, 192\.168\.\d{1,3}\.\d{1,3}, 169\.254\.\d{1,3}\.\d{1,3}, 127\.\d{1,3}\.\d{1,3}\.\d{1,3}
By default, 10/8, 192.168/16, 169.254/16 and 127/8 are allowed ; 172.16/12 has not been enabled by default because it is complex to describe with regular expressions
proxiesHeaderName of the http header created by this valve to hold the list of proxies that have been processed in the incoming remoteIPHeaderRemoteIPProxiesHeaderCompliant http header namex-forwarded-by
trustedProxiesList of trusted proxies ip adress. If they appear in the remoteIpHeader value, they will be trusted and will appear in the proxiesHeader valueRemoteIPTrustedProxyComma delimited list of regular expressions (in the syntax supported by the java.util.regex.Pattern library) 
protocolHeaderName of the http header read by this valve that holds the flag that this request N/ACompliant http header name like X-Forwarded-Proto, X-Forwarded-Ssl or Front-End-Httpsnull
protocolHeaderHttpsValueValue of the protocolHeader to indicate that it is an Https requestN/AString like https or ONhttps
httpServerPortValue returned by javax.servlet.ServletRequest.getServerPort() when the protocolHeader indicates http protocolN/Ainteger80
httpsServerPortValue returned by javax.servlet.ServletRequest.getServerPort() when the protocolHeader indicates https protocolN/Ainteger443

This Valve may be attached to any Container, depending on the granularity of the filtering you wish to perform.

Regular expression vs. IP address blocks: mod_remoteip allows to use address blocks (e.g. 192.168/16) to configure RemoteIPInternalProxy and RemoteIPTrustedProxy ; as Tomcat doesn't have a library similar to apr_ipsubnet_test, RemoteIpValve uses regular expression to configure internalProxies and trustedProxies in the same fashion as RequestFilterValve does.


Sample with internal proxies

RemoteIpValve configuration:

 <Valve 
   className="org.apache.catalina.connector.RemoteIpValve"
   internalProxies="192\.168\.0\.10, 192\.168\.0\.11"
   remoteIPHeader="x-forwarded-for"
   remoteIPProxiesHeader="x-forwarded-by"
   protocolHeader="x-forwarded-proto"
   />

Request values:

propertyValue Before RemoteIpValveValue After RemoteIpValve
request.remoteAddr192.168.0.10140.211.11.130
request.header['x-forwarded-for']140.211.11.130, 192.168.0.10null
request.header['x-forwarded-by']nullnull
request.header['x-forwarded-proto']httpshttps
request.schemehttphttps
request.securefalsetrue
request.serverPort80443
Note : x-forwarded-by header is null because only internal proxies as been traversed by the request. x-forwarded-by is null because all the proxies are trusted or internal.

Sample with trusted proxies

RemoteIpValve configuration:

 <Valve 
   className="org.apache.catalina.connector.RemoteIpValve"
   internalProxies="192\.168\.0\.10, 192\.168\.0\.11"
   remoteIPHeader="x-forwarded-for"
   remoteIPProxiesHeader="x-forwarded-by"
   trustedProxies="proxy1, proxy2"
   />

Request values:

propertyValue Before RemoteIpValveValue After RemoteIpValve
request.remoteAddr192.168.0.10140.211.11.130
request.header['x-forwarded-for']140.211.11.130, proxy1, proxy2null
request.header['x-forwarded-by']nullproxy1, proxy2
Note : proxy1 and proxy2 are both trusted proxies that come in x-forwarded-for header, they both are migrated in x-forwarded-by header. x-forwarded-by is null because all the proxies are trusted or internal.

Sample with internal and trusted proxies

RemoteIpValve configuration:

 <Valve 
   className="org.apache.catalina.connector.RemoteIpValve"
   internalProxies="192\.168\.0\.10, 192\.168\.0\.11"
   remoteIPHeader="x-forwarded-for"
   remoteIPProxiesHeader="x-forwarded-by"
   trustedProxies="proxy1, proxy2"
   />

Request values:

propertyValue Before RemoteIpValveValue After RemoteIpValve
request.remoteAddr192.168.0.10140.211.11.130
request.header['x-forwarded-for']140.211.11.130, proxy1, proxy2, 192.168.0.10null
request.header['x-forwarded-by']nullproxy1, proxy2
Note : proxy1 and proxy2 are both trusted proxies that come in x-forwarded-for header, they both are migrated in x-forwarded-by header. As 192.168.0.10 is an internal proxy, it does not appear in x-forwarded-by. x-forwarded-by is null because all the proxies are trusted or internal.

Sample with an untrusted proxy

RemoteIpValve configuration:

 <Valve 
   className="org.apache.catalina.connector.RemoteIpValve"
   internalProxies="192\.168\.0\.10, 192\.168\.0\.11"
   remoteIPHeader="x-forwarded-for"
   remoteIPProxiesHeader="x-forwarded-by"
   trustedProxies="proxy1, proxy2"
   />

Request values:

propertyValue Before RemoteIpValveValue After RemoteIpValve
request.remoteAddr192.168.0.10untrusted-proxy
request.header['x-forwarded-for']140.211.11.130, untrusted-proxy, proxy1140.211.11.130
request.header['x-forwarded-by']nullproxy1
Note : x-forwarded-by holds the trusted proxy proxy1. x-forwarded-by holds 140.211.11.130 because untrusted-proxy is not trusted and thus, we can not trust that untrusted-proxy is the actual remote ip. request.remoteAddr is untrusted-proxy that is an IP verified by proxy1.
public class RemoteIpValve extends ValveBase {
    
    
java.util.regex.Pattern for a comma delimited string that support whitespace characters
    private static final Pattern commaSeparatedValuesPattern = Pattern.compile("\\s*,\\s*");
    
    
The descriptive information related to this implementation.
    private static final String info = "org.apache.catalina.connector.RemoteIpValve/1.0";
    
    
Convert a given comma delimited list of regular expressions into an array of compiled java.util.regex.Pattern

Returns:
array of patterns (not null)
    protected static Pattern[] commaDelimitedListToPatternArray(String commaDelimitedPatterns) {
        String[] patterns = commaDelimitedListToStringArray(commaDelimitedPatterns);
        List<PatternpatternsList = new ArrayList<Pattern>();
        for (String pattern : patterns) {
            try {
                patternsList.add(Pattern.compile(pattern));
            } catch (PatternSyntaxException e) {
                throw .remoteIpValvePatternError(patterne);
            }
        }
        return patternsList.toArray(new Pattern[0]);
    }
    
    
Convert a given comma delimited list of regular expressions into an array of String

Returns:
array of patterns (non null)
    protected static String[] commaDelimitedListToStringArray(String commaDelimitedStrings) {
        return (commaDelimitedStrings == null || commaDelimitedStrings.length() == 0) ? new String[0] : 
            .split(commaDelimitedStrings);
    }
    
    
Convert an array of strings in a comma delimited string
    protected static String listToCommaDelimitedString(List<StringstringList) {
        if (stringList == null) {
            return "";
        }
        StringBuilder result = new StringBuilder();
        for (Iterator<Stringit = stringList.iterator(); it.hasNext();) {
            Object element = it.next();
            if (element != null) {
                result.append(element);
                if (it.hasNext()) {
                    result.append(", ");
                }
            }
        }
        return result.toString();
    }
    
    
Return true if the given str matches at least one of the given patterns.
    protected static boolean matchesOne(String strPattern... patterns) {
        for (Pattern pattern : patterns) {
            if (pattern.matcher(str).matches()) {
                return true;
            }
        }
        return false;
    }
    
    
    private int httpServerPort = 80;
    
    
    private int httpsServerPort = 443;
    
    
    private Pattern[] internalProxies = new Pattern[] {
        Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"), Pattern.compile("192\\.168\\.\\d{1,3}\\.\\d{1,3}"),
        Pattern.compile("169\\.254\\.\\d{1,3}\\.\\d{1,3}"), Pattern.compile("127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")
    };
    
    
    private String protocolHeader = null;
    
    
    private String protocolHeaderHttpsValue = "https";
    
    
    private String proxiesHeader = "X-Forwarded-By";
    
    
    private String remoteIpHeader = "X-Forwarded-For";
    
    
    private Pattern[] trustedProxies = new Pattern[0];
    
    public int getHttpsServerPort() {
        return ;
    }
    
    public int getHttpServerPort() {
        return ;
    }
    
    
Return descriptive information about this Valve implementation.
    @Override
    public String getInfo() {
        return ;
    }
    
    

Returns:
comma delimited list of internal proxies
See also:
setInternalProxies(java.lang.String)
    public String getInternalProxies() {
        List<StringinternalProxiesAsStringList = new ArrayList<String>();
        for (Pattern internalProxyPattern : ) {
            internalProxiesAsStringList.add(String.valueOf(internalProxyPattern));
        }
        return listToCommaDelimitedString(internalProxiesAsStringList);
    }
    
    

Returns:
the protocol header (e.g. "X-Forwarded-Proto")
See also:
setProtocolHeader(java.lang.String)
    public String getProtocolHeader() {
        return ;
    }
    
    

Returns:
the value of the protocol header for incoming https request (e.g. "https")
See also:
setProtocolHeaderHttpsValue(java.lang.String)
        return ;
    }
    
    

Returns:
the proxies header name (e.g. "X-Forwarded-By")
See also:
setProxiesHeader(java.lang.String)
    public String getProxiesHeader() {
        return ;
    }
    
    

Returns:
the remote IP header name (e.g. "X-Forwarded-For")
See also:
setRemoteIpHeader(java.lang.String)
    public String getRemoteIpHeader() {
        return ;
    }

    

Returns:
comma delimited list of trusted proxies
See also:
setTrustedProxies(java.lang.String)
    public String getTrustedProxies() {
        List<StringtrustedProxiesAsStringList = new ArrayList<String>();
        for (Pattern trustedProxy : ) {
            trustedProxiesAsStringList.add(String.valueOf(trustedProxy));
        }
        return listToCommaDelimitedString(trustedProxiesAsStringList);
    }
    
    
    @Override
    public void invoke(Request requestResponse responsethrows IOExceptionServletException {
        final String originalRemoteAddr = request.getRemoteAddr();
        final String originalRemoteHost = request.getRemoteHost();
        final String originalScheme = request.getScheme();
        final boolean originalSecure = request.isSecure();
        final int originalServerPort = request.getServerPort();
        
        if (matchesOne(originalRemoteAddr)) {
            String remoteIp = null;
            // In java 6, proxiesHeaderValue should be declared as a java.util.Deque
            LinkedList<StringproxiesHeaderValue = new LinkedList<String>();
            
            String[] remoteIPHeaderValue = commaDelimitedListToStringArray(request.getHeader());
            int idx;
            // loop on remoteIPHeaderValue to find the first trusted remote ip and to build the proxies chain
            for (idx = remoteIPHeaderValue.length - 1; idx >= 0; idx--) {
                String currentRemoteIp = remoteIPHeaderValue[idx];
                remoteIp = currentRemoteIp;
                if (matchesOne(currentRemoteIp)) {
                    // do nothing, internalProxies IPs are not appended to the
                } else if (matchesOne(currentRemoteIp)) {
                    proxiesHeaderValue.addFirst(currentRemoteIp);
                } else {
                    idx--; // decrement idx because break statement doesn't do it
                    break;
                }
            }
            // continue to loop on remoteIPHeaderValue to build the new value of the remoteIPHeader
            LinkedList<StringnewRemoteIpHeaderValue = new LinkedList<String>();
            for (; idx >= 0; idx--) {
                String currentRemoteIp = remoteIPHeaderValue[idx];
                newRemoteIpHeaderValue.addFirst(currentRemoteIp);
            }
            if (remoteIp != null) {
                
                request.setRemoteAddr(remoteIp);
                request.setRemoteHost(remoteIp);
                
                // use request.coyoteRequest.mimeHeaders.setValue(str).setString(str) because request.addHeader(str, str) is no-op in Tomcat
                // 6.0
                if (proxiesHeaderValue.size() == 0) {
                    request.getCoyoteRequest().getMimeHeaders().removeHeader();
                } else {
                    String commaDelimitedListOfProxies = listToCommaDelimitedString(proxiesHeaderValue);
                    request.getCoyoteRequest().getMimeHeaders().setValue().setString(commaDelimitedListOfProxies);
                }
                if (newRemoteIpHeaderValue.size() == 0) {
                    request.getCoyoteRequest().getMimeHeaders().removeHeader();
                } else {
                    String commaDelimitedRemoteIpHeaderValue = listToCommaDelimitedString(newRemoteIpHeaderValue);
                    request.getCoyoteRequest().getMimeHeaders().setValue().setString(commaDelimitedRemoteIpHeaderValue);
                }
            }
            
            if ( != null) {
                String protocolHeaderValue = request.getHeader();
                if (protocolHeaderValue == null) {
                    // don't modify the secure,scheme and serverPort attributes
                    // of the request
                } else if (.equalsIgnoreCase(protocolHeaderValue)) {
                    request.setSecure(true);
                    // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0
                    request.getCoyoteRequest().scheme().setString("https");
                    
                    request.setServerPort();
                } else {
                    request.setSecure(false);
                    // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0
                    request.getCoyoteRequest().scheme().setString("http");
                    
                    request.setServerPort();
                }
            }
            
            if (..isDebugEnabled()) {
                ..debug("Incoming request " + request.getRequestURI() + " with originalRemoteAddr '" + originalRemoteAddr
                          + "', originalRemoteHost='" + originalRemoteHost + "', originalSecure='" + originalSecure + "', originalScheme='"
                          + originalScheme + "' will be seen as newRemoteAddr='" + request.getRemoteAddr() + "', newRemoteHost='"
                          + request.getRemoteHost() + "', newScheme='" + request.getScheme() + "', newSecure='" + request.isSecure() + "'");
            }
        }
        try {
            getNext().invoke(requestresponse);
        } finally {
            request.setRemoteAddr(originalRemoteAddr);
            request.setRemoteHost(originalRemoteHost);
            
            request.setSecure(originalSecure);
            
            // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0
            request.getCoyoteRequest().scheme().setString(originalScheme);
            
            request.setServerPort(originalServerPort);
        }
    }
    
    

Server Port value if the protocolHeader is not null and does not indicate HTTP

Default value : 80

    public void setHttpServerPort(int httpServerPort) {
        this. = httpServerPort;
    }
    
    

Server Port value if the protocolHeader indicates HTTPS

Default value : 443

    public void setHttpsServerPort(int httpsServerPort) {
        this. = httpsServerPort;
    }
    
    

Comma delimited list of internal proxies. Can be expressed with regular expressions.

Default value : 10\.\d{1,3}\.\d{1,3}\.\d{1,3}, 192\.168\.\d{1,3}\.\d{1,3}, 127\.\d{1,3}\.\d{1,3}\.\d{1,3}

    public void setInternalProxies(String commaDelimitedInternalProxies) {
        this. = commaDelimitedListToPatternArray(commaDelimitedInternalProxies);
    }
    
    

Header that holds the incoming protocol, usally named X-Forwarded-Proto. If null, request.scheme and request.secure will not be modified.

Default value : null

    public void setProtocolHeader(String protocolHeader) {
        this. = protocolHeader;
    }
    
    

Case insensitive value of the protocol header to indicate that the incoming http request uses SSL.

Default value : https

    public void setProtocolHeaderHttpsValue(String protocolHeaderHttpsValue) {
        this. = protocolHeaderHttpsValue;
    }
    
    

The proxiesHeader directive specifies a header into which mod_remoteip will collect a list of all of the intermediate client IP addresses trusted to resolve the actual remote IP. Note that intermediate RemoteIPTrustedProxy addresses are recorded in this header, while any intermediate RemoteIPInternalProxy addresses are discarded.

Name of the http header that holds the list of trusted proxies that has been traversed by the http request.

The value of this header can be comma delimited.

Default value : X-Forwarded-By

    public void setProxiesHeader(String proxiesHeader) {
        this. = proxiesHeader;
    }
    
    

Name of the http header from which the remote ip is extracted.

The value of this header can be comma delimited.

Default value : X-Forwarded-For

Parameters:
remoteIpHeader
    public void setRemoteIpHeader(String remoteIpHeader) {
        this. = remoteIpHeader;
    }
    
    

Comma delimited list of proxies that are trusted when they appear in the remoteIpHeader header. Can be expressed as a regular expression.

Default value : empty list, no external proxy is trusted.

    public void setTrustedProxies(String commaDelimitedTrustedProxies) {
        this. = commaDelimitedListToPatternArray(commaDelimitedTrustedProxies);
    }
New to GrepCode? Check out our FAQ X