Skip to content

Commit 9e84378

Browse files
committed
Close MQTT connection with delay when authentication fails
For consistency with other protocols (to protect from potential DoS attacks). Wrong credentials and virtual host access errors trigger the delay. References #11831 We keep the delay low when running tests. Otherwise, ``` make -C deps/rabbitmq_mqtt ct-auth ``` would run 3 minutes longer (with a SILENT_CLOSE_DELAY of 3 seconds). (cherry picked from commit 80ff6d0)
1 parent c143ddf commit 9e84378

File tree

1 file changed

+17
-10
lines changed

1 file changed

+17
-10
lines changed

deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@
4242
-define(QUEUE_TTL_KEY, <<"x-expires">>).
4343
-define(DEFAULT_EXCHANGE_NAME, <<>>).
4444

45+
-ifdef(TEST).
46+
-define(SILENT_CLOSE_DELAY, 10).
47+
-else.
48+
-define(SILENT_CLOSE_DELAY, 3_000).
49+
-endif.
50+
4551
-type send_fun() :: fun((iodata()) -> ok).
4652
-type session_expiry_interval() :: non_neg_integer() | infinity.
4753
-type subscriptions() :: #{topic_filter() => #mqtt_subscription_opts{}}.
@@ -621,16 +627,16 @@ check_extended_auth(_) ->
621627
check_credentials(Username, Password, SslLoginName, PeerIp) ->
622628
case creds(Username, Password, SslLoginName) of
623629
nocreds ->
624-
auth_attempt_failed(PeerIp, <<>>),
625630
?LOG_ERROR("MQTT login failed: no credentials provided"),
631+
auth_attempt_failed(PeerIp, <<>>),
626632
{error, ?RC_BAD_USER_NAME_OR_PASSWORD};
627633
{invalid_creds, {undefined, Pass}} when is_binary(Pass) ->
628-
auth_attempt_failed(PeerIp, <<>>),
629634
?LOG_ERROR("MQTT login failed: no username is provided"),
635+
auth_attempt_failed(PeerIp, <<>>),
630636
{error, ?RC_BAD_USER_NAME_OR_PASSWORD};
631637
{invalid_creds, {User, _Pass}} when is_binary(User) ->
632-
auth_attempt_failed(PeerIp, User),
633638
?LOG_ERROR("MQTT login failed for user '~s': no password provided", [User]),
639+
auth_attempt_failed(PeerIp, User),
634640
{error, ?RC_BAD_USER_NAME_OR_PASSWORD};
635641
{UserBin, PassBin} ->
636642
{ok, {UserBin, PassBin}}
@@ -998,8 +1004,8 @@ check_vhost_exists(VHost, Username, PeerIp) ->
9981004
true ->
9991005
ok;
10001006
false ->
1001-
auth_attempt_failed(PeerIp, Username),
10021007
?LOG_ERROR("MQTT connection failed: virtual host '~s' does not exist", [VHost]),
1008+
auth_attempt_failed(PeerIp, Username),
10031009
{error, ?RC_BAD_USER_NAME_OR_PASSWORD}
10041010
end.
10051011

@@ -1038,10 +1044,10 @@ check_user_login(VHost, Username, Password, ClientId, PeerIp, ConnName) ->
10381044
notify_auth_result(user_authentication_success, Username1, ConnName),
10391045
{ok, User};
10401046
{refused, Username, Msg, Args} ->
1041-
auth_attempt_failed(PeerIp, Username),
10421047
?LOG_ERROR("MQTT connection failed: access refused for user '~s':" ++ Msg,
10431048
[Username | Args]),
10441049
notify_auth_result(user_authentication_failure, Username, ConnName),
1050+
auth_attempt_failed(PeerIp, Username),
10451051
{error, ?RC_BAD_USER_NAME_OR_PASSWORD}
10461052
end.
10471053

@@ -1070,9 +1076,9 @@ check_vhost_access(VHost, User = #user{username = Username}, ClientId, PeerIp) -
10701076
ok ->
10711077
{ok, AuthzCtx}
10721078
catch exit:#amqp_error{name = not_allowed} ->
1073-
auth_attempt_failed(PeerIp, Username),
10741079
?LOG_ERROR("MQTT connection failed: access refused for user '~s' to vhost '~s'",
10751080
[Username, VHost]),
1081+
auth_attempt_failed(PeerIp, Username),
10761082
{error, ?RC_NOT_AUTHORIZED}
10771083
end.
10781084

@@ -1081,9 +1087,9 @@ check_user_loopback(Username, PeerIp) ->
10811087
ok ->
10821088
ok;
10831089
not_allowed ->
1090+
?LOG_WARNING("MQTT login failed: user '~s' can only connect via localhost",
1091+
[Username]),
10841092
auth_attempt_failed(PeerIp, Username),
1085-
?LOG_WARNING(
1086-
"MQTT login failed: user '~s' can only connect via localhost", [Username]),
10871093
{error, ?RC_NOT_AUTHORIZED}
10881094
end.
10891095

@@ -1102,8 +1108,8 @@ ensure_credential_expiry_timer(User = #user{username = Username}, PeerIp) ->
11021108
_TimerRef = erlang:send_after(Time, self(), credential_expired),
11031109
ok;
11041110
false ->
1105-
auth_attempt_failed(PeerIp, Username),
11061111
?LOG_WARNING("Credential expired ~b ms ago", [abs(Time)]),
1112+
auth_attempt_failed(PeerIp, Username),
11071113
{error, ?RC_NOT_AUTHORIZED}
11081114
end
11091115
end.
@@ -1222,7 +1228,8 @@ creds(User, Pass, SSLLoginName) ->
12221228

12231229
-spec auth_attempt_failed(inet:ip_address(), binary()) -> ok.
12241230
auth_attempt_failed(PeerIp, Username) ->
1225-
rabbit_core_metrics:auth_attempt_failed(PeerIp, Username, mqtt).
1231+
rabbit_core_metrics:auth_attempt_failed(PeerIp, Username, mqtt),
1232+
timer:sleep(?SILENT_CLOSE_DELAY).
12261233

12271234
maybe_downgrade_qos(?QOS_0) -> ?QOS_0;
12281235
maybe_downgrade_qos(?QOS_1) -> ?QOS_1;

0 commit comments

Comments
 (0)