Skip to content

Commit 0369841

Browse files
Merge pull request #16226 from rabbitmq/rabbitmq-server-16222
`rabbitmq.conf`: make cipher suites configurable for TLS clients in three more places (plugins)
2 parents d0fa76d + cf8f50e commit 0369841

8 files changed

Lines changed: 322 additions & 3 deletions

File tree

deps/rabbitmq_auth_backend_http/priv/schema/rabbitmq_auth_backend_http.schema

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,15 @@ fun(Conf) ->
130130
[ V || {_, V} <- Settings ]
131131
end}.
132132

133+
{mapping, "auth_http.ssl_options.ciphers.$cipher", "rabbitmq_auth_backend_http.ssl_options.ciphers",
134+
[{datatype, string}]}.
135+
136+
{translation, "rabbitmq_auth_backend_http.ssl_options.ciphers",
137+
fun(Conf) ->
138+
Settings = cuttlefish_variable:filter_by_prefix("auth_http.ssl_options.ciphers", Conf),
139+
lists:reverse([V || {_, V} <- Settings])
140+
end}.
141+
133142
{mapping, "auth_http.ssl_options.sni", "rabbitmq_auth_backend_http.ssl_options.server_name_indication",
134143
[{datatype, [{enum, [none]}, string]}]}.
135144

deps/rabbitmq_auth_backend_http/test/config_schema_SUITE_data/rabbitmq_auth_backend_http.snippets

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,5 +203,35 @@
203203
{versions,['tlsv1.2','tlsv1.1']}
204204
]}
205205
]}],
206+
[]},
207+
{ssl_options_ciphers,
208+
"auth_http.ssl_options.cacertfile = test/config_schema_SUITE_data/certs/ca_certificate.pem
209+
auth_http.ssl_options.certfile = test/config_schema_SUITE_data/certs/server_certificate.pem
210+
auth_http.ssl_options.keyfile = test/config_schema_SUITE_data/certs/server_key.pem
211+
auth_http.ssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384
212+
auth_http.ssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384
213+
auth_http.ssl_options.ciphers.3 = ECDHE-ECDSA-AES256-SHA384
214+
auth_http.ssl_options.ciphers.4 = ECDHE-RSA-AES256-SHA384
215+
auth_http.ssl_options.ciphers.5 = ECDH-ECDSA-AES256-GCM-SHA384
216+
auth_http.ssl_options.ciphers.6 = ECDH-RSA-AES256-GCM-SHA384
217+
auth_http.ssl_options.ciphers.7 = ECDH-ECDSA-AES256-SHA384
218+
auth_http.ssl_options.ciphers.8 = ECDH-RSA-AES256-SHA384
219+
auth_http.ssl_options.ciphers.9 = DHE-RSA-AES256-GCM-SHA384",
220+
[],
221+
[{rabbitmq_auth_backend_http,
222+
[{ssl_options,
223+
[{cacertfile,"test/config_schema_SUITE_data/certs/ca_certificate.pem"},
224+
{certfile,"test/config_schema_SUITE_data/certs/server_certificate.pem"},
225+
{keyfile,"test/config_schema_SUITE_data/certs/server_key.pem"},
226+
{ciphers,
227+
["ECDHE-ECDSA-AES256-GCM-SHA384",
228+
"ECDHE-RSA-AES256-GCM-SHA384",
229+
"ECDHE-ECDSA-AES256-SHA384",
230+
"ECDHE-RSA-AES256-SHA384",
231+
"ECDH-ECDSA-AES256-GCM-SHA384",
232+
"ECDH-RSA-AES256-GCM-SHA384",
233+
"ECDH-ECDSA-AES256-SHA384",
234+
"ECDH-RSA-AES256-SHA384",
235+
"DHE-RSA-AES256-GCM-SHA384"]}]}]}],
206236
[]}
207237
].

deps/rabbitmq_auth_backend_ldap/priv/schema/rabbitmq_auth_backend_ldap.schema

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,15 @@ fun(Conf) ->
310310
[ V || {_, V} <- Settings ]
311311
end}.
312312

