diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index 40910614a..f97a1500b 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -2679,10 +2679,30 @@ validate_ciphers(Ciphers) -> validate_tls_versions(Collection, Versions) -> AvailableVersions = available_tls_vsns(Collection), case lists:filter(fun(V) -> not lists:member(V, AvailableVersions) end, Versions) of - [] -> ok; + [] -> validate_tls_version_gap(Versions); Vs -> {error, {unsupported_tls_versions, Vs}} end. +%% See also `validate_version_gap/1` in OTP ssl.erl, +%% e.g: https://github.com/emqx/otp/blob/emqx-OTP-25.1.2/lib/ssl/src/ssl.erl#L2566. +%% Do not allow configuration of TLS 1.3 with a gap where TLS 1.2 is not supported +%% as that configuration can trigger the built in version downgrade protection +%% mechanism and the handshake can fail with an Illegal Parameter alert. +validate_tls_version_gap(Versions) -> + case lists:member('tlsv1.3', Versions) of + true when length(Versions) >= 2 -> + case lists:member('tlsv1.2', Versions) of + true -> + ok; + false -> + {error, + "Using multiple versions that include tlsv1.3 but " + "exclude tlsv1.2 is not allowed"} + end; + _ -> + ok + end. + validations() -> [ {check_process_watermark, fun check_process_watermark/1}, diff --git a/apps/emqx/src/emqx_tls_lib.erl b/apps/emqx/src/emqx_tls_lib.erl index d40ae5033..b5b653f56 100644 --- a/apps/emqx/src/emqx_tls_lib.erl +++ b/apps/emqx/src/emqx_tls_lib.erl @@ -556,33 +556,9 @@ resolve_cert_path_for_read_strict(Path) -> resolve_cert_path_for_read(Path) -> emqx_schema:naive_env_interpolation(Path). -ensure_valid_options(Options, Versions0) -> - Versions = validate_version_gap(Versions0), +ensure_valid_options(Options, Versions) -> ensure_valid_options(Options, Versions, []). -%% See also lib/ssl/src/ssl.erl#L2617. -%% Do not allow configuration of TLS 1.3 with a gap where TLS 1.2 is not supported -%% as that configuration can trigger the built in version downgrade protection -%% mechanism and the handshake can fail with an Illegal Parameter alert. -validate_version_gap(Versions) -> - case lists:member('tlsv1.3', Versions) of - true when length(Versions) >= 2 -> - case lists:member('tlsv1.2', Versions) of - true -> - Versions; - false -> - NewVersions = ['tlsv1.3'], - ?SLOG(warning, #{ - msg => "tlsv13_version_gap", - versions => Versions, - new_versions => NewVersions - }), - NewVersions - end; - _ -> - Versions - end. - ensure_valid_options([], _, Acc) -> lists:reverse(Acc); ensure_valid_options([{_, undefined} | T], Versions, Acc) -> @@ -607,7 +583,7 @@ ensure_valid_options([{K, V} | T], Versions, Acc) -> end end. -%% see lib/ssl/src/ssl.erl, assert_option_dependency/4 +%% see otp/lib/ssl/src/ssl.erl, `assert_option_dependency/4` tls_option_compatible_versions(beast_mitigation) -> [dtlsv1, 'tlsv1']; tls_option_compatible_versions(padding_check) -> diff --git a/apps/emqx/test/emqx_schema_tests.erl b/apps/emqx/test/emqx_schema_tests.erl index b5fd156ad..a6e72cd27 100644 --- a/apps/emqx/test/emqx_schema_tests.erl +++ b/apps/emqx/test/emqx_schema_tests.erl @@ -94,6 +94,18 @@ ssl_opts_tls_psk_test() -> Checked = validate(Sc, #{<<"versions">> => [<<"tlsv1.2">>]}), ?assertMatch(#{versions := ['tlsv1.2']}, Checked). +ssl_opts_version_gap_test_() -> + Sc = emqx_schema:server_ssl_opts_schema(#{}, false), + RanchSc = emqx_schema:server_ssl_opts_schema(#{}, true), + Reason = "Using multiple versions that include tlsv1.3 but exclude tlsv1.2 is not allowed", + [ + ?_assertThrow( + {_, [#{kind := validation_error, reason := Reason}]}, + validate(S, #{<<"versions">> => [<<"tlsv1.1">>, <<"tlsv1.3">>]}) + ) + || S <- [Sc, RanchSc] + ]. + bad_cipher_test() -> Sc = emqx_schema:server_ssl_opts_schema(#{}, false), Reason = {bad_ciphers, ["foo"]}, diff --git a/changes/ce/fix-11028.en.md b/changes/ce/fix-11028.en.md new file mode 100644 index 000000000..c47351520 --- /dev/null +++ b/changes/ce/fix-11028.en.md @@ -0,0 +1,7 @@ +Disallow using multiple TLS versions in the listener config that include tlsv1.3 but exclude tlsv1.2. + +Using TLS configuration with such version gap caused connection errors. +Additionally, drop and log TLS options that are incompatible with the selected TLS version(s). + +Note: any old listener configuration with the version gap described above will fail to load +after applying this fix and must be manually fixed.