Merge pull request #9802 from sstrigler/EMQX-8469-enable-proxy-protocol-support-in-emqx-dashboard-schema
feat: support HAProxy protocol for dashboard API
This commit is contained in:
commit
ade5316419
|
@ -96,6 +96,16 @@ The configuration is only valid when the inet6 is true."""
|
|||
zh: "IPv6 only"
|
||||
}
|
||||
}
|
||||
proxy_header {
|
||||
desc {
|
||||
en: "Enable support for `HAProxy` header. Be aware once enabled regular HTTP requests can't be handled anymore."
|
||||
zh: "开启对 `HAProxy` 的支持,注意:一旦开启了这个功能,就无法再处理普通的 HTTP 请求了。"
|
||||
}
|
||||
label: {
|
||||
en: "Enable support for HAProxy header"
|
||||
zh: "开启对 `HAProxy` 的支持"
|
||||
}
|
||||
}
|
||||
desc_dashboard {
|
||||
desc {
|
||||
en: "Configuration for EMQX dashboard."
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{application, emqx_dashboard, [
|
||||
{description, "EMQX Web Dashboard"},
|
||||
% strict semver, bump manually!
|
||||
{vsn, "5.0.12"},
|
||||
{vsn, "5.0.13"},
|
||||
{modules, []},
|
||||
{registered, [emqx_dashboard_sup]},
|
||||
{applications, [kernel, stdlib, mnesia, minirest, emqx]},
|
||||
|
|
|
@ -92,8 +92,8 @@ start_listeners(Listeners) ->
|
|||
},
|
||||
Res =
|
||||
lists:foldl(
|
||||
fun({Name, Protocol, Bind, RanchOptions}, Acc) ->
|
||||
Minirest = BaseMinirest#{protocol => Protocol},
|
||||
fun({Name, Protocol, Bind, RanchOptions, ProtoOpts}, Acc) ->
|
||||
Minirest = BaseMinirest#{protocol => Protocol, protocol_options => ProtoOpts},
|
||||
case minirest:start(Name, RanchOptions, Minirest) of
|
||||
{ok, _} ->
|
||||
?ULOG("Listener ~ts on ~ts started.~n", [
|
||||
|
@ -125,7 +125,7 @@ stop_listeners(Listeners) ->
|
|||
?SLOG(warning, #{msg => "stop_listener_failed", name => Name, port => Port})
|
||||
end
|
||||
end
|
||||
|| {Name, _, Port, _} <- listeners(Listeners)
|
||||
|| {Name, _, Port, _, _} <- listeners(Listeners)
|
||||
],
|
||||
ok.
|
||||
|
||||
|
@ -164,7 +164,13 @@ listeners(Listeners) ->
|
|||
maps:get(enable, Conf) andalso
|
||||
begin
|
||||
{Conf1, Bind} = ip_port(Conf),
|
||||
{true, {listener_name(Protocol), Protocol, Bind, ranch_opts(Conf1)}}
|
||||
{true, {
|
||||
listener_name(Protocol),
|
||||
Protocol,
|
||||
Bind,
|
||||
ranch_opts(Conf1),
|
||||
proto_opts(Conf1)
|
||||
}}
|
||||
end
|
||||
end,
|
||||
maps:to_list(Listeners)
|
||||
|
@ -197,7 +203,7 @@ ranch_opts(Options) ->
|
|||
SocketOpts = maps:fold(
|
||||
fun filter_false/3,
|
||||
[],
|
||||
maps:without([enable, inet6, ipv6_v6only | Keys], Options)
|
||||
maps:without([enable, inet6, ipv6_v6only, proxy_header | Keys], Options)
|
||||
),
|
||||
InetOpts =
|
||||
case Options of
|
||||
|
@ -210,6 +216,9 @@ ranch_opts(Options) ->
|
|||
end,
|
||||
RanchOpts#{socket_opts => InetOpts ++ SocketOpts}.
|
||||
|
||||
proto_opts(Options) ->
|
||||
maps:with([proxy_header], Options).
|
||||
|
||||
filter_false(_K, false, S) -> S;
|
||||
filter_false(K, V, S) -> [{K, V} | S].
|
||||
|
||||
|
|
|
@ -160,6 +160,14 @@ common_listener_fields() ->
|
|||
default => false,
|
||||
desc => ?DESC(ipv6_v6only)
|
||||
}
|
||||
)},
|
||||
{"proxy_header",
|
||||
?HOCON(
|
||||
boolean(),
|
||||
#{
|
||||
desc => ?DESC(proxy_header),
|
||||
default => false
|
||||
}
|
||||
)}
|
||||
].
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
-export([
|
||||
set_default_config/0,
|
||||
set_default_config/1,
|
||||
set_default_config/2,
|
||||
request/2,
|
||||
request/3,
|
||||
request/4,
|
||||
|
@ -36,6 +37,9 @@ set_default_config() ->
|
|||
set_default_config(<<"admin">>).
|
||||
|
||||
set_default_config(DefaultUsername) ->
|
||||
set_default_config(DefaultUsername, false).
|
||||
|
||||
set_default_config(DefaultUsername, HAProxyEnabled) ->
|
||||
Config = #{
|
||||
listeners => #{
|
||||
http => #{
|
||||
|
@ -46,7 +50,8 @@ set_default_config(DefaultUsername) ->
|
|||
max_connections => 512,
|
||||
num_acceptors => 4,
|
||||
send_timeout => 5000,
|
||||
backlog => 512
|
||||
backlog => 512,
|
||||
proxy_header => HAProxyEnabled
|
||||
}
|
||||
},
|
||||
default_username => DefaultUsername,
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%
|
||||
%% 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.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_dashboard_haproxy_SUITE).
|
||||
|
||||
-compile(nowarn_export_all).
|
||||
-compile(export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include("emqx_dashboard.hrl").
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
emqx_common_test_helpers:start_apps(
|
||||
[emqx_management, emqx_dashboard],
|
||||
fun set_special_configs/1
|
||||
),
|
||||
Config.
|
||||
|
||||
set_special_configs(emqx_dashboard) ->
|
||||
emqx_dashboard_api_test_helpers:set_default_config(<<"admin">>, true),
|
||||
ok;
|
||||
set_special_configs(_) ->
|
||||
ok.
|
||||
|
||||
end_per_suite(Config) ->
|
||||
application:unload(emqx_management),
|
||||
mnesia:clear_table(?ADMIN),
|
||||
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_management]),
|
||||
mria:stop(),
|
||||
Config.
|
||||
|
||||
t_status(_Config) ->
|
||||
ProxyInfo = #{
|
||||
version => 1,
|
||||
command => proxy,
|
||||
transport_family => ipv4,
|
||||
transport_protocol => stream,
|
||||
src_address => {127, 0, 0, 1},
|
||||
src_port => 444,
|
||||
dest_address => {192, 168, 0, 1},
|
||||
dest_port => 443
|
||||
},
|
||||
{ok, Socket} = gen_tcp:connect(
|
||||
"localhost",
|
||||
18083,
|
||||
[binary, {active, false}, {packet, raw}]
|
||||
),
|
||||
ok = gen_tcp:send(Socket, ranch_proxy_header:header(ProxyInfo)),
|
||||
{ok, Token} = emqx_dashboard_admin:sign_token(<<"admin">>, <<"public">>),
|
||||
ok = gen_tcp:send(
|
||||
Socket,
|
||||
"GET /status HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"Authorization: Bearer " ++ binary_to_list(Token) ++
|
||||
"\r\n"
|
||||
"\r\n"
|
||||
),
|
||||
{_, 200, _, Rest0} = cow_http:parse_status_line(raw_recv_head(Socket)),
|
||||
{Headers, Body0} = cow_http:parse_headers(Rest0),
|
||||
{_, LenBin} = lists:keyfind(<<"content-length">>, 1, Headers),
|
||||
Len = binary_to_integer(LenBin),
|
||||
Body =
|
||||
if
|
||||
byte_size(Body0) =:= Len ->
|
||||
Body0;
|
||||
true ->
|
||||
{ok, Body1} = gen_tcp:recv(Socket, Len - byte_size(Body0), 5000),
|
||||
<<Body0/bits, Body1/bits>>
|
||||
end,
|
||||
?assertMatch({match, _}, re:run(Body, "Node .+ is started\nemqx is running")),
|
||||
ok.
|
||||
|
||||
raw_recv_head(Socket) ->
|
||||
{ok, Data} = gen_tcp:recv(Socket, 0, 10000),
|
||||
raw_recv_head(Socket, Data).
|
||||
|
||||
raw_recv_head(Socket, Buffer) ->
|
||||
case binary:match(Buffer, <<"\r\n\r\n">>) of
|
||||
nomatch ->
|
||||
{ok, Data} = gen_tcp:recv(Socket, 0, 10000),
|
||||
raw_recv_head(Socket, <<Buffer/binary, Data/binary>>);
|
||||
{_, _} ->
|
||||
Buffer
|
||||
end.
|
|
@ -0,0 +1 @@
|
|||
Support HAProxy protocol for dashboard API.
|
|
@ -0,0 +1 @@
|
|||
现在 dashboard 增加了对 `HAProxy` 协议的支持。
|
2
mix.exs
2
mix.exs
|
@ -56,7 +56,7 @@ defmodule EMQXUmbrella.MixProject do
|
|||
{:ekka, github: "emqx/ekka", tag: "0.13.9", override: true},
|
||||
{:gen_rpc, github: "emqx/gen_rpc", tag: "2.8.1", override: true},
|
||||
{:grpc, github: "emqx/grpc-erl", tag: "0.6.7", override: true},
|
||||
{:minirest, github: "emqx/minirest", tag: "1.3.7", override: true},
|
||||
{:minirest, github: "emqx/minirest", tag: "1.3.8", override: true},
|
||||
{:ecpool, github: "emqx/ecpool", tag: "0.5.2", override: true},
|
||||
{:replayq, github: "emqx/replayq", tag: "0.3.6", override: true},
|
||||
{:pbkdf2, github: "emqx/erlang-pbkdf2", tag: "2.0.4", override: true},
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.13.9"}}}
|
||||
, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.8.1"}}}
|
||||
, {grpc, {git, "https://github.com/emqx/grpc-erl", {tag, "0.6.7"}}}
|
||||
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.3.7"}}}
|
||||
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.3.8"}}}
|
||||
, {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.2"}}}
|
||||
, {replayq, {git, "https://github.com/emqx/replayq.git", {tag, "0.3.6"}}}
|
||||
, {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}}
|
||||
|
|
Loading…
Reference in New Issue