diff --git a/pom.xml b/pom.xml index d418b210..5b8559b5 100644 --- a/pom.xml +++ b/pom.xml @@ -131,7 +131,12 @@ io.netty netty-handler - 4.1.4.Final + 4.1.5.Final + + + io.netty + netty-codec-haproxy + 4.1.5.Final org.slf4j diff --git a/xrootd4j/pom.xml b/xrootd4j/pom.xml index 9b3d1bd9..734602db 100644 --- a/xrootd4j/pom.xml +++ b/xrootd4j/pom.xml @@ -52,6 +52,10 @@ io.netty netty-handler + + io.netty + netty-codec-haproxy + org.slf4j slf4j-api diff --git a/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdAuthorizationHandler.java b/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdAuthorizationHandler.java index 9e78c00c..f1814470 100644 --- a/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdAuthorizationHandler.java +++ b/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdAuthorizationHandler.java @@ -315,17 +315,14 @@ private String authorize(ChannelHandlerContext ctx, throws XrootdException { try { - Channel channel = ctx.channel(); - InetSocketAddress localAddress = - (InetSocketAddress) channel.localAddress(); - InetSocketAddress remoteAddress = - (InetSocketAddress) channel.remoteAddress(); + InetSocketAddress destinationAddress = getDestinationAddress(); + InetSocketAddress sourceAddress = getSourceAddress(); AuthorizationHandler handler = _authorizationFactory.createHandler(); return handler.authorize(request.getSubject(), - localAddress, - remoteAddress, + destinationAddress, + sourceAddress, path, OpaqueStringParser.getOpaqueMap(opaque), request.getRequestId(), diff --git a/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdRequestHandler.java b/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdRequestHandler.java index 36269957..806b1897 100644 --- a/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdRequestHandler.java +++ b/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdRequestHandler.java @@ -19,14 +19,20 @@ package org.dcache.xrootd.core; import com.google.common.base.Strings; +import com.google.common.net.InetAddresses; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.haproxy.HAProxyMessage; +import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; import io.netty.util.ReferenceCountUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.InetSocketAddress; +import java.util.Objects; + import org.dcache.xrootd.protocol.messages.AuthenticationRequest; import org.dcache.xrootd.protocol.messages.CloseRequest; import org.dcache.xrootd.protocol.messages.DirListRequest; @@ -72,11 +78,49 @@ public class XrootdRequestHandler extends ChannelInboundHandlerAdapter private static final Logger _log = LoggerFactory.getLogger(XrootdRequestHandler.class); + private boolean _isHealthCheck; + + private InetSocketAddress _destinationAddress; + + private InetSocketAddress _sourceAddress; + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception + { + _destinationAddress = (InetSocketAddress) ctx.channel().localAddress(); + _sourceAddress = (InetSocketAddress) ctx.channel().remoteAddress(); + } + @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof XrootdRequest) { requestReceived(ctx, (XrootdRequest) msg); + } else if (msg instanceof HAProxyMessage) { + HAProxyMessage proxyMessage = (HAProxyMessage) msg; + switch (proxyMessage.command()) { + case LOCAL: + _isHealthCheck = true; + break; + case PROXY: + String sourceAddress = proxyMessage.sourceAddress(); + String destinationAddress = proxyMessage.destinationAddress(); + InetSocketAddress localAddress = (InetSocketAddress) ctx.channel().localAddress(); + if (proxyMessage.proxiedProtocol() == HAProxyProxiedProtocol.TCP4 || + proxyMessage.proxiedProtocol() == HAProxyProxiedProtocol.TCP6) { + if (Objects.equals(destinationAddress, localAddress.getAddress().getHostAddress())) { + /* Workaround for what looks like a bug in HAProxy - health checks should + * generate a LOCAL command, but it appears they do actually use PROXY. + */ + _isHealthCheck = true; + } else { + _destinationAddress = new InetSocketAddress(InetAddresses.forString(destinationAddress), proxyMessage.destinationPort()); + _sourceAddress = new InetSocketAddress(InetAddresses.forString(sourceAddress), proxyMessage.sourcePort()); + } + } + break; + } + ctx.fireChannelRead(msg); } else { ctx.fireChannelRead(msg); } @@ -362,4 +406,32 @@ protected Object doOnEndSession(ChannelHandlerContext ctx, { return unsupported(ctx, request); } + + /** + * The socket address the client connected to. May be the local address + * of the channel, but could also be an address on a proxy server + * between the client and the server. + */ + protected InetSocketAddress getDestinationAddress() + { + return _destinationAddress; + } + + /** + * The socket address the client connected from. May be the remote address + * of the channel, but in case a proxy is in between the client and the + * server, the source address will be a different from the remote address. + */ + protected InetSocketAddress getSourceAddress() + { + return _sourceAddress; + } + + /** + * True if this looks like a health check connection from a proxy server. + */ + protected boolean isHealthCheck() + { + return _isHealthCheck; + } }