313+
{mapping, "auth_ldap.ssl_options.ciphers.$cipher", "rabbitmq_auth_backend_ldap.ssl_options.ciphers",
314+
[{datatype, string}]}.
315+
316+
{translation, "rabbitmq_auth_backend_ldap.ssl_options.ciphers",
317+
fun(Conf) ->
318+
Settings = cuttlefish_variable:filter_by_prefix("auth_ldap.ssl_options.ciphers", Conf),
319+
lists:reverse([V || {_, V} <- Settings])
320+
end}.
321+
313322
{mapping, "auth_ldap.ssl_options.sni", "rabbitmq_auth_backend_ldap.ssl_options.server_name_indication",
314323
[{datatype, [{enum, [none]}, string]}]}.
315324

deps/rabbitmq_auth_backend_ldap/test/config_schema_SUITE_data/rabbitmq_auth_backend_ldap.snippets

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,39 @@
339339
{use_ssl, true}]}],
340340
[]},
341341

342+
{ssl_options_ciphers,
343+
"auth_ldap.use_ssl = true
344+
auth_ldap.ssl_options.cacertfile = test/config_schema_SUITE_data/certs/ca_certificate.pem
345+
auth_ldap.ssl_options.certfile = test/config_schema_SUITE_data/certs/server_certificate.pem
346+
auth_ldap.ssl_options.keyfile = test/config_schema_SUITE_data/certs/server_key.pem
347+
auth_ldap.ssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384
348+
auth_ldap.ssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384
349+
auth_ldap.ssl_options.ciphers.3 = ECDHE-ECDSA-AES256-SHA384
350+
auth_ldap.ssl_options.ciphers.4 = ECDHE-RSA-AES256-SHA384
351+
auth_ldap.ssl_options.ciphers.5 = ECDH-ECDSA-AES256-GCM-SHA384
352+
auth_ldap.ssl_options.ciphers.6 = ECDH-RSA-AES256-GCM-SHA384
353+
auth_ldap.ssl_options.ciphers.7 = ECDH-ECDSA-AES256-SHA384
354+
auth_ldap.ssl_options.ciphers.8 = ECDH-RSA-AES256-SHA384
355+
auth_ldap.ssl_options.ciphers.9 = DHE-RSA-AES256-GCM-SHA384",
356+
[],
357+
[{rabbitmq_auth_backend_ldap,
358+
[{ssl_options,
359+
[{cacertfile,"test/config_schema_SUITE_data/certs/ca_certificate.pem"},
360+
{certfile,"test/config_schema_SUITE_data/certs/server_certificate.pem"},
361+
{keyfile,"test/config_schema_SUITE_data/certs/server_key.pem"},
362+
{ciphers,
363+
["ECDHE-ECDSA-AES256-GCM-SHA384",
364+
"ECDHE-RSA-AES256-GCM-SHA384",
365+
"ECDHE-ECDSA-AES256-SHA384",
366+
"ECDHE-RSA-AES256-SHA384",
367+
"ECDH-ECDSA-AES256-GCM-SHA384",
368+
"ECDH-RSA-AES256-GCM-SHA384",
369+
"ECDH-ECDSA-AES256-SHA384",
370+
"ECDH-RSA-AES256-SHA384",
371+
"DHE-RSA-AES256-GCM-SHA384"]}]},
372+
{use_ssl, true}]}],
373+
[]},
374+
342375
{vhost_access_query,
343376
"auth_ldap.queries.vhost_access = '''
344377
{in_group,\"ou=${vhost}-users,ou=vhosts,dc=example,dc=com\"}

deps/rabbitmq_trust_store/priv/schema/rabbitmq_trust_store.schema

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,12 @@ fun(Conf) ->
147147
Settings = cuttlefish_variable:filter_by_prefix("trust_store.ssl_options.versions", Conf),
148148
[ V || {_, V} <- Settings ]
149149
end}.
150+
151+
{mapping, "trust_store.ssl_options.ciphers.$cipher", "rabbitmq_trust_store.ssl_options.ciphers",
152+
[{datatype, string}]}.
153+
154+
{translation, "rabbitmq_trust_store.ssl_options.ciphers",
155+
fun(Conf) ->
156+
Settings = cuttlefish_variable:filter_by_prefix("trust_store.ssl_options.ciphers", Conf),
157+
lists:reverse([V || {_, V} <- Settings])
158+
end}.

deps/rabbitmq_trust_store/src/rabbit_trust_store_http_provider.erl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
-define(PROFILE, ?MODULE).
1616

17-
-export([list_certs/1, list_certs/2, load_cert/3]).
17+
-export([list_certs/1, list_certs/2, load_cert/3, prepare_ssl_opts/1]).
1818

1919
-record(http_state,{
2020
url :: string(),
@@ -80,10 +80,14 @@ init_state(Config) ->
8080
HttpOptions0 = [{timeout, Timeout}, {connect_timeout, Timeout}],
8181
HttpOptions = case proplists:get_value(ssl_options, Config) of
8282
undefined -> HttpOptions0;
83-
SslOpts -> [{ssl, SslOpts} | HttpOptions0]
83+
SslOpts -> [{ssl, prepare_ssl_opts(SslOpts)} | HttpOptions0]
8484
end,
8585
#http_state{url = Url, http_options = HttpOptions, headers = [{"connection", "close"} | Headers]}.
8686

87+
-spec prepare_ssl_opts(proplists:proplist()) -> proplists:proplist().
88+
prepare_ssl_opts(SslOpts) ->
89+
rabbit_ssl_options:fix_client(SslOpts).
90+
8791
https_request_timeout() ->
8892
application:get_env(rabbitmq_trust_store, https_request_timeout, 20000).
8993

deps/rabbitmq_trust_store/test/config_schema_SUITE_data/rabbitmq_trust_store.snippets

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,37 @@
2929
{ssl_options,
3030
[{certfile,"test/config_schema_SUITE_data/certs/server_certificate.pem"},
3131
{password,<<"i_am_password">>}]}]}],
32-
[rabbitmq_trust_store]}].
32+
[rabbitmq_trust_store]},
33+
{ssl_options_ciphers,
34+
"trust_store.providers.1 = http
35+
trust_store.url = https://example.com
36+
trust_store.ssl_options.cacertfile = test/config_schema_SUITE_data/certs/ca_certificate.pem
37+
trust_store.ssl_options.certfile = test/config_schema_SUITE_data/certs/server_certificate.pem
38+
trust_store.ssl_options.keyfile = test/config_schema_SUITE_data/certs/server_key.pem
39+
trust_store.ssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384
40+
trust_store.ssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384
41+
trust_store.ssl_options.ciphers.3 = ECDHE-ECDSA-AES256-SHA384
42+
trust_store.ssl_options.ciphers.4 = ECDHE-RSA-AES256-SHA384
43+
trust_store.ssl_options.ciphers.5 = ECDH-ECDSA-AES256-GCM-SHA384
44+
trust_store.ssl_options.ciphers.6 = ECDH-RSA-AES256-GCM-SHA384
45+
trust_store.ssl_options.ciphers.7 = ECDH-ECDSA-AES256-SHA384
46+
trust_store.ssl_options.ciphers.8 = ECDH-RSA-AES256-SHA384
47+
trust_store.ssl_options.ciphers.9 = DHE-RSA-AES256-GCM-SHA384",
48+
[{rabbitmq_trust_store,
49+
[{providers,[rabbit_trust_store_http_provider]},
50+
{url,"https://example.com"},
51+
{ssl_options,
52+
[{cacertfile,"test/config_schema_SUITE_data/certs/ca_certificate.pem"},
53+
{certfile,"test/config_schema_SUITE_data/certs/server_certificate.pem"},
54+
{keyfile,"test/config_schema_SUITE_data/certs/server_key.pem"},
55+
{ciphers,
56+
["ECDHE-ECDSA-AES256-GCM-SHA384",
57+
"ECDHE-RSA-AES256-GCM-SHA384",
58+
"ECDHE-ECDSA-AES256-SHA384",
59+
"ECDHE-RSA-AES256-SHA384",
60+
"ECDH-ECDSA-AES256-GCM-SHA384",
61+
"ECDH-RSA-AES256-GCM-SHA384",
62+
"ECDH-ECDSA-AES256-SHA384",
63+
"ECDH-RSA-AES256-SHA384",
64+
"DHE-RSA-AES256-GCM-SHA384"]}]}]}],
65+
[rabbitmq_trust_store]}].
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
%% This Source Code Form is subject to the terms of the Mozilla Public
2+
%% License, v. 2.0. If a copy of the MPL was not distributed with this
3+
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
%%
5+
%% Copyright (c) 2007-2026 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
%%
7+
8+
-module(http_provider_tls_prop_SUITE).
9+
10+
-compile([export_all, nowarn_export_all]).
11+
12+
-include_lib("proper/include/proper.hrl").
13+
-include_lib("common_test/include/ct.hrl").
14+
-include_lib("eunit/include/eunit.hrl").
15+
16+
all() ->
17+
[{group, unit},
18+
{group, prop}].
19+
20+
groups() ->
21+
[{unit, [parallel],
22+
[sslv3_filtered_from_versions,
23+
other_versions_preserved,
24+
hibernate_after_added_when_absent,
25+
hibernate_after_preserved_when_set,
26+
cacertfile_prevents_cacerts_injection,
27+
cacerts_preserved_when_set,
28+
user_options_preserved]},
29+
{prop, [parallel],
30+
[prop_sslv3_never_present,
31+
prop_hibernate_after_always_set,
32+
prop_cacertfile_prevents_cacerts_injection,
33+
prop_user_options_preserved]}].
34+
35+
suite() ->
36+
[{timetrap, {seconds, 60}}].
37+
38+
init_per_suite(Config) ->
39+
{ok, _} = application:ensure_all_started(ssl),
40+
Config.
41+
42+
end_per_suite(_Config) ->
43+
ok.
44+
45+
init_per_group(_GroupName, Config) ->
46+
Config.
47+
48+
end_per_group(_GroupName, Config) ->
49+
Config.
50+
51+
init_per_testcase(_Case, Config) ->
52+
Config.
53+
54+
end_per_testcase(_Case, _Config) ->
55+
ok.
56+
57+
%% -------------------------------------------------------------------
58+
%% Unit tests
59+
%% -------------------------------------------------------------------
60+
61+
sslv3_filtered_from_versions(_Config) ->
62+
Opts = prepare([{versions, [sslv3, 'tlsv1.2', 'tlsv1.3']}]),
63+
Versions = opt(versions, Opts),
64+
false = lists:member(sslv3, Versions),
65+
true = lists:member('tlsv1.2', Versions),
66+
true = lists:member('tlsv1.3', Versions).
67+
68+
other_versions_preserved(_Config) ->
69+
Opts = prepare([{versions, ['tlsv1.2', 'tlsv1.3']}]),
70+
['tlsv1.2', 'tlsv1.3'] = opt(versions, Opts).
71+
72+
hibernate_after_added_when_absent(_Config) ->
73+
Opts = prepare([]),
74+
6000 = opt(hibernate_after, Opts).
75+
76+
hibernate_after_preserved_when_set(_Config) ->
77+
Opts = prepare([{hibernate_after, 12000}]),
78+
12000 = opt(hibernate_after, Opts).
79+
80+
cacertfile_prevents_cacerts_injection(_Config) ->
81+
Opts = prepare([{cacertfile, "/path/to/ca.pem"}]),
82+
undefined = opt(cacerts, Opts).
83+
84+
cacerts_preserved_when_set(_Config) ->
85+
MyCaCerts = [<<1, 2, 3>>],
86+
Opts = prepare([{cacerts, MyCaCerts}]),
87+
MyCaCerts = opt(cacerts, Opts).
88+
89+
user_options_preserved(_Config) ->
90+
Input = [{certfile, "/path/to/cert.pem"},
91+
{keyfile, "/path/to/key.pem"},
92+
{versions, ['tlsv1.2', 'tlsv1.3']}],
93+
Opts = prepare(Input),
94+
"/path/to/cert.pem" = opt(certfile, Opts),
95+
"/path/to/key.pem" = opt(keyfile, Opts),
96+
['tlsv1.2', 'tlsv1.3'] = opt(versions, Opts).
97+
98+
%% -------------------------------------------------------------------
99+
%% Property-based tests
100+
%% -------------------------------------------------------------------
101+
102+
prop_sslv3_never_present(_Config) ->
103+
run_proper(
104+
fun() ->
105+
?FORALL(
106+
UserOpts, ssl_options(),
107+
not lists:member(sslv3, proplists:get_value(versions, prepare(UserOpts), [])))
108+
end).
109+
110+
prop_hibernate_after_always_set(_Config) ->
111+
run_proper(
112+
fun() ->
113+
?FORALL(
114+
UserOpts, ssl_options(),
115+
is_integer(opt(hibernate_after, prepare(UserOpts))))
116+
end).
117+
118+
prop_cacertfile_prevents_cacerts_injection(_Config) ->
119+
run_proper(
120+
fun() ->
121+
?FORALL(
122+
BaseOpts, ssl_options_without_ca(),
123+
undefined =:= opt(cacerts, prepare([{cacertfile, "/path/to/ca.pem"} | BaseOpts])))
124+
end).
125+
126+
prop_user_options_preserved(_Config) ->
127+
run_proper(
128+
fun() ->
129+
?FORALL(
130+
UserOpts, ssl_options(),
131+
begin
132+
Result = prepare(UserOpts),
133+
%% Keys that fix_client may normalise or supplement.
134+
Normalised = [versions, hibernate_after, cacerts],
135+
lists:all(
136+
fun({K, V}) ->
137+
lists:member(K, Normalised) orelse
138+
opt(K, Result) =:= V
139+
end, UserOpts)
140+
end)
141+
end).
142+
143+
%% -------------------------------------------------------------------
144+
%% Generators
145+
%% -------------------------------------------------------------------
146+
147+
ssl_options() ->
148+
?LET(Opts, list(ssl_option()),
149+
unique_keys(Opts)).
150+
151+
ssl_options_without_ca() ->
152+
?LET(Opts, ssl_options(),
153+
lists:filter(fun({K, _}) ->
154+
K =/= cacerts andalso K =/= cacertfile
155+
end, Opts)).
156+
157+
ssl_option() ->
158+
oneof([
159+
{versions, list(oneof(['tlsv1.2', 'tlsv1.3']))},
160+
{hibernate_after, range(1000, 60000)},
161+
{certfile, binary()},
162+
{keyfile, binary()},
163+
{cacerts, list(binary())},
164+
{cacertfile, binary()},
165+
{ciphers, list(binary())}
166+
]).
167+
168+
unique_keys(Opts) ->
169+
lists:foldl(fun({K, _} = Opt, Acc) ->
170+
case lists:keymember(K, 1, Acc) of
171+
true -> Acc;
172+
false -> [Opt | Acc]
173+
end
174+
end, [], Opts).
175+
176+
%% -------------------------------------------------------------------
177+
%% Helpers
178+
%% -------------------------------------------------------------------
179+
180+
prepare(Opts) ->
181+
rabbit_trust_store_http_provider:prepare_ssl_opts(Opts).
182+
183+
opt(Key, Options) ->
184+
proplists:get_value(Key, Options).
185+
186+
run_proper(Fun) ->
187+
?assert(proper:counterexample(
188+
Fun(),
189+
[{numtests, 100},
190+
{on_output, fun(".", _) -> ok;
191+
(F, A) -> ct:pal(?LOW_IMPORTANCE, F, A)
192+
end}])).

0 commit comments

Comments
 (0)