Issue #5247 - Priority based ForwardedRequestCustomizer

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
Joakim Erdfelt 2020-09-09 14:44:58 -05:00
parent 165e59b3e2
commit f74cada660
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
1 changed files with 187 additions and 130 deletions

View File

@ -30,6 +30,7 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.QuotedCSVParser; import org.eclipse.jetty.http.QuotedCSVParser;
import org.eclipse.jetty.server.HttpConfiguration.Customizer; import org.eclipse.jetty.server.HttpConfiguration.Customizer;
import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.ArrayTrie;
@ -151,7 +152,7 @@ public class ForwardedRequestCustomizer implements Customizer
*/ */
public void setForcedHost(String hostAndPort) public void setForcedHost(String hostAndPort)
{ {
_forcedHost = new HostPortHttpField(new ForcedHostPort(hostAndPort)); _forcedHost = new HostPortHttpField(hostAndPort);
} }
/** /**
@ -398,26 +399,16 @@ public class ForwardedRequestCustomizer implements Customizer
request.setSecure(true); request.setSecure(true);
} }
if (forwarded._server != null && forwarded._host instanceof PortSetHostPort) if (forwarded.hasAuthority())
{ {
httpFields.put(new HostPortHttpField(forwarded._server, forwarded._host.getPort())); httpFields.put(new HostPortHttpField(forwarded._authority._host, forwarded._authority._port));
request.setAuthority(forwarded._server, forwarded._host.getPort()); request.setAuthority(forwarded._authority._host, forwarded._authority._port);
}
else if (forwarded._host != null)
{
httpFields.put(new HostPortHttpField(forwarded._host));
request.setAuthority(forwarded._host.getHost(), forwarded._host.getPort());
}
else if (forwarded._server != null)
{
httpFields.put(new HostPortHttpField(forwarded._server));
request.setAuthority(forwarded._server, 0);
} }
if (forwarded._for != null) if (forwarded.hasFor())
{ {
int port = forwarded._for.getPort() > 0 ? forwarded._for.getPort() : request.getRemotePort(); int port = forwarded._for._port > 0 ? forwarded._for._port : request.getRemotePort();
request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._for.getHost(), port)); request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._for._host, port));
} }
} }
@ -426,7 +417,7 @@ public class ForwardedRequestCustomizer implements Customizer
throw new BadMessageException("Bad header value for " + field.getName(), t); throw new BadMessageException("Bad header value for " + field.getName(), t);
} }
protected String getLeftMost(String headerValue) protected static String getLeftMost(String headerValue)
{ {
if (headerValue == null) if (headerValue == null)
return null; return null;
@ -479,23 +470,23 @@ public class ForwardedRequestCustomizer implements Customizer
size += 128; // experimented good baseline size size += 128; // experimented good baseline size
_handles = new ArrayTrie<>(size); _handles = new ArrayTrie<>(size);
if (updateForwardedHandle(lookup, getForwardedCipherSuiteHeader(), "handleCipherSuite"))
continue;
if (updateForwardedHandle(lookup, getForwardedSslSessionIdHeader(), "handleSslSessionId"))
continue;
if (updateForwardedHandle(lookup, getForwardedHeader(), "handleRFC7239")) if (updateForwardedHandle(lookup, getForwardedHeader(), "handleRFC7239"))
continue; continue;
if (updateForwardedHandle(lookup, getForwardedForHeader(), "handleFor")) if (updateForwardedHandle(lookup, getForwardedHostHeader(), "handleForwardedHost"))
continue; continue;
if (updateForwardedHandle(lookup, getForwardedPortHeader(), "handlePort")) if (updateForwardedHandle(lookup, getForwardedForHeader(), "handleForwardedFor"))
continue; continue;
if (updateForwardedHandle(lookup, getForwardedHostHeader(), "handleHost")) if (updateForwardedHandle(lookup, getForwardedPortHeader(), "handleForwardedPort"))
continue; continue;
if (updateForwardedHandle(lookup, getForwardedProtoHeader(), "handleProto")) if (updateForwardedHandle(lookup, getForwardedProtoHeader(), "handleProto"))
continue; continue;
if (updateForwardedHandle(lookup, getForwardedHttpsHeader(), "handleHttps")) if (updateForwardedHandle(lookup, getForwardedHttpsHeader(), "handleHttps"))
continue; continue;
if (updateForwardedHandle(lookup, getForwardedServerHeader(), "handleServer")) if (updateForwardedHandle(lookup, getForwardedServerHeader(), "handleForwardedServer"))
continue;
if (updateForwardedHandle(lookup, getForwardedCipherSuiteHeader(), "handleCipherSuite"))
continue;
if (updateForwardedHandle(lookup, getForwardedSslSessionIdHeader(), "handleSslSessionId"))
continue; continue;
break; break;
} }
@ -516,53 +507,84 @@ public class ForwardedRequestCustomizer implements Customizer
return !_handles.put(headerName, lookup.findVirtual(Forwarded.class, forwardedMethodName, type)); return !_handles.put(headerName, lookup.findVirtual(Forwarded.class, forwardedMethodName, type));
} }
private static class ForcedHostPort extends HostPort private static class MutableHostPort
{ {
ForcedHostPort(String authority) String _host;
int _hostPriority = -1;
int _port = -1;
int _portPriority = -1;
public void setHost(String host, int priority)
{ {
super(authority); if (priority > _hostPriority)
{
_host = host;
_hostPriority = priority;
}
}
public void setPort(int port, int priority)
{
if (port > 0 && priority > _portPriority)
{
_port = port;
_portPriority = priority;
}
}
public void setHostPort(HostPort hostPort, int priority)
{
if (_host == null || priority > _hostPriority)
{
_host = hostPort.getHost();
_hostPriority = priority;
// Is this an authoritative port?
if (priority == FORWARDED_PRIORITY)
{
// Trust port (even if 0/unset)
_port = hostPort.getPort();
_portPriority = priority;
}
else
{
setPort(hostPort.getPort(), priority);
}
}
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder("MutableHostPort{");
sb.append("host='").append(_host).append("'/").append(_hostPriority);
sb.append(", port=").append(_port);
sb.append("/").append(_portPriority);
sb.append('}');
return sb.toString();
} }
} }
private static class PossiblyPartialHostPort extends HostPort private static final int MAX_PRIORITY = 999;
{ private static final int FORWARDED_PRIORITY = 8;
PossiblyPartialHostPort(String authority) private static final int XFORWARDED_HOST_PRIORITY = 7;
{ private static final int XFORWARDED_FOR_PRIORITY = 6;
super(authority); private static final int XFORWARDED_PORT_PRIORITY = 5;
} private static final int XFORWARDED_SERVER_PRIORITY = 4;
// HostPort seen in Request metadata
protected PossiblyPartialHostPort(String host, int port) private static final int REQUEST_PRIORITY = 3;
{ private static final int XFORWARDED_PROTO_PRIORITY = 2;
super(host, port); private static final int XPROXIED_HTTPS_PRIORITY = 1;
}
}
private static class PortSetHostPort extends PossiblyPartialHostPort
{
PortSetHostPort(String host, int port)
{
super(host, port);
}
}
private static class Rfc7239HostPort extends HostPort
{
Rfc7239HostPort(String authority)
{
super(authority);
}
}
private class Forwarded extends QuotedCSVParser private class Forwarded extends QuotedCSVParser
{ {
HttpConfiguration _config; HttpConfiguration _config;
Request _request; Request _request;
boolean _protoRfc7239; MutableHostPort _authority;
MutableHostPort _for;
String _proto; String _proto;
HostPort _for; int _protoPriority = -1;
HostPort _host;
String _server;
public Forwarded(Request request, HttpConfiguration config) public Forwarded(Request request, HttpConfiguration config)
{ {
@ -570,7 +592,45 @@ public class ForwardedRequestCustomizer implements Customizer
_request = request; _request = request;
_config = config; _config = config;
if (_forcedHost != null) if (_forcedHost != null)
_host = _forcedHost.getHostPort(); {
getAuthority().setHostPort(_forcedHost.getHostPort(), MAX_PRIORITY);
}
else
{
HttpURI requestURI = request.getMetaData().getURI();
if (requestURI.getHost() != null)
{
getAuthority().setHostPort(new HostPort(requestURI.getHost(), requestURI.getPort()), REQUEST_PRIORITY);
}
}
}
public boolean hasAuthority()
{
return _authority != null && _authority._host != null;
}
public boolean hasFor()
{
return _for != null && _for._host != null;
}
private MutableHostPort getAuthority()
{
if (_authority == null)
{
_authority = new MutableHostPort();
}
return _authority;
}
private MutableHostPort getFor()
{
if (_for == null)
{
_for = new MutableHostPort();
}
return _for;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -596,80 +656,47 @@ public class ForwardedRequestCustomizer implements Customizer
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void handleHost(HttpField field) public void handleForwardedHost(HttpField field)
{ {
HostPort hostField = new HostPort(getLeftMost(field.getValue())); updateAuthority(getLeftMost(field.getValue()), XFORWARDED_HOST_PRIORITY);
if (getForwardedPortAsAuthority() && !StringUtil.isEmpty(getForwardedPortHeader()))
{
if (_host == null)
_host = new PossiblyPartialHostPort(hostField.getHost(), hostField.getPort());
else if (_host instanceof PortSetHostPort)
_host = new HostPort(hostField.getHost(), hostField.getPort() > 0 ? hostField.getPort() : _host.getPort());
}
else if (_host == null)
{
_host = hostField;
}
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void handleServer(HttpField field) public void handleForwardedFor(HttpField field)
{
getFor().setHostPort(new HostPort(getLeftMost(field.getValue())), XFORWARDED_FOR_PRIORITY);
}
@SuppressWarnings("unused")
public void handleForwardedServer(HttpField field)
{ {
if (getProxyAsAuthority()) if (getProxyAsAuthority())
return; return;
_server = getLeftMost(field.getValue()); updateAuthority(getLeftMost(field.getValue()), XFORWARDED_SERVER_PRIORITY);
}
@SuppressWarnings("unused")
public void handleForwardedPort(HttpField field)
{
int port = HostPort.parsePort(getLeftMost(field.getValue()));
updatePort(port, XFORWARDED_PORT_PRIORITY);
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void handleProto(HttpField field) public void handleProto(HttpField field)
{ {
if (_proto == null) updateProto(getLeftMost(field.getValue()), XFORWARDED_PROTO_PRIORITY);
_proto = getLeftMost(field.getValue());
}
@SuppressWarnings("unused")
public void handleFor(HttpField field)
{
String authority = getLeftMost(field.getValue());
if (!getForwardedPortAsAuthority() && !StringUtil.isEmpty(getForwardedPortHeader()))
{
if (_for == null)
_for = new PossiblyPartialHostPort(authority);
else if (_for instanceof PortSetHostPort)
_for = new HostPort(HostPort.normalizeHost(authority), _for.getPort());
}
else if (_for == null)
{
_for = new HostPort(authority);
}
}
@SuppressWarnings("unused")
public void handlePort(HttpField field)
{
int port = HostPort.parsePort(getLeftMost(field.getValue()));
if (!getForwardedPortAsAuthority())
{
if (_for == null)
_for = new PortSetHostPort(_request.getRemoteHost(), port);
else if (_for instanceof PossiblyPartialHostPort && _for.getPort() <= 0)
_for = new HostPort(HostPort.normalizeHost(_for.getHost()), port);
}
else
{
if (_host == null)
_host = new PortSetHostPort(_request.getServerName(), port);
else if (_host instanceof PossiblyPartialHostPort && _host.getPort() <= 0)
_host = new HostPort(HostPort.normalizeHost(_host.getHost()), port);
}
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void handleHttps(HttpField field) public void handleHttps(HttpField field)
{ {
if (_proto == null && ("on".equalsIgnoreCase(field.getValue()) || "true".equalsIgnoreCase(field.getValue()))) if ("on".equalsIgnoreCase(field.getValue()) || "true".equalsIgnoreCase(field.getValue()))
_proto = HttpScheme.HTTPS.asString(); {
updateProto(HttpScheme.HTTPS.asString(), XPROXIED_HTTPS_PRIORITY);
updatePort(443, XPROXIED_HTTPS_PRIORITY);
}
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -692,30 +719,60 @@ public class ForwardedRequestCustomizer implements Customizer
break; break;
if (value.startsWith("_") || "unknown".equals(value)) if (value.startsWith("_") || "unknown".equals(value))
break; break;
if (_host == null || !(_host instanceof Rfc7239HostPort)) getAuthority().setHostPort(new HostPort(value), FORWARDED_PRIORITY);
_host = new Rfc7239HostPort(value);
break; break;
case "for": case "for":
if (value.startsWith("_") || "unknown".equals(value)) if (value.startsWith("_") || "unknown".equals(value))
break; break;
if (_for == null || !(_for instanceof Rfc7239HostPort)) getFor().setHostPort(new HostPort(value), FORWARDED_PRIORITY);
_for = new Rfc7239HostPort(value);
break; break;
case "host": case "host":
if (value.startsWith("_") || "unknown".equals(value)) if (value.startsWith("_") || "unknown".equals(value))
break; break;
if (_host == null || !(_host instanceof Rfc7239HostPort)) getAuthority().setHostPort(new HostPort(value), FORWARDED_PRIORITY);
_host = new Rfc7239HostPort(value);
break; break;
case "proto": case "proto":
if (_proto == null || !_protoRfc7239) updateProto(value, FORWARDED_PRIORITY);
{ getAuthority().setPort(getPortForProto(value), FORWARDED_PRIORITY);
_protoRfc7239 = true;
_proto = value;
}
break; break;
} }
} }
} }
private int getPortForProto(String proto)
{
if ("http".equalsIgnoreCase(proto))
return 80;
if ("https".equalsIgnoreCase(proto))
return 443;
return -1;
}
private void updateAuthority(String value, int priority)
{
HostPort hostField = new HostPort(value);
getAuthority().setHostPort(hostField, priority);
}
private void updatePort(int port, int priority)
{
if (getForwardedPortAsAuthority())
{
getAuthority().setPort(port, priority);
}
else
{
getFor().setPort(port, priority);
}
}
private void updateProto(String proto, int priority)
{
if (priority > _protoPriority)
{
_proto = proto;
_protoPriority = priority;
}
}
} }
} }