Merge pull request #8426 from zmstone/0705-add-quick-deny-for-anonymous

feat: add a quick deny option to allow_anonymous config
This commit is contained in:
Zaiming (Stone) Shi 2022-07-06 14:00:03 +01:00 committed by GitHub
commit 3db6fd85bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 53 deletions

View File

@ -807,7 +807,7 @@ end}.
%% @doc Allow anonymous authentication. %% @doc Allow anonymous authentication.
{mapping, "allow_anonymous", "emqx.allow_anonymous", [ {mapping, "allow_anonymous", "emqx.allow_anonymous", [
{default, false}, {default, false},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false, false_quick_deny]}}
]}. ]}.
%% @doc ACL nomatch. %% @doc ACL nomatch.
@ -962,7 +962,7 @@ end}.
]}. ]}.
{mapping, "zone.$name.allow_anonymous", "emqx.zones", [ {mapping, "zone.$name.allow_anonymous", "emqx.zones", [
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false, false_quick_deny]}}
]}. ]}.
{mapping, "zone.$name.acl_nomatch", "emqx.zones", [ {mapping, "zone.$name.acl_nomatch", "emqx.zones", [

View File

@ -6,7 +6,7 @@
%% the emqx `release' version, which in turn is comprised of several %% the emqx `release' version, which in turn is comprised of several
%% apps, one of which is this. See `emqx_release.hrl' for more %% apps, one of which is this. See `emqx_release.hrl' for more
%% info. %% info.
{vsn, "4.3.17"}, % strict semver, bump manually! {vsn, "4.3.18"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, []}, {registered, []},
{applications, [ kernel {applications, [ kernel

View File

@ -1,8 +1,10 @@
%% -*- mode: erlang -*- %% -*- mode: erlang -*-
%% Unless you know what you are doing, DO NOT edit manually!! %% Unless you know what you are doing, DO NOT edit manually!!
{VSN, {VSN,
[{"4.3.16", [{"4.3.17",[{load_module,emqx_access_control,brutal_purge,soft_purge,[]}]},
[{load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {"4.3.16",
[{load_module,emqx_access_control,brutal_purge,soft_purge,[]},
{load_module,emqx_plugins,brutal_purge,soft_purge,[]},
{load_module,emqx_metrics,brutal_purge,soft_purge,[]}, {load_module,emqx_metrics,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]},
{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, {load_module,emqx_access_rule,brutal_purge,soft_purge,[]},
@ -650,8 +652,10 @@
{load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_message,brutal_purge,soft_purge,[]},
{load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]},
{<<".*">>,[]}], {<<".*">>,[]}],
[{"4.3.16", [{"4.3.17",[{load_module,emqx_access_control,brutal_purge,soft_purge,[]}]},
[{load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {"4.3.16",
[{load_module,emqx_access_control,brutal_purge,soft_purge,[]},
{load_module,emqx_plugins,brutal_purge,soft_purge,[]},
{load_module,emqx_metrics,brutal_purge,soft_purge,[]}, {load_module,emqx_metrics,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_app,brutal_purge,soft_purge,[]},
{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, {load_module,emqx_access_rule,brutal_purge,soft_purge,[]},

View File

@ -33,15 +33,13 @@
-spec(authenticate(emqx_types:clientinfo()) -> {ok, result()} | {error, term()}). -spec(authenticate(emqx_types:clientinfo()) -> {ok, result()} | {error, term()}).
authenticate(ClientInfo = #{zone := Zone}) -> authenticate(ClientInfo = #{zone := Zone}) ->
AuthResult = default_auth_result(Zone), ok = emqx_metrics:inc('client.authenticate'),
case Username = maps:get(username, ClientInfo, undefined),
begin ok = emqx_metrics:inc('client.authenticate'), {MaybeStop, AuthResult} = default_auth_result(Username, Zone),
emqx_zone:get_env(Zone, bypass_auth_plugins, false) case MaybeStop of
end stop ->
of
true ->
return_auth_result(AuthResult); return_auth_result(AuthResult);
false -> continue ->
return_auth_result(emqx_hooks:run_fold('client.authenticate', [ClientInfo], AuthResult)) return_auth_result(emqx_hooks:run_fold('client.authenticate', [ClientInfo], AuthResult))
end. end.
@ -91,10 +89,29 @@ inc_acl_metrics(cache_hit) ->
emqx_metrics:inc('client.acl.cache_hit'). emqx_metrics:inc('client.acl.cache_hit').
%% Auth %% Auth
default_auth_result(Zone) -> default_auth_result(Username, Zone) ->
case emqx_zone:get_env(Zone, allow_anonymous, false) of IsAnonymous = (Username =:= undefined orelse Username =:= <<>>),
true -> #{auth_result => success, anonymous => true}; AllowAnonymous = emqx_zone:get_env(Zone, allow_anonymous, false),
false -> #{auth_result => not_authorized, anonymous => false} Bypass = emqx_zone:get_env(Zone, bypass_auth_plugins, false),
%% the `anonymous` field in auth result does not mean the client is
%% connected without username, but if the auth result is based on
%% allowing anonymous access.
IsResultBasedOnAllowAnonymous =
case AllowAnonymous of
true -> true;
_ -> false
end,
Result = case AllowAnonymous of
true -> #{auth_result => success, anonymous => IsResultBasedOnAllowAnonymous};
_ -> #{auth_result => not_authorized, anonymous => IsResultBasedOnAllowAnonymous}
end,
case {IsAnonymous, AllowAnonymous} of
{true, false_quick_deny} ->
{stop, Result};
_ when Bypass ->
{stop, Result};
_ ->
{continue, Result}
end. end.
-compile({inline, [return_auth_result/1]}). -compile({inline, [return_auth_result/1]}).

View File

@ -38,6 +38,12 @@ t_authenticate(_) ->
emqx_zone:set_env(zone, allow_anonymous, true), emqx_zone:set_env(zone, allow_anonymous, true),
?assertMatch({ok, _}, emqx_access_control:authenticate(clientinfo())). ?assertMatch({ok, _}, emqx_access_control:authenticate(clientinfo())).
t_authenticate_fast_fail(_) ->
emqx_zone:set_env(zone, allow_anonymous, false_quick_deny),
?assertMatch({error, _}, emqx_access_control:authenticate(clientinfo())),
emqx_zone:set_env(zone, allow_anonymous, true),
?assertMatch({ok, _}, emqx_access_control:authenticate(clientinfo())).
t_check_acl(_) -> t_check_acl(_) ->
emqx_zone:set_env(zone, acl_nomatch, deny), emqx_zone:set_env(zone, acl_nomatch, deny),
application:set_env(emqx, enable_acl_cache, false), application:set_env(emqx, enable_acl_cache, false),