chore: delete internal acl code

This commit is contained in:
Rory Z 2021-06-25 17:36:32 +08:00 committed by zhanghongtong
parent d7c1cf6a5f
commit 1f7291380b
13 changed files with 19 additions and 488 deletions

View File

@ -1,152 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2017-2021 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_access_rule).
%% APIs
-export([ match/3
, compile/1
]).
-export_type([rule/0]).
-type(acl_result() :: allow | deny).
-type(who() :: all | binary() |
{client, binary()} |
{user, binary()} |
{ipaddr, esockd_cidr:cidr_string()}).
-type(access() :: subscribe | publish | pubsub).
-type(rule() :: {acl_result(), all} |
{acl_result(), who(), access(), list(emqx_topic:topic())}).
-define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))).
-define(PUBSUB(A), ((A =:= subscribe) orelse (A =:= publish) orelse (A =:= pubsub))).
%% @doc Compile Access Rule.
compile({A, all}) when ?ALLOW_DENY(A) ->
{A, all};
compile({A, Who, Access, Topic}) when ?ALLOW_DENY(A), ?PUBSUB(Access), is_binary(Topic) ->
{A, compile(who, Who), Access, [compile(topic, Topic)]};
compile({A, Who, Access, TopicFilters}) when ?ALLOW_DENY(A), ?PUBSUB(Access) ->
{A, compile(who, Who), Access, [compile(topic, Topic) || Topic <- TopicFilters]}.
compile(who, all) ->
all;
compile(who, {ipaddr, CIDR}) ->
{ipaddr, esockd_cidr:parse(CIDR, true)};
compile(who, {client, all}) ->
{client, all};
compile(who, {client, ClientId}) ->
{client, bin(ClientId)};
compile(who, {user, all}) ->
{user, all};
compile(who, {user, Username}) ->
{user, bin(Username)};
compile(who, {'and', Conds}) when is_list(Conds) ->
{'and', [compile(who, Cond) || Cond <- Conds]};
compile(who, {'or', Conds}) when is_list(Conds) ->
{'or', [compile(who, Cond) || Cond <- Conds]};
compile(topic, {eq, Topic}) ->
{eq, emqx_topic:words(bin(Topic))};
compile(topic, Topic) ->
Words = emqx_topic:words(bin(Topic)),
case pattern(Words) of
true -> {pattern, Words};
false -> Words
end.
pattern(Words) ->
lists:member(<<"%u">>, Words) orelse lists:member(<<"%c">>, Words).
bin(L) when is_list(L) ->
list_to_binary(L);
bin(B) when is_binary(B) ->
B.
%% @doc Match access rule
-spec(match(emqx_types:clientinfo(), emqx_types:topic(), rule())
-> {matched, allow} | {matched, deny} | nomatch).
match(_ClientInfo, _Topic, {AllowDeny, all}) when ?ALLOW_DENY(AllowDeny) ->
{matched, AllowDeny};
match(ClientInfo, Topic, {AllowDeny, Who, _PubSub, TopicFilters})
when ?ALLOW_DENY(AllowDeny) ->
case match_who(ClientInfo, Who)
andalso match_topics(ClientInfo, Topic, TopicFilters) of
true -> {matched, AllowDeny};
false -> nomatch
end.
match_who(_ClientInfo, all) ->
true;
match_who(_ClientInfo, {user, all}) ->
true;
match_who(_ClientInfo, {client, all}) ->
true;
match_who(#{clientid := ClientId}, {client, ClientId}) ->
true;
match_who(#{username := Username}, {user, Username}) ->
true;
match_who(#{peerhost := undefined}, {ipaddr, _Tup}) ->
false;
match_who(#{peerhost := IP}, {ipaddr, CIDR}) ->
esockd_cidr:match(IP, CIDR);
match_who(ClientInfo, {'and', Conds}) when is_list(Conds) ->
lists:foldl(fun(Who, Allow) ->
match_who(ClientInfo, Who) andalso Allow
end, true, Conds);
match_who(ClientInfo, {'or', Conds}) when is_list(Conds) ->
lists:foldl(fun(Who, Allow) ->
match_who(ClientInfo, Who) orelse Allow
end, false, Conds);
match_who(_ClientInfo, _Who) ->
false.
match_topics(_ClientInfo, _Topic, []) ->
false;
match_topics(ClientInfo, Topic, [{pattern, PatternFilter}|Filters]) ->
TopicFilter = feed_var(ClientInfo, PatternFilter),
match_topic(emqx_topic:words(Topic), TopicFilter)
orelse match_topics(ClientInfo, Topic, Filters);
match_topics(ClientInfo, Topic, [TopicFilter|Filters]) ->
match_topic(emqx_topic:words(Topic), TopicFilter)
orelse match_topics(ClientInfo, Topic, Filters).
match_topic(Topic, {eq, TopicFilter}) ->
Topic == TopicFilter;
match_topic(Topic, TopicFilter) ->
emqx_topic:match(Topic, TopicFilter).
feed_var(ClientInfo, Pattern) ->
feed_var(ClientInfo, Pattern, []).
feed_var(_ClientInfo, [], Acc) ->
lists:reverse(Acc);
feed_var(ClientInfo = #{clientid := undefined}, [<<"%c">>|Words], Acc) ->
feed_var(ClientInfo, Words, [<<"%c">>|Acc]);
feed_var(ClientInfo = #{clientid := ClientId}, [<<"%c">>|Words], Acc) ->
feed_var(ClientInfo, Words, [ClientId |Acc]);
feed_var(ClientInfo = #{username := undefined}, [<<"%u">>|Words], Acc) ->
feed_var(ClientInfo, Words, [<<"%u">>|Acc]);
feed_var(ClientInfo = #{username := Username}, [<<"%u">>|Words], Acc) ->
feed_var(ClientInfo, Words, [Username|Acc]);
feed_var(ClientInfo, [W|Words], Acc) ->
feed_var(ClientInfo, Words, [W|Acc]).

View File

@ -823,8 +823,7 @@ tr_modules(Conf) ->
[{emqx_mod_subscription, Subscriptions()}],
[{emqx_mod_rewrite, Rewrites()}],
[{emqx_mod_topic_metrics, []}],
[{emqx_mod_delayed, []}],
[{emqx_mod_acl_internal, [{acl_file, conf_get("acl.acl_file", Conf)}]}]
[{emqx_mod_delayed, []}]
]).
tr_sysmon(Conf) ->

View File

@ -1,2 +1 @@
{emqx_mod_acl_internal, true}.
{emqx_mod_presence, true}.

View File

@ -1,15 +0,0 @@
{allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]}.
{allow, {user, "testuser"}, subscribe, ["a/b/c", "d/e/f/#"]}.
{allow, {user, "admin"}, pubsub, ["a/b/c", "d/e/f/#"]}.
{allow, {client, "testClient"}, subscribe, ["testTopics/testClient"]}.
{allow, all, subscribe, ["clients/%c"]}.
{allow, all, pubsub, ["users/%u/#"]}.
{deny, all, subscribe, ["$SYS/#", "#"]}.
{deny, all}.

View File

@ -1,3 +0,0 @@
{deny, {user, "emqx"}, pubsub, ["acl_deny_action"]}.
{deny, {user, "pub_deny"}, publish, ["pub_deny"]}.
{allow, all}.

View File

@ -1,97 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2019-2021 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_access_rule_SUITE).
-compile(export_all).
-compile(nowarn_export_all).
-include_lib("eunit/include/eunit.hrl").
all() -> emqx_ct:all(?MODULE).
init_per_suite(Config) ->
emqx_ct_helpers:boot_modules([router, broker]),
emqx_ct_helpers:start_apps([]),
Config.
end_per_suite(_Config) ->
emqx_ct_helpers:stop_apps([]).
t_compile(_) ->
Rule1 = {allow, all, pubsub, <<"%u">>},
Compile1 = {allow, all, pubsub, [{pattern,[<<"%u">>]}]},
Rule2 = {allow, {ipaddr, "127.0.0.1"}, pubsub, <<"%c">>},
Compile2 = {allow, {ipaddr, {{127,0,0,1}, {127,0,0,1}, 32}}, pubsub, [{pattern,[<<"%c">>]}]},
Rule3 = {allow, {'and', [{client, <<"testClient">>}, {user, <<"testUser">>}]}, pubsub, [<<"testTopics1">>, <<"testTopics2">>]},
Compile3 = {allow, {'and', [{client, <<"testClient">>}, {user, <<"testUser">>}]}, pubsub, [[<<"testTopics1">>], [<<"testTopics2">>]]},
Rule4 = {allow, {'or', [{client, all}, {user, all}]}, pubsub, [ <<"testTopics1">>, <<"testTopics2">>]},
Compile4 = {allow, {'or', [{client, all}, {user, all}]}, pubsub, [[<<"testTopics1">>], [<<"testTopics2">>]]},
?assertEqual(Compile1, emqx_access_rule:compile(Rule1)),
?assertEqual(Compile2, emqx_access_rule:compile(Rule2)),
?assertEqual(Compile3, emqx_access_rule:compile(Rule3)),
?assertEqual(Compile4, emqx_access_rule:compile(Rule4)).
t_match(_) ->
ClientInfo1 = #{zone => external,
clientid => <<"testClient">>,
username => <<"TestUser">>,
peerhost => {127,0,0,1}
},
ClientInfo2 = #{zone => external,
clientid => <<"testClient">>,
username => <<"TestUser">>,
peerhost => {192,168,0,10}
},
ClientInfo3 = #{zone => external,
clientid => <<"testClient">>,
username => <<"TestUser">>,
peerhost => undefined
},
?assertEqual({matched, deny}, emqx_access_rule:match([], [], {deny, all})),
?assertEqual({matched, allow}, emqx_access_rule:match([], [], {allow, all})),
?assertEqual(nomatch, emqx_access_rule:match(ClientInfo1, <<"Test/Topic">>,
emqx_access_rule:compile({allow, {user, all}, pubsub, []}))),
?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Test/Topic">>,
emqx_access_rule:compile({allow, {client, all}, pubsub, ["$SYS/#", "#"]}))),
?assertEqual(nomatch, emqx_access_rule:match(ClientInfo3, <<"Test/Topic">>,
emqx_access_rule:compile({allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}))),
?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Test/Topic">>,
emqx_access_rule:compile({allow, {ipaddr, "127.0.0.1"}, subscribe, ["$SYS/#", "#"]}))),
?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo2, <<"Test/Topic">>,
emqx_access_rule:compile({allow, {ipaddr, "192.168.0.1/24"}, subscribe, ["$SYS/#", "#"]}))),
?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"d/e/f/x">>,
emqx_access_rule:compile({allow, {user, "TestUser"}, subscribe, ["a/b/c", "d/e/f/#"]}))),
?assertEqual(nomatch, emqx_access_rule:match(ClientInfo1, <<"d/e/f/x">>,
emqx_access_rule:compile({allow, {user, "admin"}, pubsub, ["d/e/f/#"]}))),
?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"testTopics/testClient">>,
emqx_access_rule:compile({allow, {client, "testClient"}, publish, ["testTopics/testClient"]}))),
?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"clients/testClient">>,
emqx_access_rule:compile({allow, all, pubsub, ["clients/%c"]}))),
?assertEqual({matched, allow}, emqx_access_rule:match(#{username => <<"user2">>}, <<"users/user2/abc/def">>,
emqx_access_rule:compile({allow, all, subscribe, ["users/%u/#"]}))),
?assertEqual({matched, deny}, emqx_access_rule:match(ClientInfo1, <<"d/e/f">>,
emqx_access_rule:compile({deny, all, subscribe, ["$SYS/#", "#"]}))),
?assertEqual(nomatch, emqx_access_rule:match(ClientInfo1, <<"Topic">>,
emqx_access_rule:compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, <<"Topic">>}))),
?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Topic">>,
emqx_access_rule:compile({allow, {'and', [{ipaddr, "127.0.0.1"}, {user, <<"TestUser">>}]}, publish, <<"Topic">>}))),
?assertEqual({matched, allow}, emqx_access_rule:match(ClientInfo1, <<"Topic">>,
emqx_access_rule:compile({allow, {'or', [{ipaddr, "127.0.0.1"}, {user, <<"WrongUser">>}]}, publish, ["Topic"]}))).

