All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.netty.common.proxyprotocol.HAProxyMessageChannelHandler Maven / Gradle / Ivy

There is a newer version: 2.5.13
Show newest version
/*
 * Copyright 2018 Netflix, Inc.
 *
 *      Licensed 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 com.netflix.netty.common.proxyprotocol;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.InetAddresses;
import com.netflix.netty.common.SourceAddressChannelHandler;
import com.netflix.zuul.Attrs;
import com.netflix.zuul.netty.server.Server;
import io.netty.channel.Channel;
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.HAProxyProtocolVersion;
import io.netty.handler.codec.haproxy.HAProxyTLV;
import io.netty.handler.codec.haproxy.HAProxyTLV.Type;
import io.netty.util.AttributeKey;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Copies any decoded HAProxyMessage into the channel attributes, and doesn't pass it any further along the pipeline.
 * Use in conjunction with HAProxyMessageDecoder if proxy protocol is enabled on the ELB.
 */
public final class HAProxyMessageChannelHandler extends ChannelInboundHandlerAdapter {

    public static final AttributeKey ATTR_HAPROXY_MESSAGE =
            AttributeKey.newInstance("_haproxy_message");
    public static final AttributeKey ATTR_HAPROXY_VERSION =
            AttributeKey.newInstance("_haproxy_version");
    public static final AttributeKey> ATTR_HAPROXY_CUSTOM_TLVS = AttributeKey.newInstance(
            "_haproxy_tlvs");

    @VisibleForTesting
    static final Attrs.Key HAPM_DEST_PORT = Attrs.newKey("hapm_port");

    @VisibleForTesting
    static final Attrs.Key HAPM_DEST_IP_VERSION = Attrs.newKey("hapm_dst_ipproto");

    @VisibleForTesting
    static final Attrs.Key HAPM_SRC_IP_VERSION = Attrs.newKey("hapm_src_ipproto");

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof HAProxyMessage) {
            HAProxyMessage hapm = (HAProxyMessage) msg;
            Channel channel = ctx.channel();
            channel.attr(ATTR_HAPROXY_MESSAGE).set(hapm);
            ctx.channel().closeFuture().addListener((ChannelFutureListener) future -> hapm.release());
            channel.attr(ATTR_HAPROXY_VERSION).set(hapm.protocolVersion());
            // Parse and persist any custom TLVs that might be part of the connection
            final List tlvList = hapm.tlvs().stream().filter(tlv -> tlv.type() == Type.OTHER)
                    .collect(Collectors.toList());
            channel.attr(ATTR_HAPROXY_CUSTOM_TLVS).set(tlvList);
            // Get the real host and port that the client connected with.
            parseDstAddr(hapm, channel);
            parseSrcAddr(hapm, channel);
            // Remove ourselves (this handler) from the channel now, as this is conn. level info
            ctx.pipeline().remove(this);
        }
    }

    private void parseSrcAddr(HAProxyMessage hapm, Channel channel) {
        String sourceAddress = hapm.sourceAddress();
        if (sourceAddress != null) {
            channel.attr(SourceAddressChannelHandler.ATTR_SOURCE_ADDRESS).set(sourceAddress);

            SocketAddress srcAddr;
            out:
            {
                switch (hapm.proxiedProtocol()) {
                    case UNKNOWN:
                        throw new IllegalArgumentException("unknown proxy protocol" + sourceAddress);
                    case TCP4:
                    case TCP6:
                        InetSocketAddress inetAddr;
                        srcAddr = inetAddr =
                                new InetSocketAddress(InetAddresses.forString(sourceAddress), hapm.sourcePort());
                        Attrs attrs = channel.attr(Server.CONN_DIMENSIONS).get();
                        if (inetAddr.getAddress() instanceof Inet4Address) {
                            HAPM_SRC_IP_VERSION.put(attrs, "v4");
                        } else if (inetAddr.getAddress() instanceof Inet6Address) {
                            HAPM_SRC_IP_VERSION.put(attrs, "v6");
                        } else {
                            HAPM_SRC_IP_VERSION.put(attrs, "unknown");
                        }
                        break out;
                    case UNIX_STREAM: // TODO: implement
                    case UDP4:
                    case UDP6:
                    case UNIX_DGRAM:
                        throw new IllegalArgumentException("unknown proxy protocol" + sourceAddress);
                }
                throw new AssertionError(hapm.proxiedProtocol());
            }
            channel.attr(SourceAddressChannelHandler.ATTR_REMOTE_ADDR).set(srcAddr);
        }
    }

    private void parseDstAddr(HAProxyMessage hapm, Channel channel) {
        String destinationAddress = hapm.destinationAddress();
        if (destinationAddress != null) {
            channel.attr(SourceAddressChannelHandler.ATTR_LOCAL_ADDRESS).set(destinationAddress);
            SocketAddress dstAddr;
            out:
            {
                switch (hapm.proxiedProtocol()) {
                    case UNKNOWN:
                        throw new IllegalArgumentException("unknown proxy protocol" + destinationAddress);
                    case TCP4:
                    case TCP6:
                        InetSocketAddress inetAddr = new InetSocketAddress(
                                InetAddresses.forString(destinationAddress), hapm.destinationPort());
                        dstAddr = inetAddr;
                        // set ppv2 attr explicitly because ATTR_LOCAL_ADDR could be non ppv2
                        channel.attr(SourceAddressChannelHandler.ATTR_PROXY_PROTOCOL_DESTINATION_ADDRESS)
                                .set(inetAddr);
                        Attrs attrs = channel.attr(Server.CONN_DIMENSIONS).get();
                        if (inetAddr.getAddress() instanceof Inet4Address) {
                            HAPM_DEST_IP_VERSION.put(attrs, "v4");
                        } else if (inetAddr.getAddress() instanceof Inet6Address) {
                            HAPM_DEST_IP_VERSION.put(attrs, "v6");
                        } else {
                            HAPM_DEST_IP_VERSION.put(attrs, "unknown");
                        }
                        HAPM_DEST_PORT.put(attrs, hapm.destinationPort());
                        break out;
                    case UNIX_STREAM: // TODO: implement
                    case UDP4:
                    case UDP6:
                    case UNIX_DGRAM:
                        throw new IllegalArgumentException("unknown proxy protocol" + destinationAddress);
                }
                throw new AssertionError(hapm.proxiedProtocol());
            }
            channel.attr(SourceAddressChannelHandler.ATTR_LOCAL_ADDR).set(dstAddr);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy