/* * 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.wicket.protocol.http.servlet; import java.util.regex.Pattern; import javax.servlet.FilterConfig; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Sets {@link ServletRequest#isSecure()} to <code>true</code> if * {@link ServletRequest#getRemoteAddr()} matches one of the <code>securedRemoteAddresses</code> of * this filter. * <p> * This filter is often used in combination with {@link XForwardedRequestWrapperFactory} to get the * remote address of the client even if the request goes through load balancers (e.g. F5 Big IP, * Nortel Alteon) or proxies (e.g. Apache mod_proxy_http) * <p> * <strong>Configuration parameters:</strong> * <table border="1"> * <tr> * <th>XForwardedFilter property</th> * <th>Description</th> * <th>Format</th> * <th>Default value</th> * </tr> * <tr> * <td>securedRemoteAddresses</td> * <td>IP addresses for which {@link ServletRequest#isSecure()} must return <code>true</code></td> * <td>Comma delimited list of regular expressions (in the syntax supported by the * {@link java.util.regex.Pattern} library)</td> * <td>Class A, B and C <a href="http://en.wikipedia.org/wiki/Private_network">private network IP * address blocks</a> : 10\.\d{1,3}\.\d{1,3}\.\d{1,3}, 192\.168\.\d{1,3}\.\d{1,3}, * 172\\.(?:1[6-9]|2\\d|3[0-1]).\\d{1,3}.\\d{1,3}, 169\.254\.\d{1,3}\.\d{1,3}, * 127\.\d{1,3}\.\d{1,3}\.\d{1,3}</td> * </tr> * </table> * Note : the default configuration is can usually be used as internal servers are often trusted. * </p> * <p> * <strong>Sample with secured remote addresses limited to 192.168.0.10 and 192.168.0.11</strong> * </p> * <p> * SecuredRemoteAddressFilter configuration sample : * </p> * * <code><pre> * <filter> * <filter-name>SecuredRemoteAddressFilter</filter-name> * <filter-class>fr.xebia.servlet.filter.SecuredRemoteAddressFilter</filter-class> * <init-param> * <param-name>securedRemoteAddresses</param-name><param-value>192\.168\.0\.10, 192\.168\.0\.11</param-value> * </init-param> * </filter> * * <filter-mapping> * <filter-name>SecuredRemoteAddressFilter</filter-name> * <url-pattern>/*</url-pattern> * <dispatcher>REQUEST</dispatcher> * </filter-mapping></pre></code> * <p> * A request with <code>{@link ServletRequest#getRemoteAddr()} = 192.168.0.10 or 192.168.0.11</code> * will be seen as <code>{@link ServletRequest#isSecure()} == true</code> even if * <code>{@link HttpServletRequest#getScheme()} == "http"</code>. * </p> * * @author <a href="mailto:cyrille@cyrilleleclerc.com">Cyrille Le Clerc</a> * @author Juergen Donnerstag */ public class SecuredRemoteAddressRequestWrapperFactory extends AbstractRequestWrapperFactory { /** Logger */ private static final Logger log = LoggerFactory.getLogger(SecuredRemoteAddressRequestWrapperFactory.class); private final static String SECURED_REMOTE_ADDRESSES_PARAMETER = "securedRemoteAddresses"; /** */ public static class Config { /** @see #setSecuredRemoteAdresses(String) */ private Pattern[] securedRemoteAddresses = 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("172\\.(?:1[6-9]|2\\d|3[0-1]).\\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}") }; /** * Comma delimited list of secured remote addresses. Expressed with regular expressions. * <p> * Default value : 10\.\d{1,3}\.\d{1,3}\.\d{1,3}, 192\.168\.\d{1,3}\.\d{1,3}, * 172\\.(?:1[6-9]|2\\d|3[0-1]).\\d{1,3}.\\d{1,3}, 169\.254\.\d{1,3}\.\d{1,3}, * 127\.\d{1,3}\.\d{1,3}\.\d{1,3} * * @param comaDelimitedSecuredRemoteAddresses */ public void setSecuredRemoteAdresses(final String comaDelimitedSecuredRemoteAddresses) { securedRemoteAddresses = commaDelimitedListToPatternArray(comaDelimitedSecuredRemoteAddresses); } } // Filter Config private Config config = new Config(); /** * Construct. */ public SecuredRemoteAddressRequestWrapperFactory() { } /** * @return SecuredRemoteAddress and XForwarded filter specific config */ public final Config getConfig() { return config; } /** * The Wicket application might want to provide its own config * * @param config */ public final void setConfig(final Config config) { this.config = config; } /** * {@inheritDoc} */ @Override public HttpServletRequest getWrapper(final HttpServletRequest request) { HttpServletRequest xRequest = super.getWrapper(request); if (log.isDebugEnabled()) { log.debug("Incoming request uri=" + request.getRequestURI() + " with originalSecure='" + request.isSecure() + "', remoteAddr='" + request.getRemoteAddr() + "' will be seen with newSecure='" + request.isSecure() + "'"); } return xRequest; } /** * {@inheritDoc} */ @Override public boolean needsWrapper(final HttpServletRequest request) { return !request.isSecure() && matchesOne(request.getRemoteAddr(), config.securedRemoteAddresses) == false; } /** * If incoming remote address matches one of the declared IP pattern, wraps the incoming * {@link HttpServletRequest} to override {@link HttpServletRequest#isSecure()} to set it to * <code>true</code>. */ @Override public HttpServletRequest newRequestWrapper(final HttpServletRequest request) { return new HttpServletRequestWrapper(request) { @Override public boolean isSecure() { return true; } }; } /** * @param filterConfig */ public void init(final FilterConfig filterConfig) { String comaDelimitedSecuredRemoteAddresses = filterConfig.getInitParameter(SECURED_REMOTE_ADDRESSES_PARAMETER); if (comaDelimitedSecuredRemoteAddresses != null) { config.setSecuredRemoteAdresses(comaDelimitedSecuredRemoteAddresses); } } }