View File

@ -266,11 +266,18 @@ t_kick_1(_Config) ->
% mqtt connection kicked by coap with same client id
t_acl(Config) ->
%% Update acl file and reload mod_acl_internal
Path = filename:join([testdir(proplists:get_value(data_dir, Config)), "deny.conf"]),
ok = file:write_file(Path, <<"{deny, {user, \"coap\"}, publish, [\"abc\"]}.">>),
OldPath = emqx:get_env(acl_file),
emqx_mod_acl_internal:reload([{acl_file, Path}]),
OldPath = emqx:get_env(plugins_etc_dir),
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_authz, "test")),
Conf = #{<<"authz">> =>
#{<<"rules">> =>
[#{<<"principal">> =>#{<<"username">> => <<"coap">>},
<<"permission">> => deny,
<<"topics">> => [<<"abc">>],
<<"action">> => <<"publish">>}
]}},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf'), jsx:encode(Conf)),
application:ensure_all_started(emqx_authz),
emqx:subscribe(<<"abc">>),
URI = "coap://127.0.0.1/mqtt/adbc?c=client1&u=coap&p=secret",
@ -282,9 +289,9 @@ t_acl(Config) ->
ok
end,
application:set_env(emqx, acl_file, OldPath),
file:delete(Path),
emqx_mod_acl_internal:reload([{acl_file, OldPath}]).
file:delete(filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf')),
application:set_env(emqx, plugins_etc_dir, OldPath),
application:stop(emqx_authz).
t_stats(_) ->
ok.

View File

@ -1,122 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 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_mod_acl_internal).
-behaviour(emqx_gen_mod).
-include_lib("emqx/include/emqx.hrl").
-include_lib("emqx/include/logger.hrl").
-logger_header("[ACL_INTERNAL]").
%% APIs
-export([ check_acl/5
, rules_from_file/1
]).
%% emqx_gen_mod callbacks
-export([ load/1
, unload/1
, reload/1
, description/0
]).
-type(acl_rules() :: #{publish => [emqx_access_rule:rule()],
subscribe => [emqx_access_rule:rule()]}).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
load(Env) ->
Rules = rules_from_file(proplists:get_value(acl_file, Env)),
emqx_hooks:add('client.check_acl', {?MODULE, check_acl, [Rules]}, -1).
unload(_Env) ->
emqx_hooks:del('client.check_acl', {?MODULE, check_acl}).
reload(Env) ->
emqx_acl_cache:is_enabled() andalso (
lists:foreach(
fun(Pid) -> erlang:send(Pid, clean_acl_cache) end,
emqx_cm:all_channels())),
unload(Env), load(Env).
description() ->
"EMQ X Internal ACL Module".
%%--------------------------------------------------------------------
%% ACL callbacks
%%--------------------------------------------------------------------
%% @doc Check ACL
-spec(check_acl(emqx_types:clientinfo(), emqx_types:pubsub(), emqx_topic:topic(),
emqx_access_rule:acl_result(), acl_rules())
-> {ok, allow} | {ok, deny} | ok).
check_acl(Client, PubSub, Topic, _AclResult, Rules) ->
case match(Client, Topic, lookup(PubSub, Rules)) of
{matched, allow} -> {ok, allow};
{matched, deny} -> {ok, deny};
nomatch -> ok
end.
%%--------------------------------------------------------------------
%% Internal Functions
%%--------------------------------------------------------------------
lookup(PubSub, Rules) ->
maps:get(PubSub, Rules, []).
match(_Client, _Topic, []) ->
nomatch;
match(Client, Topic, [Rule|Rules]) ->
case emqx_access_rule:match(Client, Topic, Rule) of
nomatch ->
match(Client, Topic, Rules);
{matched, AllowDeny} ->
{matched, AllowDeny}
end.
-spec(rules_from_file(file:filename()) -> map()).
rules_from_file(AclFile) ->
case file:consult(AclFile) of
{ok, Terms} ->
Rules = [emqx_access_rule:compile(Term) || Term <- Terms],
#{publish => [Rule || Rule <- Rules, filter(publish, Rule)],
subscribe => [Rule || Rule <- Rules, filter(subscribe, Rule)]};
{error, eacces} ->
?LOG(alert, "Insufficient permissions to read the ~s file", [AclFile]),
#{};
{error, enoent} ->
?LOG(alert, "The ~s file does not exist", [AclFile]),
#{};
{error, Reason} ->
?LOG(alert, "Failed to read ~s: ~p", [AclFile, Reason]),
#{}
end.
filter(_PubSub, {allow, all}) ->
true;
filter(_PubSub, {deny, all}) ->
true;
filter(publish, {_AllowDeny, _Who, publish, _Topics}) ->
true;
filter(_PubSub, {_AllowDeny, _Who, pubsub, _Topics}) ->
true;
filter(subscribe, {_AllowDeny, _Who, subscribe, _Topics}) ->
true;
filter(_PubSub, {_AllowDeny, _Who, _, _Topics}) ->
false.

