Merge pull request #11137 from zhongwencool/dashboard-https-ssl-options
feat: refactor dashboard https ssl_options
This commit is contained in:
commit
8b679cf358
|
@ -2302,6 +2302,8 @@ ciphers_schema(Default) ->
|
||||||
#{
|
#{
|
||||||
default => default_ciphers(Default),
|
default => default_ciphers(Default),
|
||||||
converter => fun
|
converter => fun
|
||||||
|
(undefined) ->
|
||||||
|
[];
|
||||||
(<<>>) ->
|
(<<>>) ->
|
||||||
[];
|
[];
|
||||||
("") ->
|
("") ->
|
||||||
|
@ -2649,6 +2651,8 @@ parse_ka_int(Bin, Name, Min, Max) ->
|
||||||
throw(#{reason => lists:flatten(Msg), value => I})
|
throw(#{reason => lists:flatten(Msg), value => I})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
user_lookup_fun_tr(undefined, Opts) ->
|
||||||
|
user_lookup_fun_tr(<<"emqx_tls_psk:lookup">>, Opts);
|
||||||
user_lookup_fun_tr(Lookup, #{make_serializable := true}) ->
|
user_lookup_fun_tr(Lookup, #{make_serializable := true}) ->
|
||||||
fmt_user_lookup_fun(Lookup);
|
fmt_user_lookup_fun(Lookup);
|
||||||
user_lookup_fun_tr(Lookup, _) ->
|
user_lookup_fun_tr(Lookup, _) ->
|
||||||
|
|
|
@ -248,9 +248,10 @@ api_key_authorize(Req, Key, Secret) ->
|
||||||
)
|
)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
ensure_ssl_cert(Listeners = #{https := Https0}) ->
|
ensure_ssl_cert(Listeners = #{https := Https0 = #{ssl_options := SslOpts}}) ->
|
||||||
Https1 = emqx_tls_lib:to_server_opts(tls, Https0),
|
SslOpt1 = maps:from_list(emqx_tls_lib:to_server_opts(tls, SslOpts)),
|
||||||
Listeners#{https => maps:from_list(Https1)};
|
Https1 = maps:remove(ssl_options, Https0),
|
||||||
|
Listeners#{https => maps:merge(Https1, SslOpt1)};
|
||||||
ensure_ssl_cert(Listeners) ->
|
ensure_ssl_cert(Listeners) ->
|
||||||
Listeners.
|
Listeners.
|
||||||
|
|
||||||
|
|
|
@ -174,17 +174,19 @@ diff_listeners(Type, Stop, Start) -> {#{Type => Stop}, #{Type => Start}}.
|
||||||
|
|
||||||
-define(DIR, <<"dashboard">>).
|
-define(DIR, <<"dashboard">>).
|
||||||
|
|
||||||
ensure_ssl_cert(#{<<"listeners">> := #{<<"https">> := #{<<"bind">> := Bind}}} = Conf) when
|
ensure_ssl_cert(#{<<"listeners">> := #{<<"https">> := #{<<"bind">> := Bind} = Https0}} = Conf0) when
|
||||||
Bind =/= 0
|
Bind =/= 0
|
||||||
->
|
->
|
||||||
Https = emqx_utils_maps:deep_get([<<"listeners">>, <<"https">>], Conf, undefined),
|
Https1 = emqx_dashboard_schema:https_converter(Https0, #{}),
|
||||||
|
Conf1 = emqx_utils_maps:deep_put([<<"listeners">>, <<"https">>], Conf0, Https1),
|
||||||
|
Ssl = maps:get(<<"ssl_options">>, Https1, undefined),
|
||||||
Opts = #{required_keys => [[<<"keyfile">>], [<<"certfile">>], [<<"cacertfile">>]]},
|
Opts = #{required_keys => [[<<"keyfile">>], [<<"certfile">>], [<<"cacertfile">>]]},
|
||||||
case emqx_tls_lib:ensure_ssl_files(?DIR, Https, Opts) of
|
case emqx_tls_lib:ensure_ssl_files(?DIR, Ssl, Opts) of
|
||||||
{ok, undefined} ->
|
{ok, undefined} ->
|
||||||
{error, <<"ssl_cert_not_found">>};
|
{error, <<"ssl_cert_not_found">>};
|
||||||
{ok, NewHttps} ->
|
{ok, NewSsl} ->
|
||||||
{ok,
|
Keys = [<<"listeners">>, <<"https">>, <<"ssl_options">>],
|
||||||
emqx_utils_maps:deep_merge(Conf, #{<<"listeners">> => #{<<"https">> => NewHttps}})};
|
{ok, emqx_utils_maps:deep_put(Keys, Conf1, NewSsl)};
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?SLOG(error, Reason#{msg => "bad_ssl_config"}),
|
?SLOG(error, Reason#{msg => "bad_ssl_config"}),
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
|
|
|
@ -21,7 +21,8 @@
|
||||||
roots/0,
|
roots/0,
|
||||||
fields/1,
|
fields/1,
|
||||||
namespace/0,
|
namespace/0,
|
||||||
desc/1
|
desc/1,
|
||||||
|
https_converter/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
namespace() -> dashboard.
|
namespace() -> dashboard.
|
||||||
|
@ -63,7 +64,8 @@ fields("dashboard") ->
|
||||||
desc => ?DESC(bootstrap_users_file),
|
desc => ?DESC(bootstrap_users_file),
|
||||||
required => false,
|
required => false,
|
||||||
default => <<>>,
|
default => <<>>,
|
||||||
deprecated => {since, "5.1.0"}
|
deprecated => {since, "5.1.0"},
|
||||||
|
importance => ?IMPORTANCE_HIDDEN
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
];
|
];
|
||||||
|
@ -82,7 +84,8 @@ fields("listeners") ->
|
||||||
?R_REF("https"),
|
?R_REF("https"),
|
||||||
#{
|
#{
|
||||||
desc => "SSL listeners",
|
desc => "SSL listeners",
|
||||||
required => {false, recursively}
|
required => {false, recursively},
|
||||||
|
converter => fun ?MODULE:https_converter/2
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
];
|
];
|
||||||
|
@ -95,11 +98,38 @@ fields("http") ->
|
||||||
fields("https") ->
|
fields("https") ->
|
||||||
[
|
[
|
||||||
enable(false),
|
enable(false),
|
||||||
bind(18084)
|
bind(18084),
|
||||||
| common_listener_fields() ++ server_ssl_opts()
|
ssl_options()
|
||||||
].
|
| common_listener_fields() ++
|
||||||
|
hidden_server_ssl_options()
|
||||||
|
];
|
||||||
|
fields("ssl_options") ->
|
||||||
|
server_ssl_options().
|
||||||
|
|
||||||
server_ssl_opts() ->
|
ssl_options() ->
|
||||||
|
{"ssl_options",
|
||||||
|
?HOCON(
|
||||||
|
?R_REF("ssl_options"),
|
||||||
|
#{
|
||||||
|
required => true,
|
||||||
|
desc => ?DESC(ssl_options),
|
||||||
|
importance => ?IMPORTANCE_HIGH
|
||||||
|
}
|
||||||
|
)}.
|
||||||
|
|
||||||
|
hidden_server_ssl_options() ->
|
||||||
|
lists:map(
|
||||||
|
fun({K, V}) ->
|
||||||
|
{K, V#{
|
||||||
|
importance => ?IMPORTANCE_HIDDEN,
|
||||||
|
default => undefined,
|
||||||
|
required => false
|
||||||
|
}}
|
||||||
|
end,
|
||||||
|
server_ssl_options()
|
||||||
|
).
|
||||||
|
|
||||||
|
server_ssl_options() ->
|
||||||
Opts0 = emqx_schema:server_ssl_opts_schema(#{}, true),
|
Opts0 = emqx_schema:server_ssl_opts_schema(#{}, true),
|
||||||
exclude_fields(["fail_if_no_peer_cert"], Opts0).
|
exclude_fields(["fail_if_no_peer_cert"], Opts0).
|
||||||
|
|
||||||
|
@ -213,6 +243,8 @@ desc("http") ->
|
||||||
?DESC(desc_http);
|
?DESC(desc_http);
|
||||||
desc("https") ->
|
desc("https") ->
|
||||||
?DESC(desc_https);
|
?DESC(desc_https);
|
||||||
|
desc("ssl_options") ->
|
||||||
|
?DESC(ssl_options);
|
||||||
desc(_) ->
|
desc(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
@ -241,7 +273,7 @@ cors(desc) -> ?DESC(cors);
|
||||||
cors(_) -> undefined.
|
cors(_) -> undefined.
|
||||||
|
|
||||||
%% TODO: change it to string type
|
%% TODO: change it to string type
|
||||||
%% It will be up to the dashboard package which languagues to support
|
%% It will be up to the dashboard package which languages to support
|
||||||
i18n_lang(type) -> ?ENUM([en, zh]);
|
i18n_lang(type) -> ?ENUM([en, zh]);
|
||||||
i18n_lang(default) -> en;
|
i18n_lang(default) -> en;
|
||||||
i18n_lang('readOnly') -> true;
|
i18n_lang('readOnly') -> true;
|
||||||
|
@ -257,3 +289,13 @@ validate_sample_interval(Second) ->
|
||||||
Msg = "must be between 1 and 60 and be a divisor of 60.",
|
Msg = "must be between 1 and 60 and be a divisor of 60.",
|
||||||
{error, Msg}
|
{error, Msg}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
https_converter(Conf = #{<<"ssl_options">> := _}, _Opts) ->
|
||||||
|
Conf;
|
||||||
|
https_converter(Conf = #{}, _Opts) ->
|
||||||
|
Keys = lists:map(fun({K, _}) -> list_to_binary(K) end, server_ssl_options()),
|
||||||
|
SslOpts = maps:with(Keys, Conf),
|
||||||
|
Conf1 = maps:without(Keys, Conf),
|
||||||
|
Conf1#{<<"ssl_options">> => SslOpts};
|
||||||
|
https_converter(Conf, _Opts) ->
|
||||||
|
Conf.
|
||||||
|
|
|
@ -49,7 +49,7 @@ t_update_conf(_Config) ->
|
||||||
Conf = #{
|
Conf = #{
|
||||||
dashboard => #{
|
dashboard => #{
|
||||||
listeners => #{
|
listeners => #{
|
||||||
https => #{bind => 18084},
|
https => #{bind => 18084, ssl_options => #{depth => 5}},
|
||||||
http => #{bind => 18083}
|
http => #{bind => 18083}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,12 @@ t_update_conf(_Config) ->
|
||||||
get, http_api_path(["clients"]), Headers
|
get, http_api_path(["clients"]), Headers
|
||||||
),
|
),
|
||||||
Raw = emqx:get_raw_config([<<"dashboard">>]),
|
Raw = emqx:get_raw_config([<<"dashboard">>]),
|
||||||
|
?assertEqual(
|
||||||
|
5,
|
||||||
|
emqx_utils_maps:deep_get(
|
||||||
|
[<<"listeners">>, <<"https">>, <<"ssl_options">>, <<"depth">>], Raw
|
||||||
|
)
|
||||||
|
),
|
||||||
?assertEqual(Client1, Client2),
|
?assertEqual(Client1, Client2),
|
||||||
?check_trace(
|
?check_trace(
|
||||||
begin
|
begin
|
||||||
|
@ -120,7 +126,7 @@ t_default_ssl_cert(_Config) ->
|
||||||
validate_https(Conf, 512, default_ssl_cert(), verify_none),
|
validate_https(Conf, 512, default_ssl_cert(), verify_none),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_normal_ssl_cert(_Config) ->
|
t_compatibility_ssl_cert(_Config) ->
|
||||||
MaxConnection = 1000,
|
MaxConnection = 1000,
|
||||||
Conf = #{
|
Conf = #{
|
||||||
dashboard => #{
|
dashboard => #{
|
||||||
|
@ -138,6 +144,29 @@ t_normal_ssl_cert(_Config) ->
|
||||||
validate_https(Conf, MaxConnection, default_ssl_cert(), verify_none),
|
validate_https(Conf, MaxConnection, default_ssl_cert(), verify_none),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
t_normal_ssl_cert(_Config) ->
|
||||||
|
MaxConnection = 1024,
|
||||||
|
Conf = #{
|
||||||
|
dashboard => #{
|
||||||
|
listeners => #{
|
||||||
|
https => #{
|
||||||
|
bind => 18084,
|
||||||
|
ssl_options => #{
|
||||||
|
cacertfile => naive_env_interpolation(
|
||||||
|
<<"${EMQX_ETC_DIR}/certs/cacert.pem">>
|
||||||
|
),
|
||||||
|
certfile => naive_env_interpolation(<<"${EMQX_ETC_DIR}/certs/cert.pem">>),
|
||||||
|
keyfile => naive_env_interpolation(<<"${EMQX_ETC_DIR}/certs/key.pem">>),
|
||||||
|
depth => 5
|
||||||
|
},
|
||||||
|
max_connections => MaxConnection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validate_https(Conf, MaxConnection, default_ssl_cert(), verify_none),
|
||||||
|
ok.
|
||||||
|
|
||||||
t_verify_cacertfile(_Config) ->
|
t_verify_cacertfile(_Config) ->
|
||||||
MaxConnection = 1024,
|
MaxConnection = 1024,
|
||||||
DefaultSSLCert = default_ssl_cert(),
|
DefaultSSLCert = default_ssl_cert(),
|
||||||
|
|
|
@ -222,11 +222,13 @@ t_dashboard(_Config) ->
|
||||||
),
|
),
|
||||||
|
|
||||||
Https2 = #{
|
Https2 = #{
|
||||||
enable => true,
|
<<"bind">> => 18084,
|
||||||
bind => 18084,
|
<<"ssl_options">> =>
|
||||||
keyfile => "etc/certs/badkey.pem",
|
#{
|
||||||
cacertfile => "etc/certs/badcacert.pem",
|
<<"keyfile">> => "etc/certs/badkey.pem",
|
||||||
certfile => "etc/certs/badcert.pem"
|
<<"cacertfile">> => "etc/certs/badcacert.pem",
|
||||||
|
<<"certfile">> => "etc/certs/badcert.pem"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Dashboard2 = Dashboard#{<<"listeners">> => Listeners#{<<"https">> => Https2}},
|
Dashboard2 = Dashboard#{<<"listeners">> => Listeners#{<<"https">> => Https2}},
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
|
@ -240,20 +242,21 @@ t_dashboard(_Config) ->
|
||||||
emqx, filename:join(["etc", "certs", "cacert.pem"])
|
emqx, filename:join(["etc", "certs", "cacert.pem"])
|
||||||
),
|
),
|
||||||
Https3 = #{
|
Https3 = #{
|
||||||
<<"enable">> => true,
|
|
||||||
<<"bind">> => 18084,
|
<<"bind">> => 18084,
|
||||||
<<"keyfile">> => list_to_binary(KeyFile),
|
<<"ssl_options">> => #{
|
||||||
<<"cacertfile">> => list_to_binary(CacertFile),
|
<<"keyfile">> => list_to_binary(KeyFile),
|
||||||
<<"certfile">> => list_to_binary(CertFile)
|
<<"cacertfile">> => list_to_binary(CacertFile),
|
||||||
|
<<"certfile">> => list_to_binary(CertFile)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Dashboard3 = Dashboard#{<<"listeners">> => Listeners#{<<"https">> => Https3}},
|
Dashboard3 = Dashboard#{<<"listeners">> => Listeners#{<<"https">> => Https3}},
|
||||||
?assertMatch({ok, _}, update_config("dashboard", Dashboard3)),
|
?assertMatch({ok, _}, update_config("dashboard", Dashboard3)),
|
||||||
|
|
||||||
Dashboard4 = Dashboard#{<<"listeners">> => Listeners#{<<"https">> => #{<<"enable">> => false}}},
|
Dashboard4 = Dashboard#{<<"listeners">> => Listeners#{<<"https">> => #{<<"bind">> => 0}}},
|
||||||
?assertMatch({ok, _}, update_config("dashboard", Dashboard4)),
|
?assertMatch({ok, _}, update_config("dashboard", Dashboard4)),
|
||||||
{ok, Dashboard41} = get_config("dashboard"),
|
{ok, Dashboard41} = get_config("dashboard"),
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
Https3#{<<"enable">> => false},
|
Https3#{<<"bind">> => 0},
|
||||||
read_conf([<<"dashboard">>, <<"listeners">>, <<"https">>]),
|
read_conf([<<"dashboard">>, <<"listeners">>, <<"https">>]),
|
||||||
Dashboard41
|
Dashboard41
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Refactors the dashboard listener configuration to use a nested `ssl_options` field for ssl settings.
|
|
@ -7,7 +7,9 @@ backlog.label:
|
||||||
"""Backlog"""
|
"""Backlog"""
|
||||||
|
|
||||||
bind.desc:
|
bind.desc:
|
||||||
"""Port without IP(18083) or port with specified IP(127.0.0.1:18083)."""
|
"""Port without IP(18083) or port with specified IP(127.0.0.1:18083).
|
||||||
|
Disabled when setting bind to `0`.
|
||||||
|
"""
|
||||||
|
|
||||||
bind.label:
|
bind.label:
|
||||||
"""Bind"""
|
"""Bind"""
|
||||||
|
@ -136,4 +138,10 @@ token_expired_time.desc:
|
||||||
token_expired_time.label:
|
token_expired_time.label:
|
||||||
"""Token expired time"""
|
"""Token expired time"""
|
||||||
|
|
||||||
|
ssl_options.desc:
|
||||||
|
"""SSL/TLS options for the dashboard listener."""
|
||||||
|
|
||||||
|
ssl_options.label:
|
||||||
|
"""SSL options"""
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,10 @@ dashboard {
|
||||||
listeners.http {
|
listeners.http {
|
||||||
bind = 18083
|
bind = 18083
|
||||||
}
|
}
|
||||||
|
listeners.https {
|
||||||
|
bind = 18084
|
||||||
|
depth = 5
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
authentication = [
|
authentication = [
|
||||||
|
|
|
@ -7,9 +7,23 @@ EMQX_ROOT="${EMQX_ROOT:-_build/$PROFILE/rel/emqx}"
|
||||||
EMQX_WAIT_FOR_START="${EMQX_WAIT_FOR_START:-30}"
|
EMQX_WAIT_FOR_START="${EMQX_WAIT_FOR_START:-30}"
|
||||||
export EMQX_WAIT_FOR_START
|
export EMQX_WAIT_FOR_START
|
||||||
|
|
||||||
|
function check_dashboard_https_ssl_options_depth() {
|
||||||
|
if [[ $1 =~ v5\.0\.25 ]]; then
|
||||||
|
EXPECT_DEPTH=5
|
||||||
|
else
|
||||||
|
EXPECT_DEPTH=10
|
||||||
|
fi
|
||||||
|
DEPTH=$("$EMQX_ROOT"/bin/emqx eval "emqx:get_config([dashboard,listeners,https,ssl_options,depth],10)")
|
||||||
|
if [[ "$DEPTH" != "$EXPECT_DEPTH" ]]; then
|
||||||
|
echo "Bad Https depth $DEPTH, expect $EXPECT_DEPTH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
start_emqx_with_conf() {
|
start_emqx_with_conf() {
|
||||||
echo "Starting $PROFILE with $1"
|
echo "Starting $PROFILE with $1"
|
||||||
"$EMQX_ROOT"/bin/emqx start
|
"$EMQX_ROOT"/bin/emqx start
|
||||||
|
check_dashboard_https_ssl_options_depth "$1"
|
||||||
"$EMQX_ROOT"/bin/emqx stop
|
"$EMQX_ROOT"/bin/emqx stop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue