feat: bootstrap dashboard users from dashboard.bootstrap_users_file
This commit is contained in:
parent
dd95a26270
commit
04c0caefac
|
@ -17,6 +17,9 @@
|
|||
|
||||
- Enhanced log security in ACL modules, sensitive data will be obscured. [#9242](https://github.com/emqx/emqx/pull/9242).
|
||||
|
||||
- Add `dashboard.bootstrap_users_file` configuration to bulk import default user&password when EMQX first starts.
|
||||
|
||||
|
||||
## Bug fixes
|
||||
|
||||
- Fix that after uploading a backup file with an UTF8 filename, HTTP API `GET /data/export` fails with status code 500 [#9224](https://github.com/emqx/emqx/pull/9224).
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
- 增强 ACL 模块中的日志安全性,敏感数据将被模糊化。[#9242](https://github.com/emqx/emqx/pull/9242)。
|
||||
|
||||
- 增加 `dashboard.bootstrap_users_file` 配置,可以在EMQX第一次启动时批量导入默认的用户/密码。
|
||||
|
||||
|
||||
## 修复
|
||||
|
||||
- 修复若上传的备份文件名中包含 UTF8 字符,`GET /data/export` HTTP 接口返回 500 错误 [#9224](https://github.com/emqx/emqx/pull/9224)。
|
||||
|
|
|
@ -17,6 +17,15 @@ dashboard.default_user.login = admin
|
|||
## Value: String
|
||||
dashboard.default_user.password = public
|
||||
|
||||
## Initialize users file
|
||||
## Is used to add an administrative user to Dashboard when emqx is first launched,
|
||||
## the format is:
|
||||
## ```
|
||||
##username1:password1
|
||||
##username2:password2
|
||||
## ```
|
||||
dashboard.bootstrap_users_file = {{ platform_etc_dir }}/bootstrap_users.txt
|
||||
|
||||
##--------------------------------------------------------------------
|
||||
## HTTP Listener
|
||||
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
{override_env, "ADMIN_PASSWORD"}
|
||||
]}.
|
||||
|
||||
{mapping, "dashboard.bootstrap_users_file", "emqx_dashboard.bootstrap_users_file", [
|
||||
{datatype, string},
|
||||
hidden
|
||||
]}.
|
||||
|
||||
{mapping, "dashboard.listener.http", "emqx_dashboard.listeners", [
|
||||
{datatype, [integer, ip]}
|
||||
]}.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{application, emqx_dashboard,
|
||||
[{description, "EMQ X Web Dashboard"},
|
||||
{vsn, "4.3.18"}, % strict semver, bump manually!
|
||||
{vsn, "4.3.19"}, % strict semver, bump manually!
|
||||
{modules, []},
|
||||
{registered, [emqx_dashboard_sup]},
|
||||
{applications, [kernel,stdlib,mnesia,minirest]},
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
-include("emqx_dashboard.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-define(DEFAULT_PASSWORD, <<"public">>).
|
||||
-define(BOOTSTRAP_USER_TAG, <<"bootstrap user">>).
|
||||
|
||||
-boot_mnesia({mnesia, [boot]}).
|
||||
-copy_mnesia({mnesia, [copy]}).
|
||||
|
@ -43,6 +44,7 @@
|
|||
, change_password/3
|
||||
, all_users/0
|
||||
, check/2
|
||||
, add_bootstrap_users/0
|
||||
]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
|
@ -195,6 +197,75 @@ check(Username, Password) ->
|
|||
{error, <<"Username/Password error">>}
|
||||
end.
|
||||
|
||||
add_bootstrap_users() ->
|
||||
Bootstrap = application:get_env(emqx_dashboard, bootstrap_users_file, undefined),
|
||||
Size = mnesia:table_info(mqtt_admin, size),
|
||||
add_bootstrap_users(Bootstrap, Size).
|
||||
|
||||
add_bootstrap_users(undefined, _) -> ok;
|
||||
add_bootstrap_users(_File, Size)when Size > 0 -> ok;
|
||||
add_bootstrap_users(File, 0) ->
|
||||
case file:open(File, [read, binary]) of
|
||||
{ok, Dev} ->
|
||||
{ok, MP} = re:compile(<<"(\.+):(\.+$)">>, [ungreedy]),
|
||||
case add_bootstrap_users(File, Dev, MP) of
|
||||
ok -> ok;
|
||||
Error ->
|
||||
%% if failed add bootstrap users, we should clear all bootstrap users
|
||||
mnesia:transaction(fun clear_bootstrap_users/0, []),
|
||||
Error
|
||||
end;
|
||||
{error, Reason} = Error ->
|
||||
?LOG(error,
|
||||
"failed to open the dashboard bootstrap users file(~s) for ~p",
|
||||
[File, Reason]
|
||||
),
|
||||
Error
|
||||
end.
|
||||
|
||||
add_bootstrap_users(File, Dev, MP) ->
|
||||
try
|
||||
add_bootstrap_user(File, Dev, MP, 1)
|
||||
catch
|
||||
throw:Error -> {error, Error};
|
||||
Type:Reason:Stacktrace ->
|
||||
{error, {Type, Reason, Stacktrace}}
|
||||
after
|
||||
file:close(Dev)
|
||||
end.
|
||||
|
||||
add_bootstrap_user(File, Dev, MP, Line) ->
|
||||
case file:read_line(Dev) of
|
||||
{ok, Bin} ->
|
||||
case re:run(Bin, MP, [global, {capture, all_but_first, binary}]) of
|
||||
{match, [[Username, Password]]} ->
|
||||
case add_user(Username, Password, ?BOOTSTRAP_USER_TAG) of
|
||||
ok ->
|
||||
add_bootstrap_user(File, Dev, MP, Line + 1);
|
||||
Reason ->
|
||||
throw(#{file => File, line => Line, content => Bin, reason => Reason})
|
||||
end;
|
||||
_ ->
|
||||
?LOG(error,
|
||||
"failed to bootstrap users file(~s) for Line(~w): ~ts",
|
||||
[File, Line, Bin]
|
||||
),
|
||||
throw(#{file => File, line => Line, content => Bin, reason => "invalid format"})
|
||||
end;
|
||||
eof ->
|
||||
ok;
|
||||
Error ->
|
||||
throw(#{file => File, line => Line, reason => Error})
|
||||
end.
|
||||
|
||||
clear_bootstrap_users() ->
|
||||
FoldFun =
|
||||
fun(#mqtt_admin{tags = ?BOOTSTRAP_USER_TAG} = User, Acc) ->
|
||||
mnesia:delete_object(User), Acc;
|
||||
(_, Acc) -> Acc
|
||||
end,
|
||||
mnesia:foldl(FoldFun, ok, mqtt_admin).
|
||||
|
||||
bad_login_penalty() ->
|
||||
timer:sleep(2000),
|
||||
ok.
|
||||
|
@ -207,16 +278,20 @@ is_valid_pwd(<<Salt:4/binary, Hash/binary>>, Password) ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
case binenv(default_user_username) of
|
||||
<<>> -> ok;
|
||||
UserName ->
|
||||
%% Add default admin user
|
||||
{ok, _} = mnesia:subscribe({table, mqtt_admin, simple}),
|
||||
PasswordHash = ensure_default_user_in_db(UserName),
|
||||
ok = ensure_default_user_passwd_hashed_in_pt(PasswordHash),
|
||||
ok = maybe_warn_default_pwd()
|
||||
end,
|
||||
{ok, state}.
|
||||
case add_bootstrap_users() of
|
||||
ok ->
|
||||
case binenv(default_user_username) of
|
||||
<<>> -> ok;
|
||||
UserName ->
|
||||
%% Add default admin user
|
||||
{ok, _} = mnesia:subscribe({table, mqtt_admin, simple}),
|
||||
PasswordHash = ensure_default_user_in_db(UserName),
|
||||
ok = ensure_default_user_passwd_hashed_in_pt(PasswordHash),
|
||||
ok = maybe_warn_default_pwd()
|
||||
end,
|
||||
{ok, state};
|
||||
Error -> {stop, Error}
|
||||
end.
|
||||
|
||||
handle_call(_Req, _From, State) ->
|
||||
{reply, error, State}.
|
||||
|
|
Loading…
Reference in New Issue