Skip to content

Commit 91c2d15

Browse files
Merge pull request #12697 from rabbitmq/mergify/bp/v4.0.x/pr-12696
HTTP API: reduce body size limit for the endpoint used to bind queues/streams/exchanges (backport #12696)
2 parents 073c993 + a66c926 commit 91c2d15

File tree

3 files changed

+83
-44
lines changed

3 files changed

+83
-44
lines changed

deps/rabbitmq_management/src/rabbit_mgmt_util.erl

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
list_login_vhosts_names/2]).
3131
-export([filter_tracked_conn_list/3]).
3232
-export([with_decode/5, decode/1, decode/2, set_resp_header/3,
33-
args/1, read_complete_body/1]).
33+
args/1, read_complete_body/1, read_complete_body_with_limit/2]).
3434
-export([reply_list/3, reply_list/5, reply_list/4,
3535
sort_list/2, destination_type/1, reply_list_or_paginate/3
3636
]).
@@ -703,26 +703,56 @@ halt_response(Code, Type, Reason, ReqData, Context) ->
703703
id(Key, ReqData) ->
704704
rabbit_web_dispatch_access_control:id(Key, ReqData).
705705

706+
%% IMPORTANT:
707+
%% Prefer read_complete_body_with_limit/2 with an explicit limit to make it easier
708+
%% to reason about what limit will be used.
706709
read_complete_body(Req) ->
707710
read_complete_body(Req, <<"">>).
708711
read_complete_body(Req, Acc) ->
709712
BodySizeLimit = application:get_env(rabbitmq_management, max_http_body_size, ?MANAGEMENT_DEFAULT_HTTP_MAX_BODY_SIZE),
710713
read_complete_body(Req, Acc, BodySizeLimit).
711714
read_complete_body(Req0, Acc, BodySizeLimit) ->
712-
case bit_size(Acc) > BodySizeLimit of
715+
N = byte_size(Acc),
716+
case N > BodySizeLimit of
713717
true ->
714-
{error, "Exceeded HTTP request body size limit"};
718+
{error, http_body_limit_exceeded, BodySizeLimit, N};
715719
false ->
716720
case cowboy_req:read_body(Req0) of
717721
{ok, Data, Req} -> {ok, <<Acc/binary, Data/binary>>, Req};
718722
{more, Data, Req} -> read_complete_body(Req, <<Acc/binary, Data/binary>>)
719723
end
720724
end.
721725

726+
read_complete_body_with_limit(Req, BodySizeLimit) when is_integer(BodySizeLimit) ->
727+
case cowboy_req:body_length(Req) of
728+
N when is_integer(N) ->
729+
case N > BodySizeLimit of
730+
true ->
731+
{error, http_body_limit_exceeded, BodySizeLimit, N};
732+
false ->
733+
do_read_complete_body_with_limit(Req, <<"">>, BodySizeLimit)
734+
end;
735+
undefined ->
736+
do_read_complete_body_with_limit(Req, <<"">>, BodySizeLimit)
737+
end.
738+
739+
do_read_complete_body_with_limit(Req0, Acc, BodySizeLimit) ->
740+
N = byte_size(Acc),
741+
case N > BodySizeLimit of
742+
true ->
743+
{error, http_body_limit_exceeded, BodySizeLimit, N};
744+
false ->
745+
case cowboy_req:read_body(Req0, #{length => BodySizeLimit, period => 30000}) of
746+
{ok, Data, Req} -> {ok, <<Acc/binary, Data/binary>>, Req};
747+
{more, Data, Req} -> do_read_complete_body_with_limit(Req, <<Acc/binary, Data/binary>>, BodySizeLimit)
748+
end
749+
end.
750+
722751
with_decode(Keys, ReqData, Context, Fun) ->
723752
case read_complete_body(ReqData) of
724-
{error, Reason} ->
725-
bad_request(Reason, ReqData, Context);
753+
{error, http_body_limit_exceeded, LimitApplied, BytesRead} ->
754+
rabbit_log:warning("HTTP API: request exceeded maximum allowed payload size (limit: ~tp bytes, payload size: ~tp bytes)", [LimitApplied, BytesRead]),
755+
bad_request("Exceeded HTTP request body size limit", ReqData, Context);
726756
{ok, Body, ReqData1} ->
727757
with_decode(Keys, Body, ReqData1, Context, Fun)
728758
end.

deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
1717
-include_lib("amqp_client/include/amqp_client.hrl").
1818

19+
%% Use a much lower limit for creating bindings over the HTTP API.
20+
%% The payload is not meant to be even 50 KiB in size.
21+
-define(HTTP_BODY_SIZE_LIMIT, 5000).
22+
1923
%%--------------------------------------------------------------------
2024

2125
init(Req, [Mode]) ->
@@ -64,39 +68,44 @@ to_json(ReqData, {Mode, Context}) ->
6468
ReqData, {Mode, Context}).
6569

