Sfoglia il codice sorgente

fix(tls): issue when ssl listner is configured to use tls v1.3 only

clients could not connect due to incompatible tls options if ssl listner is configured to use tls v1.3 only
Ivan Dyachkov 2 anni fa
parent
commit
abbba71191
2 ha cambiato i file con 112 aggiunte e 18 eliminazioni
  1. 47 18
      apps/emqx/src/emqx_tls_lib.erl
  2. 65 0
      apps/emqx/test/emqx_tls_lib_tests.erl

+ 47 - 18
apps/emqx/src/emqx_tls_lib.erl

@@ -485,7 +485,8 @@ to_server_opts(Type, Opts) ->
             cacertfile => Path(cacertfile),
             cacertfile => Path(cacertfile),
             ciphers => Ciphers,
             ciphers => Ciphers,
             versions => Versions
             versions => Versions
-        })
+        }),
+        Versions
     ).
     ).
 
 
 %% @doc Convert hocon-checked tls client options (map()) to
 %% @doc Convert hocon-checked tls client options (map()) to
@@ -510,19 +511,22 @@ to_client_opts(Type, Opts) ->
             SNI = ensure_sni(Get(server_name_indication)),
             SNI = ensure_sni(Get(server_name_indication)),
             Versions = integral_versions(Type, Get(versions)),
             Versions = integral_versions(Type, Get(versions)),
             Ciphers = integral_ciphers(Versions, Get(ciphers)),
             Ciphers = integral_ciphers(Versions, Get(ciphers)),
-            filter([
-                {keyfile, KeyFile},
-                {certfile, CertFile},
-                {cacertfile, CAFile},
-                {verify, Verify},
-                {server_name_indication, SNI},
-                {versions, Versions},
-                {ciphers, Ciphers},
-                {reuse_sessions, Get(reuse_sessions)},
-                {depth, Get(depth)},
-                {password, ensure_str(Get(password))},
-                {secure_renegotiate, Get(secure_renegotiate)}
-            ]);
+            filter(
+                [
+                    {keyfile, KeyFile},
+                    {certfile, CertFile},
+                    {cacertfile, CAFile},
+                    {verify, Verify},
+                    {server_name_indication, SNI},
+                    {versions, Versions},
+                    {ciphers, Ciphers},
+                    {reuse_sessions, Get(reuse_sessions)},
+                    {depth, Get(depth)},
+                    {password, ensure_str(Get(password))},
+                    {secure_renegotiate, Get(secure_renegotiate)}
+                ],
+                Versions
+            );
         false ->
         false ->
             []
             []
     end.
     end.
@@ -552,10 +556,35 @@ resolve_cert_path_for_read_strict(Path) ->
 resolve_cert_path_for_read(Path) ->
 resolve_cert_path_for_read(Path) ->
     emqx_schema:naive_env_interpolation(Path).
     emqx_schema:naive_env_interpolation(Path).
 
 
-filter([]) -> [];
-filter([{_, undefined} | T]) -> filter(T);
-filter([{_, ""} | T]) -> filter(T);
-filter([H | T]) -> [H | filter(T)].
+filter([], _) ->
+    [];
+filter([{_, undefined} | T], Versions) ->
+    filter(T, Versions);
+filter([{_, ""} | T], Versions) ->
+    filter(T, Versions);
+filter([{K, V} | T], Versions) ->
+    case tls_option_compatible_versions(K) of
+        all ->
+            [{K, V} | filter(T, Versions)];
+        CompatibleVersions ->
+            case CompatibleVersions -- (CompatibleVersions -- Versions) of
+                [] ->
+                    filter(T, Versions);
+                _ ->
+                    [{K, V} | filter(T, Versions)]
+            end
+    end.
+
+tls_option_compatible_versions(reuse_sessions) ->
+    [dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
+tls_option_compatible_versions(secure_renegotiate) ->
+    [dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
+tls_option_compatible_versions(user_lookup_fun) ->
+    [dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
+tls_option_compatible_versions(client_renegotiation) ->
+    [dtlsv1, 'dtlsv1.2', 'tlsv1', 'tlsv1.1', 'tlsv1.2'];
+tls_option_compatible_versions(_) ->
+    all.
 
 
 -spec fuzzy_map_get(atom() | binary(), map(), any()) -> any().
 -spec fuzzy_map_get(atom() | binary(), map(), any()) -> any().
 fuzzy_map_get(Key, Options, Default) ->
 fuzzy_map_get(Key, Options, Default) ->

+ 65 - 0
apps/emqx/test/emqx_tls_lib_tests.erl

@@ -224,6 +224,71 @@ ssl_file_deterministic_names_test() ->
     ),
     ),
     _ = file:del_dir_r(filename:join(["/tmp", ?FUNCTION_NAME])).
     _ = file:del_dir_r(filename:join(["/tmp", ?FUNCTION_NAME])).
 
 
+to_client_opts_test() ->
+    VersionsAll = [tlsv1, 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'],
+    Versions13Only = ['tlsv1.3'],
+    Options = #{
+        enable => true,
+        verify => "Verify",
+        server_name_indication => "SNI",
+        ciphers => "Ciphers",
+        depth => "depth",
+        password => "password",
+        versions => VersionsAll,
+        secure_renegotiate => "secure_renegotiate",
+        reuse_sessions => "reuse_sessions"
+    },
+    Expected1 = lists:usort(maps:keys(Options) -- [enable]),
+    ?assertEqual(
+        Expected1, lists:usort(proplists:get_keys(emqx_tls_lib:to_client_opts(tls, Options)))
+    ),
+    Expected2 =
+        lists:usort(
+            maps:keys(Options) --
+                [enable, reuse_sessions, secure_renegotiate]
+        ),
+    ?assertEqual(
+        Expected2,
+        lists:usort(
+            proplists:get_keys(
+                emqx_tls_lib:to_client_opts(tls, Options#{versions := Versions13Only})
+            )
+        )
+    ),
+    Expected3 = lists:usort(maps:keys(Options) -- [enable, depth, password]),
+    ?assertEqual(
+        Expected3,
+        lists:usort(
+            proplists:get_keys(
+                emqx_tls_lib:to_client_opts(tls, Options#{depth := undefined, password := ""})
+            )
+        )
+    ).
+
+to_server_opts_test() ->
+    VersionsAll = [tlsv1, 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'],
+    Versions13Only = ['tlsv1.3'],
+    Options = #{
+        verify => "Verify",
+        ciphers => "Ciphers",
+        versions => VersionsAll,
+        user_lookup_fun => "funfunfun",
+        client_renegotiation => "client_renegotiation"
+    },
+    Expected1 = lists:usort(maps:keys(Options)),
+    ?assertEqual(
+        Expected1, lists:usort(proplists:get_keys(emqx_tls_lib:to_server_opts(tls, Options)))
+    ),
+    Expected2 = lists:usort(maps:keys(Options) -- [user_lookup_fun, client_renegotiation]),
+    ?assertEqual(
+        Expected2,
+        lists:usort(
+            proplists:get_keys(
+                emqx_tls_lib:to_server_opts(tls, Options#{versions := Versions13Only})
+            )
+        )
+    ).
+
 bin(X) -> iolist_to_binary(X).
 bin(X) -> iolist_to_binary(X).
 
 
 test_key() ->
 test_key() ->