feat: add authentication_failure throttled log

also included changes:

1. add 'username' to log metadata
2. add 'tag' to authz warning logs
This commit is contained in:
zmstone 2024-03-20 11:10:13 +01:00
parent 611e4f6710
commit 2c81b56377
5 changed files with 46 additions and 17 deletions

View File

@ -154,7 +154,7 @@ do_authorize(ClientInfo, Action, Topic) ->
case run_hooks('client.authorize', [ClientInfo, Action, Topic], Default) of case run_hooks('client.authorize', [ClientInfo, Action, Topic], Default) of
AuthzResult = #{result := Result} when Result == allow; Result == deny -> AuthzResult = #{result := Result} when Result == allow; Result == deny ->
From = maps:get(from, AuthzResult, unknown), From = maps:get(from, AuthzResult, unknown),
ok = log_result(ClientInfo, Topic, Action, From, Result), ok = log_result(Topic, Action, From, Result),
emqx_hooks:run( emqx_hooks:run(
'client.check_authz_complete', 'client.check_authz_complete',
[ClientInfo, Action, Topic, Result, From] [ClientInfo, Action, Topic, Result, From]
@ -173,24 +173,28 @@ do_authorize(ClientInfo, Action, Topic) ->
deny deny
end. end.
log_result(#{username := Username}, Topic, Action, From, Result) -> log_result(Topic, Action, From, Result) ->
LogMeta = fun() -> LogMeta = fun() ->
#{ #{
username => Username,
topic => Topic, topic => Topic,
action => format_action(Action), action => format_action(Action),
source => format_from(From) source => format_from(From)
} }
end, end,
case Result of do_log_result(Action, Result, LogMeta).
allow ->
?SLOG(info, (LogMeta())#{msg => "authorization_permission_allowed"}); do_log_result(_Action, allow, LogMeta) ->
deny -> ?SLOG(info, (LogMeta())#{msg => "authorization_permission_allowed"}, #{tag => "AUTHZ"});
?SLOG_THROTTLE( do_log_result(?AUTHZ_PUBLISH_MATCH_MAP(_, _), deny, LogMeta) ->
warning, %% for publish action, we do not log permission deny at warning level here
(LogMeta())#{msg => authorization_permission_denied} %% because it will be logged as cannot_publish_to_topic_due_to_not_authorized
) ?SLOG(info, (LogMeta())#{msg => "authorization_permission_denied"}, #{tag => "AUTHZ"});
end. do_log_result(_, deny, LogMeta) ->
?SLOG_THROTTLE(
warning,
(LogMeta())#{msg => authorization_permission_denied},
#{tag => "AUTHZ"}
).
%% @private Format authorization rules source. %% @private Format authorization rules source.
format_from(default) -> format_from(default) ->

View File

@ -633,7 +633,7 @@ process_publish(Packet = ?PUBLISH_PACKET(QoS, Topic, PacketId), Channel) ->
msg => cannot_publish_to_topic_due_to_not_authorized, msg => cannot_publish_to_topic_due_to_not_authorized,
reason => emqx_reason_codes:name(Rc) reason => emqx_reason_codes:name(Rc)
}, },
#{topic => Topic} #{topic => Topic, tag => "AUTHZ"}
), ),
case emqx:get_config([authorization, deny_action], ignore) of case emqx:get_config([authorization, deny_action], ignore) of
ignore -> ignore ->
@ -652,7 +652,7 @@ process_publish(Packet = ?PUBLISH_PACKET(QoS, Topic, PacketId), Channel) ->
msg => cannot_publish_to_topic_due_to_quota_exceeded, msg => cannot_publish_to_topic_due_to_quota_exceeded,
reason => emqx_reason_codes:name(Rc) reason => emqx_reason_codes:name(Rc)
}, },
#{topic => Topic} #{topic => Topic, tag => "AUTHZ"}
), ),
case QoS of case QoS of
?QOS_0 -> ?QOS_0 ->
@ -1612,8 +1612,10 @@ fix_mountpoint(_ConnPkt, ClientInfo = #{mountpoint := MountPoint}) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Set log metadata %% Set log metadata
set_log_meta(_ConnPkt, #channel{clientinfo = #{clientid := ClientId}}) -> set_log_meta(_ConnPkt, #channel{clientinfo = #{clientid := ClientId} = ClientInfo}) ->
emqx_logger:set_metadata_clientid(ClientId). Username = maps:get(username, ClientInfo, undefined),
emqx_logger:set_metadata_clientid(ClientId),
emqx_logger:set_metadata_username(Username).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Check banned %% Check banned
@ -1680,6 +1682,7 @@ authenticate(
Channel Channel
); );
_ -> _ ->
log_auth_failure("bad_authentication_method"),
{error, ?RC_BAD_AUTHENTICATION_METHOD} {error, ?RC_BAD_AUTHENTICATION_METHOD}
end. end.
@ -1706,6 +1709,7 @@ do_authenticate(
auth_cache = AuthCache auth_cache = AuthCache
}}; }};
{error, Reason} -> {error, Reason} ->
log_auth_failure(Reason),
{error, emqx_reason_codes:connack_error(Reason)} {error, emqx_reason_codes:connack_error(Reason)}
end; end;
do_authenticate(Credential, #channel{clientinfo = ClientInfo} = Channel) -> do_authenticate(Credential, #channel{clientinfo = ClientInfo} = Channel) ->
@ -1713,9 +1717,20 @@ do_authenticate(Credential, #channel{clientinfo = ClientInfo} = Channel) ->
{ok, AuthResult} -> {ok, AuthResult} ->
{ok, #{}, Channel#channel{clientinfo = merge_auth_result(ClientInfo, AuthResult)}}; {ok, #{}, Channel#channel{clientinfo = merge_auth_result(ClientInfo, AuthResult)}};
{error, Reason} -> {error, Reason} ->
log_auth_failure(Reason),
{error, emqx_reason_codes:connack_error(Reason)} {error, emqx_reason_codes:connack_error(Reason)}
end. end.
log_auth_failure(Reason) ->
?SLOG_THROTTLE(
warning,
#{
msg => authentication_failure,
reason => Reason
},
#{tag => "AUTHN"}
).
merge_auth_result(ClientInfo, AuthResult) when is_map(ClientInfo) andalso is_map(AuthResult) -> merge_auth_result(ClientInfo, AuthResult) when is_map(ClientInfo) andalso is_map(AuthResult) ->
IsSuperuser = maps:get(is_superuser, AuthResult, false), IsSuperuser = maps:get(is_superuser, AuthResult, false),
maps:merge(ClientInfo, AuthResult#{is_superuser => IsSuperuser}). maps:merge(ClientInfo, AuthResult#{is_superuser => IsSuperuser}).

View File

@ -43,6 +43,7 @@
-export([ -export([
set_metadata_peername/1, set_metadata_peername/1,
set_metadata_clientid/1, set_metadata_clientid/1,
set_metadata_username/1,
set_proc_metadata/1, set_proc_metadata/1,
set_primary_log_level/1, set_primary_log_level/1,
set_log_handler_level/2, set_log_handler_level/2,
@ -142,6 +143,12 @@ set_metadata_clientid(<<>>) ->
set_metadata_clientid(ClientId) -> set_metadata_clientid(ClientId) ->
set_proc_metadata(#{clientid => ClientId}). set_proc_metadata(#{clientid => ClientId}).
-spec set_metadata_username(emqx_types:username()) -> ok.
set_metadata_username(Username) when Username =:= undefined orelse Username =:= <<>> ->
ok;
set_metadata_username(Username) ->
set_proc_metadata(#{username => Username}).
-spec set_metadata_peername(peername_str()) -> ok. -spec set_metadata_peername(peername_str()) -> ok.
set_metadata_peername(Peername) -> set_metadata_peername(Peername) ->
set_proc_metadata(#{peername => Peername}). set_proc_metadata(#{peername => Peername}).

View File

@ -69,7 +69,9 @@ enrich_report(ReportRaw, Meta) ->
ClientId = maps:get(clientid, Meta, undefined), ClientId = maps:get(clientid, Meta, undefined),
Peer = maps:get(peername, Meta, undefined), Peer = maps:get(peername, Meta, undefined),
Msg = maps:get(msg, ReportRaw, undefined), Msg = maps:get(msg, ReportRaw, undefined),
Tag = maps:get(tag, ReportRaw, undefined), %% TODO: move all tags to Meta so we can filter traces
%% based on tags (currently not supported)
Tag = maps:get(tag, ReportRaw, maps:get(tag, Meta, undefined)),
%% turn it into a list so that the order of the fields is determined %% turn it into a list so that the order of the fields is determined
lists:foldl( lists:foldl(
fun fun

View File

@ -78,6 +78,7 @@
-define(DEFAULT_MAX_PORTS, 1024 * 1024). -define(DEFAULT_MAX_PORTS, 1024 * 1024).
-define(LOG_THROTTLING_MSGS, [ -define(LOG_THROTTLING_MSGS, [
authentication_failure,
authorization_permission_denied, authorization_permission_denied,
cannot_publish_to_topic_due_to_not_authorized, cannot_publish_to_topic_due_to_not_authorized,
cannot_publish_to_topic_due_to_quota_exceeded, cannot_publish_to_topic_due_to_quota_exceeded,