View File

@ -80,17 +80,6 @@ unload(ModuleName) ->
end.
-spec(reload(module()) -> ok | ignore | {error, any()}).
reload(emqx_mod_acl_internal) ->
Modules = emqx:get_env(modules, []),
Env = proplists:get_value(emqx_mod_acl_internal, Modules, undefined),
case emqx_mod_acl_internal:reload(Env) of
ok ->
?LOG(info, "Reload ~s module successfully.", [emqx_mod_acl_internal]),
ok;
{error, Error} ->
?LOG(error, "Reload module ~s failed, cannot start for ~0p", [emqx_mod_acl_internal, Error]),
{error, Error}
end;
reload(_) ->
ignore.
@ -197,13 +186,6 @@ cli(["unload", Name]) ->
emqx_ctl:print("Unload module ~s error: ~p.~n", [Name, Reason])
end;
cli(["reload", "emqx_mod_acl_internal" = Name]) ->
case emqx_modules:reload(list_to_atom(Name)) of
ok ->
emqx_ctl:print("Module ~s reloaded successfully.~n", [Name]);
{error, Reason} ->
emqx_ctl:print("Reload module ~s error: ~p.~n", [Name, Reason])
end;
cli(["reload", Name]) ->
emqx_ctl:print("Module: ~p does not need to be reloaded.~n", [Name]);

