fix: support https (#5606)

* fix: support https
This commit is contained in:
DDDHuang 2021-09-01 16:44:34 +08:00 committed by GitHub
parent 473a3c0b35
commit c4e279bb76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 85 additions and 76 deletions

View File

@ -11,35 +11,30 @@ emqx_dashboard {
token_expired_time = 60m
listeners = [
{
protocol = http
num_acceptors = 4
max_connections = 512
protocol = http
port = 18083
backlog = 512
send_timeout = 15s
send_timeout_close = true
send_timeout = 5s
inet6 = false
ipv6_v6only = false
}
## ,
## {
## protocol: https
## port: 18084
## acceptors: 2
## backlog: 512
## send_timeout: 15s
## send_timeout_close: true
## inet6: false
## ipv6_v6only: false
## certfile = "etc/certs/cert.pem"
## keyfile = "etc/certs/key.pem"
## cacertfile = "etc/certs/cacert.pem"
## verify = verify_peer
## tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## fail_if_no_peer_cert = true
## inet6 = false
## ipv6_v6only = false
## }
# ,
# {
# protocol = https
# port = 18084
# num_acceptors = 2
# backlog = 512
# send_timeout = 5s
# inet6 = false
# ipv6_v6only = false
# certfile = "etc/certs/cert.pem"
# keyfile = "etc/certs/key.pem"
# cacertfile = "etc/certs/cacert.pem"
# verify = verify_peer
# versions = ["tlsv1.3","tlsv1.2","tlsv1.1","tlsv1"]
# ciphers = ["TLS_AES_256_GCM_SHA384","TLS_AES_128_GCM_SHA256","TLS_CHACHA20_POLY1305_SHA256","TLS_AES_128_CCM_SHA256","TLS_AES_128_CCM_8_SHA256","ECDHE-ECDSA-AES256-GCM-SHA384","ECDHE-RSA-AES256-GCM-SHA384","ECDHE-ECDSA-AES256-SHA384","ECDHE-RSA-AES256-SHA384","ECDHE-ECDSA-DES-CBC3-SHA","ECDH-ECDSA-AES256-GCM-SHA384","ECDH-RSA-AES256-GCM-SHA384","ECDH-ECDSA-AES256-SHA384","ECDH-RSA-AES256-SHA384","DHE-DSS-AES256-GCM-SHA384","DHE-DSS-AES256-SHA256","AES256-GCM-SHA384","AES256-SHA256","ECDHE-ECDSA-AES128-GCM-SHA256","ECDHE-RSA-AES128-GCM-SHA256","ECDHE-ECDSA-AES128-SHA256","ECDHE-RSA-AES128-SHA256","ECDH-ECDSA-AES128-GCM-SHA256","ECDH-RSA-AES128-GCM-SHA256","ECDH-ECDSA-AES128-SHA256","ECDH-RSA-AES128-SHA256","DHE-DSS-AES128-GCM-SHA256","DHE-DSS-AES128-SHA256","AES128-GCM-SHA256","AES128-SHA256","ECDHE-ECDSA-AES256-SHA","ECDHE-RSA-AES256-SHA","DHE-DSS-AES256-SHA","ECDH-ECDSA-AES256-SHA","ECDH-RSA-AES256-SHA","AES256-SHA","ECDHE-ECDSA-AES128-SHA","ECDHE-RSA-AES128-SHA","DHE-DSS-AES128-SHA","ECDH-ECDSA-AES128-SHA","ECDH-RSA-AES128-SHA","AES128-SHA"]
# }
]
}

View File

@ -20,9 +20,7 @@
-export([ start_listeners/0
, stop_listeners/0
, start_listener/1
, stop_listener/1]).
, stop_listeners/0]).
%% Authorization
-export([authorize_appid/1]).
@ -36,15 +34,8 @@
%%--------------------------------------------------------------------
start_listeners() ->
lists:foreach(fun start_listener/1, listeners()).
stop_listeners() ->
lists:foreach(fun stop_listener/1, listeners()).
start_listener({Proto, Port, Options}) ->
{ok, _} = application:ensure_all_started(minirest),
Authorization = {?MODULE, authorize_appid},
RanchOptions = ranch_opts(Port, Options),
GlobalSpec = #{
openapi => "3.0.0",
info => #{title => "EMQ X Dashboard API", version => "5.0.0"},
@ -56,20 +47,33 @@ start_listener({Proto, Port, Options}) ->
type => apiKey,
name => "authorization",
in => header}}}},
Dispatch = [{"/", cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}},
{"/static/[...]", cowboy_static, {priv_dir, emqx_dashboard, "www/static"}},
{'_', cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}}],
Minirest = #{
protocol => Proto,
Dispatch = [
{"/", cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}},
{"/static/[...]", cowboy_static, {priv_dir, emqx_dashboard, "www/static"}},
{'_', cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}}
],
BaseMinirest = #{
base_path => ?BASE_PATH,
modules => minirest_api:find_api_modules(apps()),
authorization => Authorization,
security => [#{application => []}],
swagger_global_spec => GlobalSpec,
dispatch => Dispatch},
MinirestOptions = maps:merge(Minirest, RanchOptions),
{ok, _} = minirest:start(listener_name(Proto), MinirestOptions),
?ULOG("Start ~p listener on ~p successfully.~n", [listener_name(Proto), Port]).
dispatch => Dispatch
},
[begin
Minirest = maps:put(protocol, Protocol, BaseMinirest),
{ok, _} = minirest:start(Name, RanchOptions, Minirest),
?ULOG("Start listener ~s on ~p successfully.~n", [Name, Port])
end || {Name, Protocol, Port, RanchOptions} <- listeners()].
stop_listeners() ->
[begin
ok = minirest:stop(Name),
?ULOG("Stop listener ~s on ~p successfully.~n", [Name, Port])
end || {Name, _, Port, _} <- listeners()].
%%--------------------------------------------------------------------
%% internal
apps() ->
[App || {App, _, _} <- application:loaded_applications(),
@ -78,30 +82,48 @@ apps() ->
_ -> false
end].
ranch_opts(Port, Options0) ->
Options = lists:foldl(
fun
({K, _V}, Acc) when K =:= max_connections orelse K =:= num_acceptors -> Acc;
({inet6, true}, Acc) -> [inet6 | Acc];
({inet6, false}, Acc) -> Acc;
({ipv6_v6only, true}, Acc) -> [{ipv6_v6only, true} | Acc];
({ipv6_v6only, false}, Acc) -> Acc;
({K, V}, Acc)->
[{K, V} | Acc]
end, [], Options0),
maps:from_list([{port, Port} | Options]).
stop_listener({Proto, Port, _}) ->
?ULOG("Stop dashboard listener on ~s successfully.~n", [format(Port)]),
minirest:stop(listener_name(Proto)).
listeners() ->
[{Protocol, Port, maps:to_list(maps:without([protocol, port], Map))}
|| Map = #{protocol := Protocol,port := Port}
<- emqx:get_config([emqx_dashboard, listeners], [])].
[begin
Protocol = maps:get(protocol, ListenerOptions, http),
Port = maps:get(port, ListenerOptions, 18083),
Name = listener_name(Protocol, Port),
RanchOptions = ranch_opts(maps:without([protocol], ListenerOptions)),
{Name, Protocol, Port, RanchOptions}
end || ListenerOptions <- emqx_config:get([emqx_dashboard, listeners], [])].
listener_name(Proto) ->
list_to_atom(atom_to_list(Proto) ++ ":dashboard").
ranch_opts(RanchOptions) ->
Keys = [ {ack_timeout, handshake_timeout}
, connection_type
, max_connections
, num_acceptors
, shutdown
, socket],
{S, R} = lists:foldl(fun key_take/2, {RanchOptions, #{}}, Keys),
R#{socket_opts => maps:fold(fun key_only/3, [], S)}.
key_take({K, K1}, {All, R}) ->
case maps:get(K, All, undefined) of
undefined ->
{All, R};
V ->
{maps:remove(K, All), R#{K1 => V}}
end;
key_take(K, {All, R}) ->
case maps:get(K, All, undefined) of
undefined ->
{All, R};
V ->
{maps:remove(K, All), R#{K => V}}
end.
key_only(K , true , S) -> [K | S];
key_only(_K, false, S) -> S;
key_only(K , V , S) -> [{K, V} | S].
listener_name(Protocol, Port) ->
Name = "dashboard:" ++ atom_to_list(Protocol) ++ ":" ++ integer_to_list(Port),
list_to_atom(Name).
authorize_appid(Req) ->
case cowboy_req:parse_header(<<"authorization">>, Req) of
@ -127,10 +149,3 @@ authorize_appid(Req) ->
#{code => <<"UNAUTHORIZED">>,
message => <<"POST '/login'">>}}
end.
format(Port) when is_integer(Port) ->
io_lib:format("0.0.0.0:~w", [Port]);
format({Addr, Port}) when is_list(Addr) ->
io_lib:format("~s:~w", [Addr, Port]);
format({Addr, Port}) when is_tuple(Addr) ->
io_lib:format("~s:~w", [inet:ntoa(Addr), Port]).

View File

@ -27,7 +27,7 @@
start(_StartType, _StartArgs) ->
{ok, Sup} = emqx_dashboard_sup:start_link(),
ok = ekka_rlog:wait_for_shards([?DASHBOARD_SHARD], infinity),
emqx_dashboard:start_listeners(),
_ = emqx_dashboard:start_listeners(),
emqx_dashboard_cli:load(),
ok = emqx_dashboard_admin:add_default_user(),
{ok, Sup}.

View File

@ -37,14 +37,13 @@ fields("http") ->
, {"num_acceptors", emqx_schema:t(integer(), undefined, 4)}
, {"max_connections", emqx_schema:t(integer(), undefined, 512)}
, {"backlog", emqx_schema:t(integer(), undefined, 1024)}
, {"send_timeout", emqx_schema:t(emqx_schema:duration(), undefined, "15s")}
, {"send_timeout_close", emqx_schema:t(boolean(), undefined, true)}
, {"send_timeout", emqx_schema:t(emqx_schema:duration(), undefined, "5s")}
, {"inet6", emqx_schema:t(boolean(), undefined, false)}
, {"ipv6_v6only", emqx_schema:t(boolean(), undefined, false)}
];
fields("https") ->
emqx_schema:ssl(#{enable => true}) ++ fields("http").
proplists:delete("fail_if_no_peer_cert", emqx_schema:ssl(#{})) ++ fields("http").
default_username(type) -> string();
default_username(default) -> "admin";

View File

@ -51,7 +51,7 @@
, {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.8.2"}}}
, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.10.8"}}}
, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.1"}}}
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.2.0"}}}
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.2.1"}}}
, {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.1"}}}
, {replayq, "0.3.3"}
, {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}}