001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.protocol.http.servlet; 018 019import java.util.regex.Pattern; 020 021import javax.servlet.FilterConfig; 022import javax.servlet.ServletRequest; 023import javax.servlet.http.HttpServletRequest; 024import javax.servlet.http.HttpServletRequestWrapper; 025 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028 029/** 030 * Sets {@link ServletRequest#isSecure()} to <code>true</code> if 031 * {@link ServletRequest#getRemoteAddr()} matches one of the <code>securedRemoteAddresses</code> of 032 * this filter. 033 * <p> 034 * This filter is often used in combination with {@link XForwardedRequestWrapperFactory} to get the 035 * remote address of the client even if the request goes through load balancers (e.g. F5 Big IP, 036 * Nortel Alteon) or proxies (e.g. Apache mod_proxy_http) 037 * <p> 038 * <strong>Configuration parameters:</strong> 039 * <table border="1"> 040 * <caption>Configuration parameters</caption> 041 * <tr> 042 * <th>XForwardedFilter property</th> 043 * <th>Description</th> 044 * <th>Format</th> 045 * <th>Default value</th> 046 * </tr> 047 * <tr> 048 * <td>securedRemoteAddresses</td> 049 * <td>IP addresses for which {@link ServletRequest#isSecure()} must return <code>true</code></td> 050 * <td>Comma delimited list of regular expressions (in the syntax supported by the 051 * {@link java.util.regex.Pattern} library)</td> 052 * <td>Class A, B and C <a href="http://en.wikipedia.org/wiki/Private_network">private network IP 053 * address blocks</a> : 10\.\d{1,3}\.\d{1,3}\.\d{1,3}, 192\.168\.\d{1,3}\.\d{1,3}, 054 * 172\\.(?:1[6-9]|2\\d|3[0-1]).\\d{1,3}.\\d{1,3}, 169\.254\.\d{1,3}\.\d{1,3}, 055 * 127\.\d{1,3}\.\d{1,3}\.\d{1,3}</td> 056 * </tr> 057 * </table> 058 * Note : the default configuration is can usually be used as internal servers are often trusted. 059 * </p> 060 * <p> 061 * <strong>Sample with secured remote addresses limited to 192.168.0.10 and 192.168.0.11</strong> 062 * </p> 063 * <p> 064 * SecuredRemoteAddressFilter configuration sample : 065 * </p> 066 * 067 * <code><pre> 068 * <filter> 069 * <filter-name>SecuredRemoteAddressFilter</filter-name> 070 * <filter-class>fr.xebia.servlet.filter.SecuredRemoteAddressFilter</filter-class> 071 * <init-param> 072 * <param-name>securedRemoteAddresses</param-name><param-value>192\.168\.0\.10, 192\.168\.0\.11</param-value> 073 * </init-param> 074 * </filter> 075 * 076 * <filter-mapping> 077 * <filter-name>SecuredRemoteAddressFilter</filter-name> 078 * <url-pattern>/*</url-pattern> 079 * <dispatcher>REQUEST</dispatcher> 080 * </filter-mapping></pre></code> 081 * <p> 082 * A request with <code>{@link ServletRequest#getRemoteAddr()} = 192.168.0.10 or 192.168.0.11</code> 083 * will be seen as <code>{@link ServletRequest#isSecure()} == true</code> even if 084 * <code>{@link HttpServletRequest#getScheme()} == "http"</code>. 085 * </p> 086 * 087 * @author <a href="mailto:cyrille@cyrilleleclerc.com">Cyrille Le Clerc</a> 088 * @author Juergen Donnerstag 089 */ 090public class SecuredRemoteAddressRequestWrapperFactory extends AbstractRequestWrapperFactory 091{ 092 /** Logger */ 093 private static final Logger log = LoggerFactory.getLogger(SecuredRemoteAddressRequestWrapperFactory.class); 094 095 private final static String SECURED_REMOTE_ADDRESSES_PARAMETER = "securedRemoteAddresses"; 096 097 public static class Config 098 { 099 /** @see #setSecuredRemoteAdresses(String) */ 100 private Pattern[] securedRemoteAddresses = new Pattern[] { 101 Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"), 102 Pattern.compile("192\\.168\\.\\d{1,3}\\.\\d{1,3}"), 103 Pattern.compile("172\\.(?:1[6-9]|2\\d|3[0-1]).\\d{1,3}.\\d{1,3}"), 104 Pattern.compile("169\\.254\\.\\d{1,3}\\.\\d{1,3}"), 105 Pattern.compile("127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}") }; 106 107 /** 108 * Comma delimited list of secured remote addresses. Expressed with regular expressions. 109 * <p> 110 * Default value : 10\.\d{1,3}\.\d{1,3}\.\d{1,3}, 192\.168\.\d{1,3}\.\d{1,3}, 111 * 172\\.(?:1[6-9]|2\\d|3[0-1]).\\d{1,3}.\\d{1,3}, 169\.254\.\d{1,3}\.\d{1,3}, 112 * 127\.\d{1,3}\.\d{1,3}\.\d{1,3} 113 * 114 * @param comaDelimitedSecuredRemoteAddresses 115 */ 116 public void setSecuredRemoteAdresses(final String comaDelimitedSecuredRemoteAddresses) 117 { 118 securedRemoteAddresses = commaDelimitedListToPatternArray(comaDelimitedSecuredRemoteAddresses); 119 } 120 } 121 122 // Filter Config 123 private Config config = new Config(); 124 125 /** 126 * Construct. 127 */ 128 public SecuredRemoteAddressRequestWrapperFactory() 129 { 130 } 131 132 /** 133 * @return SecuredRemoteAddress and XForwarded filter specific config 134 */ 135 public final Config getConfig() 136 { 137 return config; 138 } 139 140 /** 141 * The Wicket application might want to provide its own config 142 * 143 * @param config 144 */ 145 public final void setConfig(final Config config) 146 { 147 this.config = config; 148 } 149 150 @Override 151 public HttpServletRequest getWrapper(final HttpServletRequest request) 152 { 153 HttpServletRequest xRequest = super.getWrapper(request); 154 155 if (log.isDebugEnabled()) 156 { 157 log.debug("Incoming request uri=" + request.getRequestURI() + " with originalSecure='" + 158 request.isSecure() + "', remoteAddr='" + request.getRemoteAddr() + 159 "' will be seen with newSecure='" + xRequest.isSecure() + "'"); 160 } 161 162 return xRequest; 163 } 164 165 @Override 166 public boolean needsWrapper(final HttpServletRequest request) 167 { 168 return !request.isSecure() && 169 matchesOne(request.getRemoteAddr(), config.securedRemoteAddresses) == false; 170 } 171 172 /** 173 * If incoming remote address matches one of the declared IP pattern, wraps the incoming 174 * {@link HttpServletRequest} to override {@link HttpServletRequest#isSecure()} to set it to 175 * <code>true</code>. 176 */ 177 @Override 178 public HttpServletRequest newRequestWrapper(final HttpServletRequest request) 179 { 180 return new HttpServletRequestWrapper(request) 181 { 182 @Override 183 public boolean isSecure() 184 { 185 return true; 186 } 187 }; 188 } 189 190 /** 191 * @param filterConfig 192 */ 193 public void init(final FilterConfig filterConfig) 194 { 195 String comaDelimitedSecuredRemoteAddresses = filterConfig.getInitParameter(SECURED_REMOTE_ADDRESSES_PARAMETER); 196 if (comaDelimitedSecuredRemoteAddresses != null) 197 { 198 config.setSecuredRemoteAdresses(comaDelimitedSecuredRemoteAddresses); 199 } 200 } 201}