View File

@ -1,64 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 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_mod_acl_internal_SUITE).
-compile(export_all).
-compile(nowarn_export_all).
-include_lib("emqx/include/emqx_mqtt.hrl").
-include_lib("eunit/include/eunit.hrl").
all() -> emqx_ct:all(?MODULE).
init_per_suite(Config) ->
emqx_ct_helpers:boot_modules(all),
emqx_ct_helpers:start_apps([]),
Config.
end_per_suite(_Config) ->
emqx_ct_helpers:stop_apps([]).
t_load_unload(_) ->
?assertEqual(ok, emqx_mod_acl_internal:unload([])),
?assertEqual(ok, emqx_mod_acl_internal:load([])),
?assertEqual({error,already_exists}, emqx_mod_acl_internal:load([])).
t_check_acl(_) ->
Rules=#{publish => [{allow,all}], subscribe => [{deny, all}]},
?assertEqual({ok, allow}, emqx_mod_acl_internal:check_acl(clientinfo(), publish, <<"t">>, [], Rules)),
?assertEqual({ok, deny}, emqx_mod_acl_internal:check_acl(clientinfo(), subscribe, <<"t">>, [], Rules)),
?assertEqual(ok, emqx_mod_acl_internal:check_acl(clientinfo(), connect, <<"t">>, [], Rules)).
t_reload_acl(_) ->
?assertEqual(ok, emqx_mod_acl_internal:reload([])).
%%--------------------------------------------------------------------
%% Helper functions
%%--------------------------------------------------------------------
clientinfo() -> clientinfo(#{}).
clientinfo(InitProps) ->
maps:merge(#{zone => zone,
protocol => mqtt,
peerhost => {127,0,0,1},
clientid => <<"clientid">>,
username => <<"username">>,
password => <<"passwd">>,
is_superuser => false,
peercert => undefined,
mountpoint => undefined
}, InitProps).

