Просмотр исходного кода

feat(listeners): Ensure no tlsv1.3 for otp 22 or earlier

Zaiming Shi 5 лет назад
Родитель
Сommit
2d150127d4
2 измененных файлов с 70 добавлено и 2 удалено
  1. 14 2
      src/emqx_listeners.erl
  2. 56 0
      src/emqx_tls_lib.erl

+ 14 - 2
src/emqx_listeners.erl

@@ -134,7 +134,18 @@ start_listener(Proto, ListenOn, Options) when Proto == https; Proto == wss ->
     start_http_listener(fun cowboy:start_tls/3, 'mqtt:wss', ListenOn,
                         ranch_opts(Options), ws_opts(Options)).
 
-start_mqtt_listener(Name, ListenOn, Options) ->
+replace(Opts, Key, Value) -> [{Key, Value} | proplists:delete(Key, Opts)].
+
+drop_tls13_for_old_otp(Options) ->
+    case proplists:get_value(ssl_options, Options) of
+        undefined -> Options;
+        SslOpts ->
+            SslOpts1 = emqx_tls_lib:drop_tls13_for_old_otp(SslOpts),
+            replace(Options, ssl_options, SslOpts1)
+    end.
+
+start_mqtt_listener(Name, ListenOn, Options0) ->
+    Options = drop_tls13_for_old_otp(Options0),
     SockOpts = esockd:parse_opt(Options),
     esockd:open(Name, ListenOn, merge_default(SockOpts),
                 {emqx_connection, start_link, [Options -- SockOpts]}).
@@ -151,7 +162,8 @@ ws_opts(Options) ->
     ProxyProto = proplists:get_value(proxy_protocol, Options, false),
     #{env => #{dispatch => Dispatch}, proxy_header => ProxyProto}.
 
-ranch_opts(Options) ->
+ranch_opts(Options0) ->
+    Options = drop_tls13_for_old_otp(Options0),
     NumAcceptors = proplists:get_value(acceptors, Options, 4),
     MaxConnections = proplists:get_value(max_connections, Options, 1024),
     TcpOptions = proplists:get_value(tcp_options, Options, []),

+ 56 - 0
src/emqx_tls_lib.erl

@@ -21,6 +21,7 @@
         , default_ciphers/0
         , default_ciphers/1
         , integral_ciphers/2
+        , drop_tls13_for_old_otp/1
         ]).
 
 %% non-empty string
@@ -140,3 +141,58 @@ split_by_comma(Bin) ->
 %% trim spaces
 trim_space(Bin) ->
     hd([I || I <- binary:split(Bin, <<" ">>), I =/= <<>>]).
+
+%% @doc Drop tlsv1.3 version and ciphers from ssl options
+%% if running on otp 22 or earlier.
+drop_tls13_for_old_otp(SslOpts) ->
+    case list_to_integer(erlang:system_info(otp_release)) < 23 of
+        true -> drop_tls13(SslOpts);
+        false -> SslOpts
+    end.
+
+%% The ciphers that ssl:cipher_suites(exclusive, 'tlsv1.3', openssl)
+%% should return when running on otp 23.
+%% But we still have to hard-code them because tlsv1.3 on otp 22 is
+%% not trustworthy.
+-define(TLSV13_EXCLUSIVE_CIPHERS, [ "TLS_AES_256_GCM_SHA384"
+                                  , "TLS_AES_128_GCM_SHA256"
+                                  , "TLS_CHACHA20_POLY1305_SHA256"
+                                  , "TLS_AES_128_CCM_SHA256"
+                                  , "TLS_AES_128_CCM_8_SHA256"
+                                  ]).
+drop_tls13(SslOpts0) ->
+    SslOpts1 = case proplists:get_value(versions, SslOpts0) of
+                   undefined -> SslOpts0;
+                   Vsns -> replace(SslOpts0, versions, Vsns -- ['tlsv1.3'])
+               end,
+    case proplists:get_value(ciphers, SslOpts1) of
+        undefined -> SslOpts1;
+        Ciphers -> replace(SslOpts1, ciphers, Ciphers -- ?TLSV13_EXCLUSIVE_CIPHERS)
+    end.
+
+replace(Opts, Key, Value) -> [{Key, Value} | proplists:delete(Key, Opts)].
+
+-if(?OTP_RELEASE > 22).
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+drop_tls13_test() ->
+    Versions = default_versions(),
+    ?assert(lists:member('tlsv1.3', Versions)),
+    Ciphers = default_ciphers(),
+    ?assert(has_tlsv13_cipher(Ciphers)),
+    Opts0 = [{versions, Versions}, {ciphers, Ciphers}, other, {bool, true}],
+    Opts = drop_tls13(Opts0),
+    ?assertNot(lists:member('tlsv1.3', proplists:get_value(versions, Opts))),
+    ?assertNot(has_tlsv13_cipher(proplists:get_value(ciphers, Opts))).
+
+drop_tls13_no_versions_cipers_test() ->
+    Opts0 = [other, {bool, true}],
+    Opts = drop_tls13(Opts0),
+    ?_assertEqual(Opts0, Opts).
+
+has_tlsv13_cipher(Ciphers) ->
+    lists:any(fun(C) -> lists:member(C, Ciphers) end, ?TLSV13_EXCLUSIVE_CIPHERS).
+
+-endif.
+-endif.