diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index 344a1aa45..202cd4ae9 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -1156,39 +1156,11 @@ default_ciphers(quic) -> [ "TLS_AES_128_GCM_SHA256", "TLS_CHACHA20_POLY1305_SHA256" ]; -default_ciphers(tls_all_available) -> - default_ciphers('tlsv1.3') ++ - default_ciphers('tlsv1.2') ++ - default_ciphers(psk); default_ciphers(dtls_all_available) -> %% as of now, dtls does not support tlsv1.3 ciphers - default_ciphers('tlsv1.2') ++ default_ciphers('psk'); -default_ciphers('tlsv1.3') -> - case is_tlsv13_available() of - true -> ssl:cipher_suites(exclusive, 'tlsv1.3', openssl); - false -> [] - end ++ default_ciphers('tlsv1.2'); -default_ciphers('tlsv1.2') -> [ - "ECDHE-ECDSA-AES256-GCM-SHA384", - "ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-ECDSA-AES256-SHA384", "ECDHE-RSA-AES256-SHA384", - "ECDHE-ECDSA-DES-CBC3-SHA", "ECDH-ECDSA-AES256-GCM-SHA384", "ECDH-RSA-AES256-GCM-SHA384", - "ECDH-ECDSA-AES256-SHA384", "ECDH-RSA-AES256-SHA384", "DHE-DSS-AES256-GCM-SHA384", - "DHE-DSS-AES256-SHA256", "AES256-GCM-SHA384", "AES256-SHA256", - "ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-RSA-AES128-GCM-SHA256", - "ECDHE-ECDSA-AES128-SHA256", "ECDHE-RSA-AES128-SHA256", "ECDH-ECDSA-AES128-GCM-SHA256", - "ECDH-RSA-AES128-GCM-SHA256", "ECDH-ECDSA-AES128-SHA256", "ECDH-RSA-AES128-SHA256", - "DHE-DSS-AES128-GCM-SHA256", "DHE-DSS-AES128-SHA256", "AES128-GCM-SHA256", "AES128-SHA256", - "ECDHE-ECDSA-AES256-SHA", "ECDHE-RSA-AES256-SHA", "DHE-DSS-AES256-SHA", - "ECDH-ECDSA-AES256-SHA", "ECDH-RSA-AES256-SHA", "AES256-SHA", "ECDHE-ECDSA-AES128-SHA", - "ECDHE-RSA-AES128-SHA", "DHE-DSS-AES128-SHA", "ECDH-ECDSA-AES128-SHA", - "ECDH-RSA-AES128-SHA", "AES128-SHA" - ]; -default_ciphers(psk) -> - [ "RSA-PSK-AES256-GCM-SHA384","RSA-PSK-AES256-CBC-SHA384", - "RSA-PSK-AES128-GCM-SHA256","RSA-PSK-AES128-CBC-SHA256", - "RSA-PSK-AES256-CBC-SHA","RSA-PSK-AES128-CBC-SHA", - "RSA-PSK-DES-CBC3-SHA","RSA-PSK-RC4-SHA" - ]. + emqx_tls_lib:selected_ciphers(['dtlsv1.2', 'dtlsv1']); +default_ciphers(tls_all_available) -> + emqx_tls_lib:default_ciphers(). %% @private return a list of keys in a parent field -spec(keys(string(), hocon:config()) -> [string()]). diff --git a/apps/emqx/src/emqx_tls_lib.erl b/apps/emqx/src/emqx_tls_lib.erl index 683166e87..11145f684 100644 --- a/apps/emqx/src/emqx_tls_lib.erl +++ b/apps/emqx/src/emqx_tls_lib.erl @@ -19,7 +19,7 @@ -export([ default_versions/0 , integral_versions/1 , default_ciphers/0 - , default_ciphers/1 + , selected_ciphers/1 , integral_ciphers/2 , drop_tls13_for_old_otp/1 ]). @@ -59,27 +59,61 @@ integral_versions(Desired) -> Filtered end. -%% @doc Return a list of default (openssl string format) cipher suites. --spec default_ciphers() -> [string()]. -default_ciphers() -> default_ciphers(default_versions()). - %% @doc Return a list of (openssl string format) cipher suites. --spec default_ciphers([ssl:tls_version()]) -> [string()]. -default_ciphers(['tlsv1.3']) -> +-spec all_ciphers([ssl:tls_version()]) -> [string()]. +all_ciphers(['tlsv1.3']) -> %% When it's only tlsv1.3 wanted, use 'exclusive' here %% because 'all' returns legacy cipher suites too, %% which does not make sense since tlsv1.3 can not use %% legacy cipher suites. ssl:cipher_suites(exclusive, 'tlsv1.3', openssl); -default_ciphers(Versions) -> +all_ciphers(Versions) -> %% assert non-empty [_ | _] = dedup(lists:append([ssl:cipher_suites(all, V, openssl) || V <- Versions])). + +%% @doc All Pre-selected TLS ciphers. +default_ciphers() -> + selected_ciphers(available_versions()). + +%% @doc Pre-selected TLS ciphers for given versions.. +selected_ciphers(Vsns) -> + All = all_ciphers(Vsns), + dedup(lists:filter(fun(Cipher) -> lists:member(Cipher, All) end, + lists:flatmap(fun do_selected_ciphers/1, Vsns))). + +do_selected_ciphers('tlsv1.3') -> + case lists:member('tlsv1.3', proplists:get_value(available, ssl:versions())) of + true -> ssl:cipher_suites(exclusive, 'tlsv1.3', openssl); + false -> [] + end ++ do_selected_ciphers('tlsv1.2'); +do_selected_ciphers(_) -> + [ "ECDHE-ECDSA-AES256-GCM-SHA384", + "ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-ECDSA-AES256-SHA384", "ECDHE-RSA-AES256-SHA384", + "ECDHE-ECDSA-DES-CBC3-SHA", "ECDH-ECDSA-AES256-GCM-SHA384", "ECDH-RSA-AES256-GCM-SHA384", + "ECDH-ECDSA-AES256-SHA384", "ECDH-RSA-AES256-SHA384", "DHE-DSS-AES256-GCM-SHA384", + "DHE-DSS-AES256-SHA256", "AES256-GCM-SHA384", "AES256-SHA256", + "ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-RSA-AES128-GCM-SHA256", + "ECDHE-ECDSA-AES128-SHA256", "ECDHE-RSA-AES128-SHA256", "ECDH-ECDSA-AES128-GCM-SHA256", + "ECDH-RSA-AES128-GCM-SHA256", "ECDH-ECDSA-AES128-SHA256", "ECDH-RSA-AES128-SHA256", + "DHE-DSS-AES128-GCM-SHA256", "DHE-DSS-AES128-SHA256", "AES128-GCM-SHA256", "AES128-SHA256", + "ECDHE-ECDSA-AES256-SHA", "ECDHE-RSA-AES256-SHA", "DHE-DSS-AES256-SHA", + "ECDH-ECDSA-AES256-SHA", "ECDH-RSA-AES256-SHA", "AES256-SHA", "ECDHE-ECDSA-AES128-SHA", + "ECDHE-RSA-AES128-SHA", "DHE-DSS-AES128-SHA", "ECDH-ECDSA-AES128-SHA", + "ECDH-RSA-AES128-SHA", "AES128-SHA", + + %% psk + "RSA-PSK-AES256-GCM-SHA384","RSA-PSK-AES256-CBC-SHA384", + "RSA-PSK-AES128-GCM-SHA256","RSA-PSK-AES128-CBC-SHA256", + "RSA-PSK-AES256-CBC-SHA","RSA-PSK-AES128-CBC-SHA", + "RSA-PSK-DES-CBC3-SHA","RSA-PSK-RC4-SHA" + ]. + %% @doc Ensure version & cipher-suites integrity. -spec integral_ciphers([ssl:tls_version()], binary() | string() | [string()]) -> [string()]. integral_ciphers(Versions, Ciphers) when Ciphers =:= [] orelse Ciphers =:= undefined -> %% not configured - integral_ciphers(Versions, default_ciphers(Versions)); + integral_ciphers(Versions, selected_ciphers(Versions)); integral_ciphers(Versions, Ciphers) when ?IS_STRING_LIST(Ciphers) -> %% ensure tlsv1.3 ciphers if none of them is found in Ciphers dedup(ensure_tls13_cipher(lists:member('tlsv1.3', Versions), Ciphers)); @@ -93,7 +127,7 @@ integral_ciphers(Versions, Ciphers) -> %% In case tlsv1.3 is present, ensure tlsv1.3 cipher is added if user %% did not provide it from config --- which is a common mistake ensure_tls13_cipher(true, Ciphers) -> - Tls13Ciphers = default_ciphers(['tlsv1.3']), + Tls13Ciphers = selected_ciphers(['tlsv1.3']), case lists:any(fun(C) -> lists:member(C, Tls13Ciphers) end, Ciphers) of true -> Ciphers; false -> Tls13Ciphers ++ Ciphers @@ -179,10 +213,12 @@ drop_tls13(SslOpts0) -> -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). +all_ciphers() -> all_ciphers(default_versions()). + drop_tls13_test() -> Versions = default_versions(), ?assert(lists:member('tlsv1.3', Versions)), - Ciphers = default_ciphers(), + Ciphers = all_ciphers(), ?assert(has_tlsv13_cipher(Ciphers)), Opts0 = #{versions => Versions, ciphers => Ciphers, other => true}, Opts = drop_tls13(Opts0), diff --git a/apps/emqx/test/emqx_schema_tests.erl b/apps/emqx/test/emqx_schema_tests.erl index 4585089e2..e2825498d 100644 --- a/apps/emqx/test/emqx_schema_tests.erl +++ b/apps/emqx/test/emqx_schema_tests.erl @@ -62,12 +62,7 @@ ssl_opts_cipher_comma_separated_string_test() -> ssl_opts_tls_psk_test() -> Sc = emqx_schema:server_ssl_opts_schema(#{}, false), Checked = validate(Sc, #{<<"versions">> => [<<"tlsv1.2">>]}), - ?assertMatch(#{versions := ['tlsv1.2']}, Checked), - #{ciphers := Ciphers} = Checked, - PskCiphers = emqx_schema:default_ciphers(psk), - lists:foreach(fun(Cipher) -> - ?assert(lists:member(Cipher, Ciphers)) - end, PskCiphers). + ?assertMatch(#{versions := ['tlsv1.2']}, Checked). bad_cipher_test() -> Sc = emqx_schema:server_ssl_opts_schema(#{}, false),