View File

@ -49,8 +49,7 @@ t_load(_) ->
?assertEqual(ok, emqx_modules:load()),
?assertEqual({error, not_found}, emqx_modules:load(not_existed_module)),
?assertEqual({error, not_started}, emqx_modules:unload(emqx_mod_rewrite)),
?assertEqual(ignore, emqx_modules:reload(emqx_mod_rewrite)),
?assertEqual(ok, emqx_modules:reload(emqx_mod_acl_internal)).
?assertEqual(ignore, emqx_modules:reload(emqx_mod_rewrite)).
t_list(_) ->
?assertMatch([{_, _} | _ ], emqx_modules:list()).

View File

@ -87,7 +87,6 @@ emqxLoadedPlugins: >
{emqx_bridge_mqtt, false}.
emqxLoadedModules: >
{emqx_mod_acl_internal, true}.
{emqx_mod_presence, true}.
{emqx_mod_delayed, false}.
{emqx_mod_rewrite, false}.

View File

@ -97,12 +97,11 @@ For example, set mqtt tcp port to 1883
Default environment variable ``EMQX_LOADED_MODULES``, including
+ ``emqx_mod_acl_internal``
+ ``emqx_mod_presence``
```bash
# The default EMQX_LOADED_MODULES env
EMQX_LOADED_MODULES="emqx_mod_acl_internal,emqx_mod_acl_internal"
EMQX_LOADED_MODULES="emqx_mod_presence"
```
For example, set ``EMQX_LOADED_MODULES=emqx_mod_delayed,emqx_mod_rewrite`` to load these two modules.