diff --git a/apps/emqx/src/emqx_tls_lib.erl b/apps/emqx/src/emqx_tls_lib.erl index 653f26708..b0115ddc5 100644 --- a/apps/emqx/src/emqx_tls_lib.erl +++ b/apps/emqx/src/emqx_tls_lib.erl @@ -485,7 +485,8 @@ to_server_opts(Type, Opts) -> cacertfile => Path(cacertfile), ciphers => Ciphers, versions => Versions - }) + }), + Versions ). %% @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)), Versions = integral_versions(Type, Get(versions)), 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 -> [] end. @@ -552,10 +556,35 @@ resolve_cert_path_for_read_strict(Path) -> resolve_cert_path_for_read(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(). fuzzy_map_get(Key, Options, Default) -> diff --git a/apps/emqx/test/emqx_tls_lib_tests.erl b/apps/emqx/test/emqx_tls_lib_tests.erl index 4e8435484..8a412af05 100644 --- a/apps/emqx/test/emqx_tls_lib_tests.erl +++ b/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])). +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). test_key() ->