From 1a715c8708a3029cf559498ab2adc65197c2a3ae Mon Sep 17 00:00:00 2001 From: Georgy Sychev Date: Thu, 17 Feb 2022 22:49:48 +0300 Subject: [PATCH 1/7] feat(emqx_dashboard): subsciption to update password emqx_dashboard restarted in ekka hook Closes #3155 --- CHANGES-4.3.md | 1 + .../src/emqx_rule_engine.appup.src | 12 +- .../src/emqx_dashboard_admin.erl | 83 ++++---- .../test/emqx_dashboard_SUITE.erl | 149 ++++++++++++- src/emqx.appup.src | 200 ++++++------------ src/emqx.erl | 14 +- 6 files changed, 273 insertions(+), 186 deletions(-) diff --git a/CHANGES-4.3.md b/CHANGES-4.3.md index bb33daaba..77617a60c 100644 --- a/CHANGES-4.3.md +++ b/CHANGES-4.3.md @@ -25,6 +25,7 @@ File format: * Prohibit empty topics in strict mode * Make sure ehttpc delete useless pool always succeed. * Update mongodb driver to fix potential process leak. +* Dashboard admin password persists after leaving/joining the cluster ## v4.3.13 diff --git a/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src b/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src index e151086be..f47cff1f9 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src +++ b/apps/emqx_rule_engine/src/emqx_rule_engine.appup.src @@ -4,12 +4,12 @@ [{"4.3.8", [{load_module,emqx_rule_sqltester,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_runtime,brutal_purge,soft_purge,[]}, - {load_module,emqx_rule_registry,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_metrics,brutal_purge,soft_purge,[]}, - {load_module,emqx_rule_funcs,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_events,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_engine_api,brutal_purge,soft_purge,[]}, - {load_module,emqx_rule_engine,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_rule_engine,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_registry,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_funcs,brutal_purge,soft_purge,[]}]}, {"4.3.7", [{load_module,emqx_rule_sqltester,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_runtime,brutal_purge,soft_purge,[]}, @@ -101,12 +101,12 @@ [{"4.3.8", [{load_module,emqx_rule_sqltester,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_runtime,brutal_purge,soft_purge,[]}, - {load_module,emqx_rule_registry,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_metrics,brutal_purge,soft_purge,[]}, - {load_module,emqx_rule_funcs,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_events,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_engine_api,brutal_purge,soft_purge,[]}, - {load_module,emqx_rule_engine,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_rule_engine,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_registry,brutal_purge,soft_purge,[]}, + {load_module,emqx_rule_funcs,brutal_purge,soft_purge,[]}]}, {"4.3.7", [{load_module,emqx_rule_sqltester,brutal_purge,soft_purge,[]}, {load_module,emqx_rule_runtime,brutal_purge,soft_purge,[]}, diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl index 420380f88..505c468cd 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl @@ -21,7 +21,6 @@ -behaviour(gen_server). -include("emqx_dashboard.hrl"). --include_lib("emqx/include/logger.hrl"). -boot_mnesia({mnesia, [boot]}). -copy_mnesia({mnesia, [copy]}). @@ -146,9 +145,9 @@ update_pwd(Username, Fun) -> Trans = fun() -> User = case lookup_user(Username) of - [Admin] -> Admin; - [] -> - mnesia:abort(<<"Username Not Found">>) + [Admin] -> Admin; + [] -> + mnesia:abort(<<"Username Not Found">>) end, mnesia:write(Fun(User)) end, @@ -156,7 +155,14 @@ update_pwd(Username, Fun) -> -spec(lookup_user(binary()) -> [mqtt_admin()]). -lookup_user(Username) when is_binary(Username) -> mnesia:dirty_read(mqtt_admin, Username). +lookup_user(Username) when is_binary(Username) -> + case binenv(default_user_username) of + Username -> + Password = hashed_default_passwd(), + [#mqtt_admin{username=Username, password=Password, tags= <<"administrator">>}]; + _ -> + mnesia:dirty_read(mqtt_admin, Username) + end. -spec(all_users() -> [#mqtt_admin{}]). all_users() -> ets:tab2list(mqtt_admin). @@ -187,7 +193,8 @@ check(Username, Password) -> init([]) -> %% Add default admin user - _ = add_default_user(binenv(default_user_username), binenv(default_user_passwd)), + {ok, _} = mnesia:subscribe({table, mqtt_admin, simple}), + add_default_user_hashed(binenv(default_user_username), hashed_default_passwd()), {ok, state}. handle_call(_Req, _From, State) -> @@ -196,6 +203,17 @@ handle_call(_Req, _From, State) -> handle_cast(_Msg, State) -> {noreply, State}. +handle_info({mnesia_table_event, {write, Admin, _}}, State) -> + #mqtt_admin{username=Username, password=HashedPassword} = Admin, + case binenv(default_user_username) of + Username -> + application:set_env(emqx_dashboard, default_user_passwd_hashed, HashedPassword); + + _ -> + ignore + end, + {noreply, State}; + handle_info(_Msg, State) -> {noreply, State}. @@ -222,37 +240,26 @@ salt() -> <>. binenv(Key) -> - iolist_to_binary(application:get_env(emqx_dashboard, Key, "")). + iolist_to_binary(application:get_env(emqx_dashboard, Key, <<>>)). -add_default_user(Username, Password) when ?EMPTY_KEY(Username) orelse ?EMPTY_KEY(Password) -> - ignore; +add_default_user_hashed(Username, HashedPassword) -> + case mnesia:dirty_read(mqtt_admin, Username) of + [] -> + Admin = #mqtt_admin{username=Username, password=HashedPassword, tags= <<"administrator">>}, + return(mnesia:transaction(fun add_user_/1, [Admin])); + _ -> ok + end. -add_default_user(Username, Password) -> - case lookup_user(Username) of - [] -> add_user(Username, Password, <<"administrator">>); - _ -> - case check(Username, Password) of - ok -> - ?LOG(warning, - "[Dashboard] The initial default password for dashboard 'admin' user in emqx_dashboard.conf\n" - "For safety, it should be changed as soon as possible.\n" - "Please use the './bin/emqx_ctl admins' CLI to change it.\n" - "Then remove `dashboard.default_user.login/password` from emqx_dashboard.conf" - ); - {error, _} -> - %% We can't force add default, - %% otherwise passwords that have been updated via HTTP API will be reset after reboot. - ?LOG(warning, - "[Dashboard] dashboard.default_user.password in the plugins/emqx_dashboard.conf\n" - "does not match the password in the database(mnesia).\n" - "1. If you have already changed the password via the HTTP API or `./bin/emqx_ctl admins`," - "this warning has no effect.\n" - "You should remove the `dashboard.default_user.login/password` from emqx_dashboard.conf " - "to resolve this warning.\n" - "2. If you just want to update the password by manually changing the configuration file,\n" - "you need to delete the old user and password using `emqx_ctl admins del ~s` first\n" - "the new password in emqx_dashboard.conf can take effect after reboot.", - []) - end - end, - ok. +hashed_default_passwd() -> + case binenv(default_user_passwd_hashed) of + Empty0 when ?EMPTY_KEY(Empty0) -> + case binenv(default_user_passwd) of + Empty when ?EMPTY_KEY(Empty) -> + undefined; + Password -> + Hashed = hash(Password), + application:set_env(emqx_dashboard, default_user_passwd_hashed, Hashed), + Hashed + end; + HashedPassword -> HashedPassword + end. diff --git a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl index 5099d4449..28d80ae7d 100644 --- a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl +++ b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl @@ -29,6 +29,8 @@ -include_lib("emqx/include/emqx.hrl"). +-include("emqx_dashboard.hrl"). + -define(CONTENT_TYPE, "application/x-www-form-urlencoded"). -define(HOST, "http://127.0.0.1:18083/"). @@ -40,21 +42,23 @@ -define(OVERVIEWS, ['alarms/activated', 'alarms/deactivated', banned, brokers, stats, metrics, listeners, clients, subscriptions, routes, plugins]). all() -> - [{group, overview}, + [ + {group, overview}, {group, admins}, {group, rest}, {group, cli} ]. groups() -> - [{overview, [sequence], [t_overview]}, - {admins, [sequence], [t_admins_add_delete]}, - {rest, [sequence], [t_rest_api, t_auth_exhaustive_attack]}, + [ + {overview, [sequence], [t_overview]}, + {admins, [sequence], [t_admins_add_delete, t_admins_persist_default_password, t_default_password_persists_after_leaving_cluster]}, + {rest, [sequence], [t_rest_api]}, {cli, [sequence], [t_cli]} ]. init_per_suite(Config) -> - emqx_ct_helpers:start_apps([emqx_modules, emqx_management, emqx_dashboard]), + ok = emqx_ct_helpers:start_apps([emqx_modules, emqx_management, emqx_dashboard]), Config. end_per_suite(_Config) -> @@ -84,9 +88,93 @@ t_admins_add_delete(_) -> ?assertNotEqual(true, request_dashboard(get, api_path("brokers"), auth_header_("username", "pwd"))). +t_admins_persist_default_password(_) -> + emqx_dashboard_admin:change_password(<<"admin">>, <<"new_password">>), + ct:sleep(100), + [#mqtt_admin{password=Password, tags= <<"administrator">>}] = emqx_dashboard_admin:lookup_user(<<"admin">>), + + %% To ensure that state persists even if the process dies + application:stop(emqx_dashboard), + application:start(emqx_dashboard), + + ct:sleep(100), + + %% It get's restarted by the app automatically + [#mqtt_admin{password=PasswordAfterRestart}] = emqx_dashboard_admin:lookup_user(<<"admin">>), + ?assertEqual(Password, PasswordAfterRestart), + emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>). + +debug(Label, Slave) -> + ct:print( + "[~p]~nusers local ~p~nusers remote: ~p~nenv local: ~p~nenv remote: ~p", + [ + Label, + ets:tab2list(mqtt_admin), + rpc:call(Slave, ets, tab2list, [mqtt_admin]), + application:get_all_env(emqx_dashboard), + rpc:call(Slave, application, get_all_env, [emqx_dashboard]) + ]). + + +t_default_password_persists_after_leaving_cluster(_) -> + [#mqtt_admin{password=InitialPassword}] = emqx_dashboard_admin:lookup_user(<<"admin">>), + + ct:print("Cluster status: ~p", [ekka_cluster:info()]), + ct:print("Table nodes: ~p", [mnesia:table_info(mqtt_admin, active_replicas)]), + + Slave = start_slave('test1', [emqx_modules, emqx_management, emqx_dashboard]), + + %% To make sure that subscription is not lost during reconnection + rpc:call(Slave, ekka, leave, []), + ct:sleep(100), %% To ensure that leave gets processed + rpc:call(Slave, ekka, join, [node()]), + ct:sleep(100), %% To ensure that join gets processed + + ct:print("Cluster status: ~p", [ekka_cluster:info()]), + ct:print("Table nodes: ~p", [mnesia:table_info(mqtt_admin, active_replicas)]), + + ct:print("Apps: ~p", [ + rpc:call(Slave, application, which_applications, []) + ]), + + debug(0, Slave), + + emqx_dashboard_admin:change_password(<<"admin">>, <<"new_password">>), + ct:sleep(100), %% To ensure that event gets processed + + debug(1, Slave), + + [#mqtt_admin{password=Password}] = rpc:call(Slave, emqx_dashboard_admin, lookup_user, [<<"admin">>]), + ?assertNotEqual(InitialPassword, Password), + + rpc:call(Slave, ekka, leave, []), + + debug(2, Slave), + + rpc:call(Slave, application, stop, [emqx_dashboard]), + + debug(3, Slave), + + rpc:call(Slave, application, start, [emqx_dashboard]), + + debug(4, Slave), + + ?assertEqual( + ok, + rpc:call(Slave, emqx_dashboard_admin, check, [<<"admin">>, <<"new_password">>])), + + ?assertMatch( + {error, _}, + rpc:call(Slave, emqx_dashboard_admin, check, [<<"admin">>, <<"password">>])), + + {ok, _} = stop_slave(Slave, [emqx_dashboard, emqx_management, emqx_modules]), + + emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>). + t_rest_api(_Config) -> {ok, Res0} = http_get("users"), Users = get_http_data(Res0), + ct:pal("~p", [emqx_dashboard_admin:all_users()]), ?assert(lists:member(#{<<"username">> => <<"admin">>, <<"tags">> => <<"administrator">>}, Users)), @@ -170,3 +258,54 @@ api_path(Path) -> json(Data) -> {ok, Jsx} = emqx_json:safe_decode(Data, [return_maps]), Jsx. + +start_slave(Name, Apps) -> + {ok, Node} = ct_slave:start(list_to_atom(atom_to_list(Name) ++ "@" ++ host()), + [{kill_if_fail, true}, + {monitor_master, true}, + {init_timeout, 10000}, + {startup_timeout, 10000}, + {erl_flags, ebin_path()}]), + + pong = net_adm:ping(Node), + setup_node(Node, Apps), + Node. + +stop_slave(Node, Apps) -> + [ok = Res || Res <- rpc:call(Node, emqx_ct_helpers, stop_apps, [Apps])], + rpc:call(Node, ekka, leave, []), + ct_slave:stop(Node). + +host() -> + [_, Host] = string:tokens(atom_to_list(node()), "@"), Host. + +ebin_path() -> + string:join(["-pa" | lists:filter(fun is_lib/1, code:get_path())], " "). + +is_lib(Path) -> + string:prefix(Path, code:lib_dir()) =:= nomatch. + +setup_node(Node, Apps) -> + EnvHandler = + fun(emqx) -> + application:set_env(emqx, listeners, []), + application:set_env(gen_rpc, port_discovery, manual), + ok; + (emqx_management) -> + application:set_env(emqx_management, listeners, []), + ok; + (emqx_dashboard) -> + application:set_env(emqx_dashboard, listeners, []), + ok; + (_) -> + ok + end, + + [ok = rpc:call(Node, application, load, [App]) || App <- [gen_rpc, emqx | Apps]], + ok = rpc:call(Node, emqx_ct_helpers, start_apps, [Apps, EnvHandler]), + + rpc:call(Node, ekka, join, [node()]), + rpc:call(Node, application, stop, [emqx_dashboard]), + rpc:call(Node, application, start, [emqx_dashboard]), + + ok. diff --git a/src/emqx.appup.src b/src/emqx.appup.src index 8825da442..17a0e92e9 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -3,13 +3,12 @@ {VSN, [{"4.3.14", [{load_module,emqx_frame,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, + {load_module,emqx,brutal_purge,soft_purge,[]}]}, {"4.3.13", - [{load_module,emqx_plugins,brutal_purge,soft_purge,[]}, - {load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, + {load_module,emqx,brutal_purge,soft_purge,[]}, + {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_frame,brutal_purge,soft_purge,[]}, {load_module,emqx_sys_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -21,12 +20,9 @@ {load_module,emqx_ctl,brutal_purge,soft_purge,[]}, {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_misc,brutal_purge,soft_purge,[]}, - {load_module,emqx_connection,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_connection,brutal_purge,soft_purge,[]}]}, {"4.3.12", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_frame,brutal_purge,soft_purge,[]}, {load_module,emqx_misc,brutal_purge,soft_purge,[]}, @@ -48,12 +44,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.11", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -77,12 +70,9 @@ {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_http_lib,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.10", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_misc,brutal_purge,soft_purge,[]}, @@ -106,12 +96,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_connection,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.9", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_misc,brutal_purge,soft_purge,[]}, @@ -139,12 +126,9 @@ {load_module,emqx_os_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.8", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_misc,brutal_purge,soft_purge,[]}, @@ -172,12 +156,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.7", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -206,12 +187,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.6", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -240,12 +218,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.5", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -275,11 +250,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.4", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -310,11 +283,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.3", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -346,11 +317,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.2", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -382,11 +351,9 @@ {load_module,emqx_os_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.1", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, {load_module,emqx_sys,brutal_purge,soft_purge,[]}, @@ -421,11 +388,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.0", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, {load_module,emqx_sys,brutal_purge,soft_purge,[]}, @@ -463,18 +428,16 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {<<".*">>,[]}], [{"4.3.14", [{load_module,emqx_frame,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, + {load_module,emqx,brutal_purge,soft_purge,[]}]}, {"4.3.13", - [{load_module,emqx_plugins,brutal_purge,soft_purge,[]}, - {load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, + {load_module,emqx,brutal_purge,soft_purge,[]}, + {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_frame,brutal_purge,soft_purge,[]}, {load_module,emqx_sys_mon,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -486,12 +449,9 @@ {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_ctl,brutal_purge,soft_purge,[]}, {load_module,emqx_cm,brutal_purge,soft_purge,[]}, - {load_module,emqx_connection,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_connection,brutal_purge,soft_purge,[]}]}, {"4.3.12", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_frame,brutal_purge,soft_purge,[]}, {load_module,emqx_sys_mon,brutal_purge,soft_purge,[]}, @@ -512,12 +472,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.11", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_misc,brutal_purge,soft_purge,[]}, @@ -540,12 +497,9 @@ {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_http_lib,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.10", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_misc,brutal_purge,soft_purge,[]}, @@ -568,12 +522,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_connection,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.9", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_misc,brutal_purge,soft_purge,[]}, @@ -600,12 +551,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.8", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_misc,brutal_purge,soft_purge,[]}, @@ -632,12 +580,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.7", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -665,12 +610,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.6", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -698,12 +640,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.5", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, - {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -732,11 +671,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.4", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -766,11 +703,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.3", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -801,11 +736,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.2", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, @@ -836,11 +769,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.1", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, {load_module,emqx_sys,brutal_purge,soft_purge,[]}, @@ -874,11 +805,9 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.0", - [{load_module,emqx_flapping,brutal_purge,soft_purge,[]}, - {load_module,emqx_listeners,brutal_purge,soft_purge,[]}, + [{load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, {load_module,emqx_banned,brutal_purge,soft_purge,[]}, {load_module,emqx_sys,brutal_purge,soft_purge,[]}, @@ -914,6 +843,5 @@ {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]}, - {load_module,emqx_limiter,brutal_purge,soft_purge,[]}, - {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, + {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {<<".*">>,[]}]}. diff --git a/src/emqx.erl b/src/emqx.erl index 5c90cf953..7fb3c734f 100644 --- a/src/emqx.erl +++ b/src/emqx.erl @@ -234,7 +234,19 @@ shutdown(Reason) -> ). reboot() -> - lists:foreach(fun application:start/1 , default_started_applications()). + case is_application_running(emqx_dashboard) of + true -> + application:stop(emqx_dashboard), %% dashboard must be started after mnesia + lists:foreach(fun application:start/1 , default_started_applications()), + application:start(emqx_dashboard); + + false -> + lists:foreach(fun application:start/1 , default_started_applications()) + end. + +is_application_running(App) -> + StartedApps = proplists:get_value(started, application:info()), + proplists:is_defined(App, StartedApps). -ifdef(EMQX_ENTERPRISE). default_started_applications() -> From 0d4822ba781655abb1d71c68db64794dec8de855 Mon Sep 17 00:00:00 2001 From: "Zaiming (Stone) Shi" Date: Tue, 5 Apr 2022 10:52:11 +0200 Subject: [PATCH 2/7] refactor(emqx_dashboard_admin): simplify default pwd read/write --- .../src/emqx_dashboard_admin.erl | 74 ++++++++++++------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl index 505c468cd..8c4611a8b 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl @@ -156,13 +156,15 @@ update_pwd(Username, Fun) -> -spec(lookup_user(binary()) -> [mqtt_admin()]). lookup_user(Username) when is_binary(Username) -> - case binenv(default_user_username) of - Username -> - Password = hashed_default_passwd(), - [#mqtt_admin{username=Username, password=Password, tags= <<"administrator">>}]; + IsDefaultUser = binenv(default_user_username) =:= Username, + case mnesia:dirty_read(mqtt_admin, Username) of + [] when IsDefaultUser -> + _ = ensure_default_user_in_db(Username), + ok; _ -> - mnesia:dirty_read(mqtt_admin, Username) - end. + ok + end, + mnesia:dirty_read(mqtt_admin, Username). -spec(all_users() -> [#mqtt_admin{}]). all_users() -> ets:tab2list(mqtt_admin). @@ -194,7 +196,8 @@ check(Username, Password) -> init([]) -> %% Add default admin user {ok, _} = mnesia:subscribe({table, mqtt_admin, simple}), - add_default_user_hashed(binenv(default_user_username), hashed_default_passwd()), + PasswordHash = ensure_default_user_in_db(binenv(default_user_username)), + ok = ensure_default_user_passwd_hashed_in_app_env(PasswordHash), {ok, state}. handle_call(_Req, _From, State) -> @@ -204,11 +207,11 @@ handle_cast(_Msg, State) -> {noreply, State}. handle_info({mnesia_table_event, {write, Admin, _}}, State) -> - #mqtt_admin{username=Username, password=HashedPassword} = Admin, + %% the password is chagned from another node, sync it to app env + #mqtt_admin{username = Username, password = HashedPassword} = Admin, case binenv(default_user_username) of Username -> - application:set_env(emqx_dashboard, default_user_passwd_hashed, HashedPassword); - + ok = ensure_default_user_passwd_hashed_in_app_env(HashedPassword); _ -> ignore end, @@ -242,24 +245,43 @@ salt() -> binenv(Key) -> iolist_to_binary(application:get_env(emqx_dashboard, Key, <<>>)). -add_default_user_hashed(Username, HashedPassword) -> - case mnesia:dirty_read(mqtt_admin, Username) of - [] -> - Admin = #mqtt_admin{username=Username, password=HashedPassword, tags= <<"administrator">>}, - return(mnesia:transaction(fun add_user_/1, [Admin])); - _ -> ok - end. +ensure_default_user_in_db(Username) -> + F = + fun() -> + case mnesia:wread(mqtt_admin, Username) of + [] -> + PasswordHash = initial_default_user_passwd_hashed(), + Admin = #mqtt_admin{username = Username, + password = PasswordHash, + tags = <<"administrator">>}, + ok = mnesia:write(Admin), + PasswordHash; + [#mqtt_admin{password = PasswordHash}] -> + PasswordHash + end + end, + {atomic, PwdHash} = mnesia:transaction(F), + PwdHash. -hashed_default_passwd() -> - case binenv(default_user_passwd_hashed) of - Empty0 when ?EMPTY_KEY(Empty0) -> +initial_default_user_passwd_hashed() -> + case get_default_user_passwd_hashed_in_app_env() of + Empty when ?EMPTY_KEY(Empty) -> + %% in case it's not set yet case binenv(default_user_passwd) of Empty when ?EMPTY_KEY(Empty) -> - undefined; - Password -> - Hashed = hash(Password), - application:set_env(emqx_dashboard, default_user_passwd_hashed, Hashed), - Hashed + error({missing_configuration, default_user_passwd}); + Pwd -> + hash(Pwd) end; - HashedPassword -> HashedPassword + PwdHash -> + PwdHash end. + +%% use this app env for a copy of the value in mnesia database +%% so that after the node leaves a cluster, db gets purged, +%% we can still find the changed password back from this app env +ensure_default_user_passwd_hashed_in_app_env(Hashed) -> + ok = application:set_env(emqx_dashboard, default_user_passwd_hashed, Hashed). + +get_default_user_passwd_hashed_in_app_env() -> + application:get_env(emqx_dashboard, default_user_passwd_hashed, <<>>). From 6cc55f5c349a2e25cf2dda860422741ff85c365f Mon Sep 17 00:00:00 2001 From: "Zaiming (Stone) Shi" Date: Tue, 5 Apr 2022 12:22:04 +0200 Subject: [PATCH 3/7] chore: add back the default password warning --- .../src/emqx_dashboard_admin.erl | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl index 8c4611a8b..185737148 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl @@ -21,6 +21,7 @@ -behaviour(gen_server). -include("emqx_dashboard.hrl"). +-include_lib("emqx/include/logger.hrl"). -boot_mnesia({mnesia, [boot]}). -copy_mnesia({mnesia, [copy]}). @@ -180,8 +181,8 @@ check(_, undefined) -> {error, <<"Password undefined">>}; check(Username, Password) -> case lookup_user(Username) of - [#mqtt_admin{password = <>}] -> - case Hash =:= md5_hash(Salt, Password) of + [#mqtt_admin{password = PwdHash}] -> + case is_valid_pwd(PwdHash, Password) of true -> ok; false -> {error, <<"Username/Password error">>} end; @@ -189,6 +190,9 @@ check(Username, Password) -> {error, <<"Username/Password error">>} end. +is_valid_pwd(<>, Password) -> + Hash =:= md5_hash(Salt, Password). + %%-------------------------------------------------------------------- %% gen_server callbacks %%-------------------------------------------------------------------- @@ -198,6 +202,7 @@ init([]) -> {ok, _} = mnesia:subscribe({table, mqtt_admin, simple}), PasswordHash = ensure_default_user_in_db(binenv(default_user_username)), ok = ensure_default_user_passwd_hashed_in_app_env(PasswordHash), + ok = maybe_warn_default_pwd(), {ok, state}. handle_call(_Req, _From, State) -> @@ -248,7 +253,7 @@ binenv(Key) -> ensure_default_user_in_db(Username) -> F = fun() -> - case mnesia:wread(mqtt_admin, Username) of + case mnesia:wread({mqtt_admin, Username}) of [] -> PasswordHash = initial_default_user_passwd_hashed(), Admin = #mqtt_admin{username = Username, @@ -285,3 +290,17 @@ ensure_default_user_passwd_hashed_in_app_env(Hashed) -> get_default_user_passwd_hashed_in_app_env() -> application:get_env(emqx_dashboard, default_user_passwd_hashed, <<>>). + +maybe_warn_default_pwd() -> + case is_valid_pwd(initial_default_user_passwd_hashed(), <<"public">>) of + true -> + ?LOG(warning, + "[Dashboard] Using default password for dashboard 'admin' user. " + "Please use the './bin/emqx_ctl admins' CLI to change it. " + "NOTE: the default password in config file is only " + "used to initialise the database record, changing the config " + "file after database is initialised has no effect." + ); + false -> + ok + end. From 155befe418e5cb604d6cf054785d4db5dc7a4fab Mon Sep 17 00:00:00 2001 From: "Zaiming (Stone) Shi" Date: Tue, 5 Apr 2022 13:23:44 +0200 Subject: [PATCH 4/7] chore: no duplicated reads and some comment/message rewording Co-authored-by: Dmitrii <99872536+ieQu1@users.noreply.github.com> --- lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl | 12 ++++++------ lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl index 185737148..24fd58e1e 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl @@ -161,11 +161,11 @@ lookup_user(Username) when is_binary(Username) -> case mnesia:dirty_read(mqtt_admin, Username) of [] when IsDefaultUser -> _ = ensure_default_user_in_db(Username), - ok; - _ -> - ok - end, - mnesia:dirty_read(mqtt_admin, Username). + %% try to read again + mnesia:dirty_read(mqtt_admin, Username); + Res -> + Res + end. -spec(all_users() -> [#mqtt_admin{}]). all_users() -> ets:tab2list(mqtt_admin). @@ -296,7 +296,7 @@ maybe_warn_default_pwd() -> true -> ?LOG(warning, "[Dashboard] Using default password for dashboard 'admin' user. " - "Please use the './bin/emqx_ctl admins' CLI to change it. " + "Please use './bin/emqx_ctl admins' command to change it. " "NOTE: the default password in config file is only " "used to initialise the database record, changing the config " "file after database is initialised has no effect." diff --git a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl index 28d80ae7d..f9d6e5b78 100644 --- a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl +++ b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl @@ -99,7 +99,7 @@ t_admins_persist_default_password(_) -> ct:sleep(100), - %% It get's restarted by the app automatically + %% It gets restarted by the app automatically [#mqtt_admin{password=PasswordAfterRestart}] = emqx_dashboard_admin:lookup_user(<<"admin">>), ?assertEqual(Password, PasswordAfterRestart), emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>). From d219e475fb7a5942b358ee5db7de389d5b5af781 Mon Sep 17 00:00:00 2001 From: "Zaiming (Stone) Shi" Date: Tue, 5 Apr 2022 13:56:43 +0200 Subject: [PATCH 5/7] fix(emqx_dashboard_admin): put changed default user pwd in pt plugin app envs are all unloaded when the app reboots --- .../src/emqx_dashboard_admin.erl | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl index 24fd58e1e..8b35d167d 100644 --- a/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl +++ b/lib-ce/emqx_dashboard/src/emqx_dashboard_admin.erl @@ -201,7 +201,7 @@ init([]) -> %% Add default admin user {ok, _} = mnesia:subscribe({table, mqtt_admin, simple}), PasswordHash = ensure_default_user_in_db(binenv(default_user_username)), - ok = ensure_default_user_passwd_hashed_in_app_env(PasswordHash), + ok = ensure_default_user_passwd_hashed_in_pt(PasswordHash), ok = maybe_warn_default_pwd(), {ok, state}. @@ -212,11 +212,11 @@ handle_cast(_Msg, State) -> {noreply, State}. handle_info({mnesia_table_event, {write, Admin, _}}, State) -> - %% the password is chagned from another node, sync it to app env + %% the password is chagned from another node, sync it to persistent_term #mqtt_admin{username = Username, password = HashedPassword} = Admin, case binenv(default_user_username) of Username -> - ok = ensure_default_user_passwd_hashed_in_app_env(HashedPassword); + ok = ensure_default_user_passwd_hashed_in_pt(HashedPassword); _ -> ignore end, @@ -269,7 +269,7 @@ ensure_default_user_in_db(Username) -> PwdHash. initial_default_user_passwd_hashed() -> - case get_default_user_passwd_hashed_in_app_env() of + case get_default_user_passwd_hashed_from_pt() of Empty when ?EMPTY_KEY(Empty) -> %% in case it's not set yet case binenv(default_user_passwd) of @@ -282,14 +282,14 @@ initial_default_user_passwd_hashed() -> PwdHash end. -%% use this app env for a copy of the value in mnesia database +%% use this persistent_term for a copy of the value in mnesia database %% so that after the node leaves a cluster, db gets purged, -%% we can still find the changed password back from this app env -ensure_default_user_passwd_hashed_in_app_env(Hashed) -> - ok = application:set_env(emqx_dashboard, default_user_passwd_hashed, Hashed). +%% we can still find the changed password back from PT +ensure_default_user_passwd_hashed_in_pt(Hashed) -> + ok = persistent_term:put({?MODULE, default_user_passwd_hashed}, Hashed). -get_default_user_passwd_hashed_in_app_env() -> - application:get_env(emqx_dashboard, default_user_passwd_hashed, <<>>). +get_default_user_passwd_hashed_from_pt() -> + persistent_term:get({?MODULE, default_user_passwd_hashed}, <<>>). maybe_warn_default_pwd() -> case is_valid_pwd(initial_default_user_passwd_hashed(), <<"public">>) of From 164209b576fbb27f997f1e3e69a983a2e182a23a Mon Sep 17 00:00:00 2001 From: "Zaiming (Stone) Shi" Date: Tue, 5 Apr 2022 22:29:43 +0200 Subject: [PATCH 6/7] test: use end_per_testcase to restore default admin password --- lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl index f9d6e5b78..8bba12242 100644 --- a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl +++ b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl @@ -65,6 +65,13 @@ end_per_suite(_Config) -> emqx_ct_helpers:stop_apps([emqx_dashboard, emqx_management, emqx_modules]), ekka_mnesia:ensure_stopped(). +init_per_testcase(_, Config) -> + Config. + +end_per_testcase(_, _) -> + %% revert to default password + emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>). + t_overview(_) -> [?assert(request_dashboard(get, api_path(erlang:atom_to_list(Overview)), auth_header_()))|| Overview <- ?OVERVIEWS]. @@ -101,8 +108,7 @@ t_admins_persist_default_password(_) -> %% It gets restarted by the app automatically [#mqtt_admin{password=PasswordAfterRestart}] = emqx_dashboard_admin:lookup_user(<<"admin">>), - ?assertEqual(Password, PasswordAfterRestart), - emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>). + ?assertEqual(Password, PasswordAfterRestart). debug(Label, Slave) -> ct:print( @@ -168,8 +174,7 @@ t_default_password_persists_after_leaving_cluster(_) -> rpc:call(Slave, emqx_dashboard_admin, check, [<<"admin">>, <<"password">>])), {ok, _} = stop_slave(Slave, [emqx_dashboard, emqx_management, emqx_modules]), - - emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>). + ok. t_rest_api(_Config) -> {ok, Res0} = http_get("users"), From c422edd977571f9ba82dfc93368c873ac92ef3d9 Mon Sep 17 00:00:00 2001 From: "Zaiming (Stone) Shi" Date: Tue, 5 Apr 2022 23:41:35 +0200 Subject: [PATCH 7/7] test(emqx_dashboard_SUITE): add init/end_per_testcase --- .../test/emqx_dashboard_SUITE.erl | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl index 8bba12242..556fb8371 100644 --- a/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl +++ b/lib-ce/emqx_dashboard/test/emqx_dashboard_SUITE.erl @@ -65,16 +65,21 @@ end_per_suite(_Config) -> emqx_ct_helpers:stop_apps([emqx_dashboard, emqx_management, emqx_modules]), ekka_mnesia:ensure_stopped(). -init_per_testcase(_, Config) -> - Config. +init_per_testcase(Case, Config) -> + ?MODULE:Case({init, Config}). -end_per_testcase(_, _) -> +end_per_testcase(Case, Config) -> %% revert to default password - emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>). + emqx_dashboard_admin:change_password(<<"admin">>, <<"public">>), + ?MODULE:Case({'end', Config}). +t_overview({init, Config}) -> Config; +t_overview({'end', _Config}) -> ok; t_overview(_) -> [?assert(request_dashboard(get, api_path(erlang:atom_to_list(Overview)), auth_header_()))|| Overview <- ?OVERVIEWS]. +t_admins_add_delete({init, Config}) -> Config; +t_admins_add_delete({'end', _Config}) -> ok; t_admins_add_delete(_) -> ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, <<"tag">>), ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, <<"tag1">>), @@ -95,6 +100,8 @@ t_admins_add_delete(_) -> ?assertNotEqual(true, request_dashboard(get, api_path("brokers"), auth_header_("username", "pwd"))). +t_admins_persist_default_password({init, Config}) -> Config; +t_admins_persist_default_password({'end', _Config}) -> ok; t_admins_persist_default_password(_) -> emqx_dashboard_admin:change_password(<<"admin">>, <<"new_password">>), ct:sleep(100), @@ -122,13 +129,20 @@ debug(Label, Slave) -> ]). -t_default_password_persists_after_leaving_cluster(_) -> +t_default_password_persists_after_leaving_cluster({init, Config}) -> + Slave = start_slave('test1', [emqx_modules, emqx_management, emqx_dashboard]), + [{slave, Slave} | Config]; +t_default_password_persists_after_leaving_cluster({'end', Config}) -> + Slave = proplists:get_value(slave, Config), + {ok, _} = stop_slave(Slave, [emqx_dashboard, emqx_management, emqx_modules]), + ok; +t_default_password_persists_after_leaving_cluster(Config) -> + Slave = proplists:get_value(slave, Config), [#mqtt_admin{password=InitialPassword}] = emqx_dashboard_admin:lookup_user(<<"admin">>), ct:print("Cluster status: ~p", [ekka_cluster:info()]), ct:print("Table nodes: ~p", [mnesia:table_info(mqtt_admin, active_replicas)]), - Slave = start_slave('test1', [emqx_modules, emqx_management, emqx_dashboard]), %% To make sure that subscription is not lost during reconnection rpc:call(Slave, ekka, leave, []), @@ -172,10 +186,10 @@ t_default_password_persists_after_leaving_cluster(_) -> ?assertMatch( {error, _}, rpc:call(Slave, emqx_dashboard_admin, check, [<<"admin">>, <<"password">>])), - - {ok, _} = stop_slave(Slave, [emqx_dashboard, emqx_management, emqx_modules]), ok. +t_rest_api({init, Config}) -> Config; +t_rest_api({'end', _Config}) -> ok; t_rest_api(_Config) -> {ok, Res0} = http_get("users"), Users = get_http_data(Res0), @@ -196,11 +210,15 @@ t_rest_api(_Config) -> ]], ok. +t_auth_exhaustive_attack({init, Config}) -> Config; +t_auth_exhaustive_attack({'end', _Config}) -> ok; t_auth_exhaustive_attack(_Config) -> {ok, Res0} = http_post("auth", #{<<"username">> => <<"invalid_login">>, <<"password">> => <<"newpwd">>}), {ok, Res1} = http_post("auth", #{<<"username">> => <<"admin">>, <<"password">> => <<"invalid_password">>}), ?assertEqual(Res0, Res1). +t_cli({init, Config}) -> Config; +t_cli({'end', _Config}) -> ok; t_cli(_Config) -> [mnesia:dirty_delete({mqtt_admin, Admin}) || Admin <- mnesia:dirty_all_keys(mqtt_admin)], emqx_dashboard_cli:admins(["add", "username", "password"]), @@ -312,5 +330,4 @@ setup_node(Node, Apps) -> rpc:call(Node, ekka, join, [node()]), rpc:call(Node, application, stop, [emqx_dashboard]), rpc:call(Node, application, start, [emqx_dashboard]), - ok.