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.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.QuotedCSVParser;
import org.eclipse.jetty.server.HttpConfiguration.Customizer;
import org.eclipse.jetty.util.ArrayTrie;
@ -151,7 +152,7 @@ public class ForwardedRequestCustomizer implements Customizer
*/
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);
}
if (forwarded._server != null && forwarded._host instanceof PortSetHostPort)
if (forwarded.hasAuthority())
{
httpFields.put(new HostPortHttpField(forwarded._server, forwarded._host.getPort()));
request.setAuthority(forwarded._server, forwarded._host.getPort());
}
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);
httpFields.put(new HostPortHttpField(forwarded._authority._host, forwarded._authority._port));
request.setAuthority(forwarded._authority._host, forwarded._authority._port);
}
if (forwarded._for != null)
if (forwarded.hasFor())
{
int port = forwarded._for.getPort() > 0 ? forwarded._for.getPort() : request.getRemotePort();
request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._for.getHost(), port));
int port = forwarded._for._port > 0 ? forwarded._for._port : request.getRemotePort();
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);
}
protected String getLeftMost(String headerValue)
protected static String getLeftMost(String headerValue)
{
if (headerValue == null)
return null;
@ -479,23 +470,23 @@ public class ForwardedRequestCustomizer implements Customizer
size += 128; // experimented good baseline size
_handles = new ArrayTrie<>(size);
if (updateForwardedHandle(lookup, getForwardedCipherSuiteHeader(), "handleCipherSuite"))
continue;
if (updateForwardedHandle(lookup, getForwardedSslSessionIdHeader(), "handleSslSessionId"))
continue;
if (updateForwardedHandle(lookup, getForwardedHeader(), "handleRFC7239"))
continue;
if (updateForwardedHandle(lookup, getForwardedForHeader(), "handleFor"))
if (updateForwardedHandle(lookup, getForwardedHostHeader(), "handleForwardedHost"))
continue;
if (updateForwardedHandle(lookup, getForwardedPortHeader(), "handlePort"))
if (updateForwardedHandle(lookup, getForwardedForHeader(), "handleForwardedFor"))
continue;
if (updateForwardedHandle(lookup, getForwardedHostHeader(), "handleHost"))
if (updateForwardedHandle(lookup, getForwardedPortHeader(), "handleForwardedPort"))
continue;
if (updateForwardedHandle(lookup, getForwardedProtoHeader(), "handleProto"))
continue;
if (updateForwardedHandle(lookup, getForwardedHttpsHeader(), "handleHttps"))
continue;
if (updateForwardedHandle(lookup, getForwardedServerHeader(), "handleServer"))
if (updateForwardedHandle(lookup, getForwardedServerHeader(), "handleForwardedServer"))
continue;
if (updateForwardedHandle(lookup, getForwardedCipherSuiteHeader(), "handleCipherSuite"))
continue;
if (updateForwardedHandle(lookup, getForwardedSslSessionIdHeader(), "handleSslSessionId"))
continue;
break;
}
@ -516,53 +507,84 @@ public class ForwardedRequestCustomizer implements Customizer
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
{
PossiblyPartialHostPort(String authority)
{
super(authority);
}
protected PossiblyPartialHostPort(String host, int port)
{
super(host, port);
}
}
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 static final int MAX_PRIORITY = 999;
private static final int FORWARDED_PRIORITY = 8;
private static final int XFORWARDED_HOST_PRIORITY = 7;
private static final int XFORWARDED_FOR_PRIORITY = 6;
private static final int XFORWARDED_PORT_PRIORITY = 5;
private static final int XFORWARDED_SERVER_PRIORITY = 4;
// HostPort seen in Request metadata
private static final int REQUEST_PRIORITY = 3;
private static final int XFORWARDED_PROTO_PRIORITY = 2;
private static final int XPROXIED_HTTPS_PRIORITY = 1;
private class Forwarded extends QuotedCSVParser
{
HttpConfiguration _config;
Request _request;
boolean _protoRfc7239;
MutableHostPort _authority;
MutableHostPort _for;
String _proto;
HostPort _for;
HostPort _host;
String _server;
int _protoPriority = -1;
public Forwarded(Request request, HttpConfiguration config)
{
@ -570,7 +592,45 @@ public class ForwardedRequestCustomizer implements Customizer
_request = request;
_config = config;
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")
@ -596,80 +656,47 @@ public class ForwardedRequestCustomizer implements Customizer
}
@SuppressWarnings("unused")
public void handleHost(HttpField field)
public void handleForwardedHost(HttpField field)
{
HostPort hostField = new HostPort(getLeftMost(field.getValue()));
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;
}
updateAuthority(getLeftMost(field.getValue()), XFORWARDED_HOST_PRIORITY);
}
@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())
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")
public void handleProto(HttpField field)
{
if (_proto == null)
_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);
}
updateProto(getLeftMost(field.getValue()), XFORWARDED_PROTO_PRIORITY);
}
@SuppressWarnings("unused")
public void handleHttps(HttpField field)
{
if (_proto == null && ("on".equalsIgnoreCase(field.getValue()) || "true".equalsIgnoreCase(field.getValue())))
_proto = HttpScheme.HTTPS.asString();
if ("on".equalsIgnoreCase(field.getValue()) || "true".equalsIgnoreCase(field.getValue()))
{
updateProto(HttpScheme.HTTPS.asString(), XPROXIED_HTTPS_PRIORITY);
updatePort(443, XPROXIED_HTTPS_PRIORITY);
}
}
@SuppressWarnings("unused")
@ -692,30 +719,60 @@ public class ForwardedRequestCustomizer implements Customizer
break;
if (value.startsWith("_") || "unknown".equals(value))
break;
if (_host == null || !(_host instanceof Rfc7239HostPort))
_host = new Rfc7239HostPort(value);
getAuthority().setHostPort(new HostPort(value), FORWARDED_PRIORITY);
break;
case "for":
if (value.startsWith("_") || "unknown".equals(value))
break;
if (_for == null || !(_for instanceof Rfc7239HostPort))
_for = new Rfc7239HostPort(value);
getFor().setHostPort(new HostPort(value), FORWARDED_PRIORITY);
break;
case "host":
if (value.startsWith("_") || "unknown".equals(value))
break;
if (_host == null || !(_host instanceof Rfc7239HostPort))
_host = new Rfc7239HostPort(value);
getAuthority().setHostPort(new HostPort(value), FORWARDED_PRIORITY);
break;
case "proto":
if (_proto == null || !_protoRfc7239)
{
_protoRfc7239 = true;
_proto = value;
}
updateProto(value, FORWARDED_PRIORITY);
getAuthority().setPort(getPortForProto(value), FORWARDED_PRIORITY);
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;
}
}
}
}