Add PROXY protocol support
This commit is contained in:
		
							parent
							
								
									baccff9ba8
								
							
						
					
					
						commit
						248280c61e
					
				| 
						 | 
					@ -45,7 +45,13 @@
 | 
				
			||||||
init(Req0, Opts) ->
 | 
					init(Req0, Opts) ->
 | 
				
			||||||
    {PeerAddr, _PeerPort} = maps:get(peer, Req0),
 | 
					    {PeerAddr, _PeerPort} = maps:get(peer, Req0),
 | 
				
			||||||
    {_, KeepaliveSup} = lists:keyfind(keepalive_sup, 1, Opts),
 | 
					    {_, KeepaliveSup} = lists:keyfind(keepalive_sup, 1, Opts),
 | 
				
			||||||
    {_, Sock} = lists:keyfind(socket, 1, Opts),
 | 
					    {_, Sock0} = lists:keyfind(socket, 1, Opts),
 | 
				
			||||||
 | 
					    Sock = case maps:get(proxy_header, Req0, undefined) of
 | 
				
			||||||
 | 
					        undefined ->
 | 
				
			||||||
 | 
					            Sock0;
 | 
				
			||||||
 | 
					        ProxyInfo ->
 | 
				
			||||||
 | 
					            {rabbit_proxy_socket, Sock0, ProxyInfo}
 | 
				
			||||||
 | 
					    end,
 | 
				
			||||||
    Req = case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req0) of
 | 
					    Req = case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req0) of
 | 
				
			||||||
        undefined  -> Req0;
 | 
					        undefined  -> Req0;
 | 
				
			||||||
        Protocols ->
 | 
					        Protocols ->
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,8 @@ init() ->
 | 
				
			||||||
    TcpConf = get_tcp_conf(get_env(tcp_config, []), Port),
 | 
					    TcpConf = get_tcp_conf(get_env(tcp_config, []), Port),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WsFrame = get_env(ws_frame, text),
 | 
					    WsFrame = get_env(ws_frame, text),
 | 
				
			||||||
    CowboyOpts = maps:from_list(get_env(cowboy_opts, [])),
 | 
					    CowboyOpts0 = maps:from_list(get_env(cowboy_opts, [])),
 | 
				
			||||||
 | 
					    CowboyOpts = CowboyOpts0#{proxy_header => get_env(proxy_protocol, false)},
 | 
				
			||||||
    CowboyWsOpts = maps:from_list(get_env(cowboy_ws_opts, [])),
 | 
					    CowboyWsOpts = maps:from_list(get_env(cowboy_ws_opts, [])),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    VhostRoutes = [
 | 
					    VhostRoutes = [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,111 @@
 | 
				
			||||||
 | 
					%%   The contents of this file are subject to the Mozilla Public License
 | 
				
			||||||
 | 
					%%   Version 1.1 (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.mozilla.org/MPL/
 | 
				
			||||||
 | 
					%%
 | 
				
			||||||
 | 
					%%   Software distributed under the License is distributed on an "AS IS"
 | 
				
			||||||
 | 
					%%   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 | 
				
			||||||
 | 
					%%   License for the specific language governing rights and limitations
 | 
				
			||||||
 | 
					%%   under the License.
 | 
				
			||||||
 | 
					%%
 | 
				
			||||||
 | 
					%%   The Original Code is RabbitMQ Management Console.
 | 
				
			||||||
 | 
					%%
 | 
				
			||||||
 | 
					%%   The Initial Developer of the Original Code is GoPivotal, Inc.
 | 
				
			||||||
 | 
					%%   Copyright (c) 2007-2017 Pivotal Software, Inc.  All rights reserved.
 | 
				
			||||||
 | 
					%%
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-module(proxy_protocol_SUITE).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-compile(export_all).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-include_lib("common_test/include/ct.hrl").
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-include_lib("eunit/include/eunit.hrl").
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suite() ->
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      %% If a test hangs, no need to wait for 30 minutes.
 | 
				
			||||||
 | 
					      {timetrap, {minutes, 2}}
 | 
				
			||||||
 | 
					    ].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					all() ->
 | 
				
			||||||
 | 
					    [{group, http_tests},
 | 
				
			||||||
 | 
					     {group, https_tests}].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					groups() ->
 | 
				
			||||||
 | 
					    Tests = [
 | 
				
			||||||
 | 
					        proxy_protocol
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [{https_tests, [], Tests},
 | 
				
			||||||
 | 
					     {http_tests, [], Tests}].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					init_per_suite(Config) ->
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:log_environment(),
 | 
				
			||||||
 | 
					    Config.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					end_per_suite(Config) ->
 | 
				
			||||||
 | 
					    Config.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					init_per_group(Group, Config) ->
 | 
				
			||||||
 | 
					    Protocol = case Group of
 | 
				
			||||||
 | 
					        http_tests -> "ws";
 | 
				
			||||||
 | 
					        https_tests -> "wss"
 | 
				
			||||||
 | 
					    end,
 | 
				
			||||||
 | 
					    Config1 = rabbit_ct_helpers:set_config(Config,
 | 
				
			||||||
 | 
					                                           [{rmq_nodename_suffix, ?MODULE},
 | 
				
			||||||
 | 
					                                            {protocol, Protocol},
 | 
				
			||||||
 | 
					                                            {rabbitmq_ct_tls_verify, verify_none},
 | 
				
			||||||
 | 
					                                            {rabbitmq_ct_tls_fail_if_no_peer_cert, false}]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:run_setup_steps(
 | 
				
			||||||
 | 
					        Config1,
 | 
				
			||||||
 | 
					        rabbit_ct_broker_helpers:setup_steps() ++ [
 | 
				
			||||||
 | 
					            fun configure_proxy_protocol/1,
 | 
				
			||||||
 | 
					            fun configure_ssl/1
 | 
				
			||||||
 | 
					        ]).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					configure_proxy_protocol(Config) ->
 | 
				
			||||||
 | 
					    rabbit_ws_test_util:update_app_env(Config, proxy_protocol, true),
 | 
				
			||||||
 | 
					    Config.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					configure_ssl(Config) ->
 | 
				
			||||||
 | 
					    ErlangConfig = proplists:get_value(erlang_node_config, Config, []),
 | 
				
			||||||
 | 
					    RabbitAppConfig = proplists:get_value(rabbit, ErlangConfig, []),
 | 
				
			||||||
 | 
					    RabbitSslConfig = proplists:get_value(ssl_options, RabbitAppConfig, []),
 | 
				
			||||||
 | 
					    Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_web_stomp_tls),
 | 
				
			||||||
 | 
					    rabbit_ws_test_util:update_app_env(Config, ssl_config, [{port, Port} | lists:keydelete(port, 1, RabbitSslConfig)]),
 | 
				
			||||||
 | 
					    Config.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					end_per_group(_Group, Config) ->
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:run_teardown_steps(Config,
 | 
				
			||||||
 | 
					      rabbit_ct_broker_helpers:teardown_steps()).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					init_per_testcase(Testcase, Config) ->
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:testcase_started(Config, Testcase).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					end_per_testcase(Testcase, Config) ->
 | 
				
			||||||
 | 
					    rabbit_ct_helpers:testcase_finished(Config, Testcase).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					proxy_protocol(Config) ->
 | 
				
			||||||
 | 
					    Port = list_to_integer(rabbit_ws_test_util:get_web_stomp_port_str(Config)),
 | 
				
			||||||
 | 
					    PortStr = integer_to_list(Port),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Protocol = ?config(protocol, Config),
 | 
				
			||||||
 | 
					    WS = rfc6455_client:new(Protocol ++ "://127.0.0.1:" ++ PortStr ++ "/ws", self(),
 | 
				
			||||||
 | 
					        undefined, [], "PROXY TCP4 192.168.1.1 192.168.1.2 80 81\r\n"),
 | 
				
			||||||
 | 
					    {ok, _} = rfc6455_client:open(WS),
 | 
				
			||||||
 | 
					    Frame = stomp:marshal("CONNECT", [{"login","guest"}, {"passcode", "guest"}], <<>>),
 | 
				
			||||||
 | 
					    rfc6455_client:send(WS, Frame),
 | 
				
			||||||
 | 
					    {ok, _P} = rfc6455_client:recv(WS),
 | 
				
			||||||
 | 
					    ConnectionName = rabbit_ct_broker_helpers:rpc(Config, 0,
 | 
				
			||||||
 | 
					        ?MODULE, connection_name, []),
 | 
				
			||||||
 | 
					    match = re:run(ConnectionName, <<"^192.168.1.1:80 ">>, [{capture, none}]),
 | 
				
			||||||
 | 
					    {close, _} = rfc6455_client:close(WS),
 | 
				
			||||||
 | 
					    ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					connection_name() ->
 | 
				
			||||||
 | 
					    Connections = ets:tab2list(connection_created),
 | 
				
			||||||
 | 
					    {_Key, Values} = lists:nth(1, Connections),
 | 
				
			||||||
 | 
					    {_, Name} = lists:keyfind(name, 1, Values),
 | 
				
			||||||
 | 
					    Name.
 | 
				
			||||||
| 
						 | 
					@ -16,19 +16,22 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-module(rfc6455_client).
 | 
					-module(rfc6455_client).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-export([new/2, new/3, new/4, open/1, recv/1, send/2, close/1, close/2]).
 | 
					-export([new/2, new/3, new/4, new/5, open/1, recv/1, send/2, close/1, close/2]).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-record(state, {host, port, addr, path, ppid, socket, data, phase, transport}).
 | 
					-record(state, {host, port, addr, path, ppid, socket, data, phase, transport}).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%% --------------------------------------------------------------------------
 | 
					%% --------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
new(WsUrl, PPid) ->
 | 
					new(WsUrl, PPid) ->
 | 
				
			||||||
    new(WsUrl, PPid, undefined, []).
 | 
					    new(WsUrl, PPid, undefined, [], <<>>).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
new(WsUrl, PPid, AuthInfo) ->
 | 
					new(WsUrl, PPid, AuthInfo) ->
 | 
				
			||||||
    new(WsUrl, PPid, AuthInfo, []).
 | 
					    new(WsUrl, PPid, AuthInfo, [], <<>>).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
new(WsUrl, PPid, AuthInfo, Protocols) ->
 | 
					new(WsUrl, PPid, AuthInfo, Protocols) ->
 | 
				
			||||||
 | 
					    new(WsUrl, PPid, AuthInfo, Protocols, <<>>).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					new(WsUrl, PPid, AuthInfo, Protocols, TcpPreface) ->
 | 
				
			||||||
    crypto:start(),
 | 
					    crypto:start(),
 | 
				
			||||||
    application:ensure_all_started(ssl),
 | 
					    application:ensure_all_started(ssl),
 | 
				
			||||||
    {Transport, Url} = case WsUrl of
 | 
					    {Transport, Url} = case WsUrl of
 | 
				
			||||||
| 
						 | 
					@ -52,7 +55,7 @@ new(WsUrl, PPid, AuthInfo, Protocols) ->
 | 
				
			||||||
                   ppid = PPid,
 | 
					                   ppid = PPid,
 | 
				
			||||||
                   transport = Transport},
 | 
					                   transport = Transport},
 | 
				
			||||||
    spawn_link(fun () ->
 | 
					    spawn_link(fun () ->
 | 
				
			||||||
                  start_conn(State, AuthInfo, Protocols)
 | 
					                  start_conn(State, AuthInfo, Protocols, TcpPreface)
 | 
				
			||||||
          end).
 | 
					          end).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
open(WS) ->
 | 
					open(WS) ->
 | 
				
			||||||
| 
						 | 
					@ -90,10 +93,22 @@ close(WS, WsReason) ->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%% --------------------------------------------------------------------------
 | 
					%% --------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
start_conn(State = #state{transport = Transport}, AuthInfo, Protocols) ->
 | 
					start_conn(State = #state{transport = Transport}, AuthInfo, Protocols, TcpPreface) ->
 | 
				
			||||||
    {ok, Socket} = Transport:connect(State#state.host, State#state.port,
 | 
					    {ok, Socket} = case TcpPreface of
 | 
				
			||||||
 | 
					        <<>> ->
 | 
				
			||||||
 | 
					            Transport:connect(State#state.host, State#state.port,
 | 
				
			||||||
 | 
					                              [binary,
 | 
				
			||||||
 | 
					                               {packet, 0}]);
 | 
				
			||||||
 | 
					        _ ->
 | 
				
			||||||
 | 
					            {ok, Socket0} = gen_tcp:connect(State#state.host, State#state.port,
 | 
				
			||||||
                                            [binary,
 | 
					                                            [binary,
 | 
				
			||||||
                                             {packet, 0}]),
 | 
					                                             {packet, 0}]),
 | 
				
			||||||
 | 
					            gen_tcp:send(Socket0, TcpPreface),
 | 
				
			||||||
 | 
					            case Transport of
 | 
				
			||||||
 | 
					                gen_tcp -> {ok, Socket0};
 | 
				
			||||||
 | 
					                ssl -> Transport:connect(Socket0, [])
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					    end,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    AuthHd = case AuthInfo of
 | 
					    AuthHd = case AuthInfo of
 | 
				
			||||||
        undefined -> "";
 | 
					        undefined -> "";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue