Skip to content

Commit 67c3af9

Browse files
committed
Implement X-Forwarded-Host support for RemoteIpFilter and RemoteIpValve
1 parent 425b037 commit 67c3af9

File tree

11 files changed

+446
-29
lines changed

11 files changed

+446
-29
lines changed

java/org/apache/catalina/AccessLog.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,35 @@ public interface AccessLog {
3838
* the AccessLog.
3939
*/
4040
public static final String REMOTE_ADDR_ATTRIBUTE =
41-
"org.apache.catalina.AccessLog.RemoteAddr";
41+
"org.apache.catalina.AccessLog.RemoteAddr";
4242

4343
/**
4444
* Name of request attribute used to override remote host name recorded by
4545
* the AccessLog.
4646
*/
4747
public static final String REMOTE_HOST_ATTRIBUTE =
48-
"org.apache.catalina.AccessLog.RemoteHost";
48+
"org.apache.catalina.AccessLog.RemoteHost";
4949

5050
/**
5151
* Name of request attribute used to override the protocol recorded by the
5252
* AccessLog.
5353
*/
5454
public static final String PROTOCOL_ATTRIBUTE =
55-
"org.apache.catalina.AccessLog.Protocol";
55+
"org.apache.catalina.AccessLog.Protocol";
56+
57+
/**
58+
* Name of request attribute used to override the server name recorded by
59+
* the AccessLog.
60+
*/
61+
public static final String SERVER_NAME_ATTRIBUTE =
62+
"org.apache.catalina.AccessLog.ServerName";
5663

5764
/**
5865
* Name of request attribute used to override the server port recorded by
5966
* the AccessLog.
6067
*/
6168
public static final String SERVER_PORT_ATTRIBUTE =
62-
"org.apache.catalina.AccessLog.ServerPort";
69+
"org.apache.catalina.AccessLog.ServerPort";
6370

6471

6572
/**

java/org/apache/catalina/filters/LocalStrings.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ httpHeaderSecurityFilter.committed=Unable to add HTTP headers since response is
5555
remoteCidrFilter.invalid=Invalid configuration provided for [{0}]. See previous messages for details.
5656
remoteCidrFilter.noRemoteIp=Client does not have an IP address. Request denied.
5757

58+
remoteIpFilter.invalidHostHeader=Invalid value [{0}] found for Host in HTTP header [{1}]
59+
remoteIpFilter.invalidHostWithPort=Host value [{0}] in HTTP header [{1}] included a port number which will be ignored
5860
remoteIpFilter.invalidNumber=Illegal number for parameter [{0}]: [{1}]
5961

6062
requestFilter.deny=Denied request for [{0}] based on property [{1}]

java/org/apache/catalina/filters/RemoteIpFilter.java

Lines changed: 105 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.apache.juli.logging.Log;
4646
import org.apache.juli.logging.LogFactory;
4747
import org.apache.tomcat.util.http.FastHttpDateFormat;
48+
import org.apache.tomcat.util.http.parser.Host;
4849
import org.apache.tomcat.util.res.StringManager;
4950

5051
/**
@@ -448,6 +449,8 @@ public static class XForwardedRequest extends HttpServletRequestWrapper {
448449

449450
protected final Map<String, List<String>> headers;
450451

452+
protected String localName;
453+
451454
protected int localPort;
452455

453456
protected String remoteAddr;
@@ -458,15 +461,19 @@ public static class XForwardedRequest extends HttpServletRequestWrapper {
458461

459462
protected boolean secure;
460463

464+
protected String serverName;
465+
461466
protected int serverPort;
462467

463468
public XForwardedRequest(HttpServletRequest request) {
464469
super(request);
470+
this.localName = request.getLocalName();
465471
this.localPort = request.getLocalPort();
466472
this.remoteAddr = request.getRemoteAddr();
467473
this.remoteHost = request.getRemoteHost();
468474
this.scheme = request.getScheme();
469475
this.secure = request.isSecure();
476+
this.serverName = request.getServerName();
470477
this.serverPort = request.getServerPort();
471478

472479
headers = new HashMap<>();
@@ -530,6 +537,11 @@ public int getIntHeader(String name) {
530537
return Integer.parseInt(value);
531538
}
532539

540+
@Override
541+
public String getLocalName() {
542+
return localName;
543+
}
544+
533545
@Override
534546
public int getLocalPort() {
535547
return localPort;
@@ -550,6 +562,11 @@ public String getScheme() {
550562
return scheme;
551563
}
552564

565+
@Override
566+
public String getServerName() {
567+
return serverName;
568+
}
569+
553570
@Override
554571
public int getServerPort() {
555572
return serverPort;
@@ -578,6 +595,10 @@ public void setHeader(String name, String value) {
578595

579596
}
580597

598+
public void setLocalName(String localName) {
599+
this.localName = localName;
600+
}
601+
581602
public void setLocalPort(int localPort) {
582603
this.localPort = localPort;
583604
}
@@ -598,6 +619,10 @@ public void setSecure(boolean secure) {
598619
this.secure = secure;
599620
}
600621

622+
public void setServerName(String serverName) {
623+
this.serverName = serverName;
624+
}
625+
601626
public void setServerPort(int serverPort) {
602627
this.serverPort = serverPort;
603628
}
@@ -642,8 +667,12 @@ public PushBuilder newPushBuilder() {
642667

643668
protected static final String PROTOCOL_HEADER_HTTPS_VALUE_PARAMETER = "protocolHeaderHttpsValue";
644669

670+
protected static final String HOST_HEADER_PARAMETER = "hostHeader";
671+
645672
protected static final String PORT_HEADER_PARAMETER = "portHeader";
646673

674+
protected static final String CHANGE_LOCAL_NAME_PARAMETER = "changeLocalName";
675+
647676
protected static final String CHANGE_LOCAL_PORT_PARAMETER = "changeLocalPort";
648677

649678
protected static final String PROXIES_HEADER_PARAMETER = "proxiesHeader";
@@ -716,6 +745,10 @@ protected static String listToCommaDelimitedString(List<String> stringList) {
716745

717746
private String protocolHeaderHttpsValue = "https";
718747

748+
private String hostHeader = null;
749+
750+
private boolean changeLocalName = false;
751+
719752
private String portHeader = null;
720753

721754
private boolean changeLocalPort = false;
@@ -822,17 +855,37 @@ public void doFilter(HttpServletRequest request, HttpServletResponse response, F
822855
}
823856
}
824857

858+
if (hostHeader != null) {
859+
String hostHeaderValue = request.getHeader(hostHeader);
860+
if (hostHeaderValue != null) {
861+
try {
862+
int portIndex = Host.parse(hostHeaderValue);
863+
if (portIndex > -1) {
864+
log.debug(sm.getString("remoteIpFilter.invalidHostWithPort", hostHeaderValue, hostHeader));
865+
hostHeaderValue = hostHeaderValue.substring(0, portIndex);
866+
}
867+
868+
xRequest.setServerName(hostHeaderValue);
869+
if (isChangeLocalName()) {
870+
xRequest.setLocalName(hostHeaderValue);
871+
}
872+
873+
} catch (IllegalArgumentException iae) {
874+
log.debug(sm.getString("remoteIpFilter.invalidHostHeader", hostHeaderValue, hostHeader));
875+
}
876+
}
877+
}
825878
request.setAttribute(Globals.REQUEST_FORWARDED_ATTRIBUTE, Boolean.TRUE);
826879

827880
if (log.isDebugEnabled()) {
828-
log.debug("Incoming request " + request.getRequestURI() + " with originalRemoteAddr '" + request.getRemoteAddr()
829-
+ "', originalRemoteHost='" + request.getRemoteHost() + "', originalSecure='" + request.isSecure()
830-
+ "', originalScheme='" + request.getScheme() + "', original[" + remoteIpHeader + "]='"
831-
+ concatRemoteIpHeaderValue + "', original[" + protocolHeader + "]='"
832-
+ (protocolHeader == null ? null : request.getHeader(protocolHeader)) + "' will be seen as newRemoteAddr='"
833-
+ xRequest.getRemoteAddr() + "', newRemoteHost='" + xRequest.getRemoteHost() + "', newScheme='"
834-
+ xRequest.getScheme() + "', newSecure='" + xRequest.isSecure() + "', new[" + remoteIpHeader + "]='"
835-
+ xRequest.getHeader(remoteIpHeader) + "', new[" + proxiesHeader + "]='" + xRequest.getHeader(proxiesHeader) + "'");
881+
log.debug("Incoming request " + request.getRequestURI() + " with originalRemoteAddr [" + request.getRemoteAddr() +
882+
"], originalRemoteHost=[" + request.getRemoteHost() + "], originalSecure=[" + request.isSecure() +
883+
"], originalScheme=[" + request.getScheme() + "], originalServerName=[" + request.getServerName() +
884+
"], originalServerPort=[" + request.getServerPort() +
885+
"] will be seen as newRemoteAddr=[" + xRequest.getRemoteAddr() +
886+
"], newRemoteHost=[" + xRequest.getRemoteHost() + "], newSecure=[" + xRequest.isSecure() +
887+
"], newScheme=[" + xRequest.getScheme() + "], newServerName=[" + xRequest.getServerName() +
888+
"], newServerPort=[" + xRequest.getServerPort() + "]");
836889
}
837890
if (requestAttributesEnabled) {
838891
request.setAttribute(AccessLog.REMOTE_ADDR_ATTRIBUTE,
@@ -843,6 +896,8 @@ public void doFilter(HttpServletRequest request, HttpServletResponse response, F
843896
xRequest.getRemoteHost());
844897
request.setAttribute(AccessLog.PROTOCOL_ATTRIBUTE,
845898
xRequest.getProtocol());
899+
request.setAttribute(AccessLog.SERVER_NAME_ATTRIBUTE,
900+
xRequest.getServerName());
846901
request.setAttribute(AccessLog.SERVER_PORT_ATTRIBUTE,
847902
Integer.valueOf(xRequest.getServerPort()));
848903
}
@@ -909,6 +964,10 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
909964
}
910965
}
911966

967+
public boolean isChangeLocalName() {
968+
return changeLocalName;
969+
}
970+
912971
public boolean isChangeLocalPort() {
913972
return changeLocalPort;
914973
}
@@ -968,10 +1027,18 @@ public void init() throws ServletException {
9681027
setProtocolHeaderHttpsValue(getInitParameter(PROTOCOL_HEADER_HTTPS_VALUE_PARAMETER));
9691028
}
9701029

1030+
if (getInitParameter(HOST_HEADER_PARAMETER) != null) {
1031+
setHostHeader(getInitParameter(HOST_HEADER_PARAMETER));
1032+
}
1033+
9711034
if (getInitParameter(PORT_HEADER_PARAMETER) != null) {
9721035
setPortHeader(getInitParameter(PORT_HEADER_PARAMETER));
9731036
}
9741037

1038+
if (getInitParameter(CHANGE_LOCAL_NAME_PARAMETER) != null) {
1039+
setChangeLocalName(Boolean.parseBoolean(getInitParameter(CHANGE_LOCAL_NAME_PARAMETER)));
1040+
}
1041+
9751042
if (getInitParameter(CHANGE_LOCAL_PORT_PARAMETER) != null) {
9761043
setChangeLocalPort(Boolean.parseBoolean(getInitParameter(CHANGE_LOCAL_PORT_PARAMETER)));
9771044
}
@@ -1005,6 +1072,22 @@ public void init() throws ServletException {
10051072
}
10061073
}
10071074

1075+
/**
1076+
* <p>
1077+
* If <code>true</code>, the return values for both {@link
1078+
* ServletRequest#getLocalName()} and {@link ServletRequest#getServerName()}
1079+
* will be modified by this Filter rather than just
1080+
* {@link ServletRequest#getServerName()}.
1081+
* </p>
1082+
* <p>
1083+
* Default value : <code>false</code>
1084+
* </p>
1085+
* @param changeLocalName The new flag value
1086+
*/
1087+
public void setChangeLocalName(boolean changeLocalName) {
1088+
this.changeLocalName = changeLocalName;
1089+
}
1090+
10081091
/**
10091092
* <p>
10101093
* If <code>true</code>, the return values for both {@link
@@ -1065,6 +1148,20 @@ public void setInternalProxies(String internalProxies) {
10651148
}
10661149
}
10671150

1151+
/**
1152+
* <p>
1153+
* Header that holds the incoming host, usually named
1154+
* <code>X-Forwarded-HOst</code>.
1155+
* </p>
1156+
* <p>
1157+
* Default value : <code>null</code>
1158+
* </p>
1159+
* @param hostHeader The header name
1160+
*/
1161+
public void setHostHeader(String hostHeader) {
1162+
this.hostHeader = hostHeader;
1163+
}
1164+
10681165
/**
10691166
* <p>
10701167
* Header that holds the incoming port, usually named

java/org/apache/catalina/valves/AbstractAccessLogValve.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,11 +1394,24 @@ protected class LocalServerNameElement implements AccessLogElement {
13941394
@Override
13951395
public void addElement(CharArrayWriter buf, Date date, Request request,
13961396
Response response, long time) {
1397+
String value = null;
1398+
if (requestAttributesEnabled) {
1399+
Object serverName = request.getAttribute(SERVER_NAME_ATTRIBUTE);
1400+
if (serverName != null) {
1401+
value = serverName.toString();
1402+
}
1403+
}
1404+
if (value == null || value.length() == 0) {
1405+
value = request.getServerName();
1406+
}
1407+
if (value == null || value.length() == 0) {
1408+
value = "-";
1409+
}
1410+
13971411
if (ipv6Canonical) {
1398-
buf.append(IPv6Utils.canonize(request.getServerName()));
1399-
} else {
1400-
buf.append(request.getServerName());
1412+
value = IPv6Utils.canonize(value);
14011413
}
1414+
buf.append(value);
14021415
}
14031416
}
14041417

java/org/apache/catalina/valves/LocalStrings.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ jdbcAccessLogValve.exception=Exception performing insert access entry
132132
remoteCidrValve.invalid=Invalid configuration provided for [{0}]. See previous messages for details.
133133
remoteCidrValve.noRemoteIp=Client does not have an IP address. Request denied.
134134

135+
remoteIpValve.invalidHostHeader=Invalid value [{0}] found for Host in HTTP header [{1}]
136+
remoteIpValve.invalidHostWithPort=Host value [{0}] in HTTP header [{1}] included a port number which will be ignored
135137
remoteIpValve.invalidPortHeader=Invalid value [{0}] found for port in HTTP header [{1}]
136138

137139
requestFilterValve.configInvalid=One or more invalid configuration settings were provided for the Remote[Addr|Host]Valve which prevented the Valve and its parent containers from starting

0 commit comments

Comments
 (0)