6670
accept_content(ReqData0, {_Mode, Context}) ->
67-
{ok, Body, ReqData} = rabbit_mgmt_util:read_complete_body(ReqData0),
68-
Source = rabbit_mgmt_util:id(source, ReqData),
69-
Dest = rabbit_mgmt_util:id(destination, ReqData),
70-
DestType = rabbit_mgmt_util:id(dtype, ReqData),
71-
VHost = rabbit_mgmt_util:vhost(ReqData),
72-
{ok, Props} = rabbit_mgmt_util:decode(Body),
73-
MethodName = case rabbit_mgmt_util:destination_type(ReqData) of
74-
exchange -> 'exchange.bind';
75-
queue -> 'queue.bind'
76-
end,
77-
{Key, Args} = key_args(DestType, Props),
78-
case rabbit_mgmt_util:direct_request(
79-
MethodName,
80-
fun rabbit_mgmt_format:format_accept_content/1,
81-
[{queue, Dest},
82-
{exchange, Source},
83-
{destination, Dest},
84-
{source, Source},
85-
{routing_key, Key},
86-
{arguments, Args}],
87-
"Binding error: ~ts", ReqData, Context) of
88-
{stop, _, _} = Res ->
89-
Res;
90-
{true, ReqData, Context2} ->
91-
From = binary_to_list(cowboy_req:path(ReqData)),
92-
Prefix = rabbit_mgmt_util:get_path_prefix(),
93-
BindingProps = rabbit_mgmt_format:pack_binding_props(Key, Args),
94-
UrlWithBindings = rabbit_mgmt_format:url("/api/bindings/~ts/e/~ts/~ts/~ts/~ts",
95-
[VHost, Source, DestType,
96-
Dest, BindingProps]),
97-
To = Prefix ++ binary_to_list(UrlWithBindings),
98-
Loc = rabbit_web_dispatch_util:relativise(From, To),
99-
{{true, Loc}, ReqData, Context2}
71+
case rabbit_mgmt_util:read_complete_body_with_limit(ReqData0, ?HTTP_BODY_SIZE_LIMIT) of
72+
{ok, Body, ReqData} ->
73+
Source = rabbit_mgmt_util:id(source, ReqData),
74+
Dest = rabbit_mgmt_util:id(destination, ReqData),
75+
DestType = rabbit_mgmt_util:id(dtype, ReqData),
76+
VHost = rabbit_mgmt_util:vhost(ReqData),
77+
{ok, Props} = rabbit_mgmt_util:decode(Body),
78+
MethodName = case rabbit_mgmt_util:destination_type(ReqData) of
79+
exchange -> 'exchange.bind';
80+
queue -> 'queue.bind'
81+
end,
82+
{Key, Args} = key_args(DestType, Props),
83+
case rabbit_mgmt_util:direct_request(
84+
MethodName,
85+
fun rabbit_mgmt_format:format_accept_content/1,
86+
[{queue, Dest},
87+
{exchange, Source},
88+
{destination, Dest},
89+
{source, Source},
90+
{routing_key, Key},
91+
{arguments, Args}],
92+
"Binding error: ~ts", ReqData, Context) of
93+
{stop, _, _} = Res ->
94+
Res;
95+
{true, ReqData, Context2} ->
96+
From = binary_to_list(cowboy_req:path(ReqData)),
97+
Prefix = rabbit_mgmt_util:get_path_prefix(),
98+
BindingProps = rabbit_mgmt_format:pack_binding_props(Key, Args),
99+
UrlWithBindings = rabbit_mgmt_format:url("/api/bindings/~ts/e/~ts/~ts/~ts/~ts",
100+
[VHost, Source, DestType,
101+
Dest, BindingProps]),
102+
To = Prefix ++ binary_to_list(UrlWithBindings),
103+
Loc = rabbit_web_dispatch_util:relativise(From, To),
104+
{{true, Loc}, ReqData, Context2}
105+
end;
106+
{error, http_body_limit_exceeded, LimitApplied, BytesRead} ->
107+
rabbit_log:warning("HTTP API: binding creation request exceeded maximum allowed payload size (limit: ~tp bytes, payload size: ~tp bytes)", [LimitApplied, BytesRead]),
108+
rabbit_mgmt_util:bad_request("Payload size limit exceeded", ReqData0, Context)
100109
end.
101110

102111
is_authorized(ReqData, {Mode, Context}) ->

deps/rabbitmq_management/src/rabbit_mgmt_wm_definitions.erl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ all_definitions(ReqData, Context) ->
8484
Context).
8585

8686
accept_json(ReqData0, Context) ->
87-
case rabbit_mgmt_util:read_complete_body(ReqData0) of
88-
{error, Reason} ->
89-
BodySizeLimit = application:get_env(rabbitmq_management, max_http_body_size, ?MANAGEMENT_DEFAULT_HTTP_MAX_BODY_SIZE),
90-
_ = rabbit_log:warning("HTTP API: uploaded definition file exceeded the maximum request body limit of ~p bytes. "
91-
"Use the 'management.http.max_body_size' key in rabbitmq.conf to increase the limit if necessary", [BodySizeLimit]),
92-
rabbit_mgmt_util:bad_request(Reason, ReqData0, Context);
87+
BodySizeLimit = application:get_env(rabbitmq_management, max_http_body_size, ?MANAGEMENT_DEFAULT_HTTP_MAX_BODY_SIZE),
88+
case rabbit_mgmt_util:read_complete_body_with_limit(ReqData0, BodySizeLimit) of
89+
{error, http_body_limit_exceeded, LimitApplied, BytesRead} ->
90+
_ = rabbit_log:warning("HTTP API: uploaded definition file size (~tp) exceeded the maximum request body limit of ~tp bytes. "
91+
"Use the 'management.http.max_body_size' key in rabbitmq.conf to increase the limit if necessary", [BytesRead, LimitApplied]),
92+
rabbit_mgmt_util:bad_request("Exceeded HTTP request body size limit", ReqData0, Context);
9393
{ok, Body, ReqData} ->
9494
accept(Body, ReqData, Context)
9595
end.

0 commit comments

Comments
 (0)