Browse Source

fix(tls): Ensure tls config integrity

For default tsl version and ciphers, we try to use otp release number
to determin if we want to use tlsv1.3

For default configs, we try to porivde both tlsv1.3 and
ciphers in config (even for commented out configs)
Zaiming Shi 5 years ago
parent
commit
f000b6583c

+ 4 - 5
apps/emqx_auth_http/src/emqx_auth_http_app.erl

@@ -74,11 +74,10 @@ translate_env(EnvName) ->
                                                         (_) ->
                                                         true
                                                     end, [{keyfile, KeyFile}, {certfile, CertFile}, {cacertfile, CACertFile}]),
-                            TlsVers = ['tlsv1.2','tlsv1.1',tlsv1],
-                            NTLSOpts = [{versions, TlsVers},
-                                        {ciphers, lists:foldl(fun(TlsVer, Ciphers) ->
-                                                                    Ciphers ++ ssl:cipher_suites(all, TlsVer)
-                                                                end, [], TlsVers)} | TLSOpts],
+                            NTLSOpts = [ {versions, emqx_tls_lib:default_versions()}
+                                       , {ciphers, emqx_tls_lib:default_ciphers()}
+                                       | TLSOpts
+                                       ],
                             [{transport, ssl}, {transport_opts, [Inet | NTLSOpts]}]
                         end,
             PoolOpts = [{host, Host},

+ 1 - 1
apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf

@@ -43,7 +43,7 @@ auth.pgsql.ssl = off
 ## You can configure multi-version use "," split,
 ## default value is :tlsv1.2
 ## Example:
-##    tlsv1.1,tlsv1.2,tlsv1.3
+##    tlsv1.2,tlsv1.1
 ##
 #auth.pgsql.ssl.tls_versions = tlsv1.2
 

+ 1 - 1
apps/emqx_bridge_mqtt/README.md

@@ -126,7 +126,7 @@ bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-S
 bridge.mqtt.emqx2.keepalive = 60s
 
 ## Supported TLS version
-bridge.mqtt.emqx2.tls_versions = tlsv1.2,tlsv1.1,tlsv1
+bridge.mqtt.emqx2.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
 
 ## Forwarding topics of the message
 bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/#

+ 4 - 4
apps/emqx_bridge_mqtt/docs/guide.rst

@@ -133,9 +133,6 @@ EMQ X MQTT bridging principle: Create an MQTT client on the EMQ X broker, and co
    ## Key file of Client SSL connection 
    bridge.mqtt.emqx2.keyfile = etc/certs/client-key.pem
 
-   ## SSL encryption
-   bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384
-
    ## TTLS PSK password
    ## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time
    ##
@@ -146,7 +143,10 @@ EMQ X MQTT bridging principle: Create an MQTT client on the EMQ X broker, and co
    bridge.mqtt.emqx2.keepalive = 60s
 
    ## Supported TLS version
-   bridge.mqtt.emqx2.tls_versions = tlsv1.2,tlsv1.1,tlsv1
+   bridge.mqtt.emqx2.tls_versions = tlsv1.2
+
+   ## SSL encryption
+   bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384
 
    ## Forwarding topics of the message
    bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/#

+ 1 - 0
apps/emqx_bridge_mqtt/etc/emqx_bridge_mqtt.conf

@@ -128,6 +128,7 @@ bridge.mqtt.aws.keepalive = 60s
 
 ## TLS versions used by the bridge.
 ##
+## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
 ## Value: String
 bridge.mqtt.aws.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
 

+ 1 - 1
apps/emqx_bridge_mqtt/priv/emqx_bridge_mqtt.schema

@@ -90,7 +90,7 @@
 
 {mapping, "bridge.mqtt.$name.tls_versions", "emqx_bridge_mqtt.bridges", [
   {datatype, string},
-  {default, "tlsv1,tlsv1.1,tlsv1.2"}
+  {default, "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"}
 ]}.
 
 {mapping, "bridge.mqtt.$name.reconnect_interval", "emqx_bridge_mqtt.bridges", [

+ 9 - 12
apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_actions.erl

@@ -671,12 +671,6 @@ format_data([], Msg) ->
 format_data(Tokens, Msg) ->
     emqx_rule_utils:proc_tmpl(Tokens, Msg).
 
-tls_versions() ->
-    ['tlsv1.2','tlsv1.1', tlsv1].
-
-ciphers(Ciphers) ->
-    string:tokens(str(Ciphers), ", ").
-
 subscriptions(Subscriptions) ->
     scan_binary(<<"[", Subscriptions/binary, "].">>).
 
@@ -749,6 +743,8 @@ options(Options, PoolName) ->
                      Topic ->
                          [{subscriptions, [{Topic, Get(<<"qos">>)}]} | Subscriptions]
                  end,
+                 %% TODO check why only ciphers are configurable but not versions
+                 TlsVersions = emqx_tls_lib:default_versions(),
                  [{address, binary_to_list(Address)},
                   {bridge_mode, GetD(<<"bridge_mode">>, true)},
                   {clean_start, true},
@@ -761,12 +757,13 @@ options(Options, PoolName) ->
                   {proto_ver, mqtt_ver(Get(<<"proto_ver">>))},
                   {retry_interval, cuttlefish_duration:parse(str(GetD(<<"retry_interval">>, "30s")), s)},
                   {ssl, cuttlefish_flag:parse(str(Get(<<"ssl">>)))},
-                  {ssl_opts, [{versions, tls_versions()},
-                              {ciphers, ciphers(Get(<<"ciphers">>))},
-                              {keyfile, str(Get(<<"keyfile">>))},
-                              {certfile, str(Get(<<"certfile">>))},
-                              {cacertfile, str(Get(<<"cacertfile">>))}
-                             ]}] ++ Subscriptions1
+                  {ssl_opts, [ {keyfile, str(Get(<<"keyfile">>))}
+                             , {certfile, str(Get(<<"certfile">>))}
+                             , {cacertfile, str(Get(<<"cacertfile">>))}
+                             , {versions, TlsVersions}
+                             , {ciphers, emqx_tls_lib:integral_ciphers(TlsVersions, Get(<<"ciphers">>))}
+                             ]}
+                 ] ++ Subscriptions1
          end.
 
 

+ 1 - 4
apps/emqx_coap/priv/emqx_coap.schema

@@ -75,10 +75,7 @@ end}.
   Ciphers =
       case cuttlefish:conf_get("coap.dtls.ciphers", Conf, undefined) of
           undefined ->
-              lists:foldl(
-                  fun(TlsVer, Ciphers) ->
-                      Ciphers ++ ssl:cipher_suites(all, TlsVer)
-                  end, [], ['dtlsv1', 'dtlsv1.2']);
+              lists:append([ssl:cipher_suites(all, V, openssl) || V <- ['dtlsv1.2', 'dtlsv1']]);
           C ->
               SplitFun(C)
       end,

+ 2 - 1
apps/emqx_dashboard/etc/emqx_dashboard.conf

@@ -105,7 +105,8 @@ dashboard.listener.http.ipv6_v6only = false
 ## TLS versions only to protect from POODLE attack.
 ##
 ## Value: String, seperated by ','
-## dashboard.listener.https.tls_versions = tlsv1.2,tlsv1.1,tlsv1
+## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
+## dashboard.listener.https.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
 
 ## See: 'listener.ssl.<name>.ciphers' in emq.conf
 ##

+ 2 - 5
apps/emqx_exproto/test/emqx_exproto_SUITE.erl

@@ -425,8 +425,8 @@ udp_opts() ->
 
 ssl_opts() ->
     Certs = certs("key.pem", "cert.pem", "cacert.pem"),
-    [{versions, ['tlsv1.2','tlsv1.1',tlsv1]},
-     {ciphers, ciphers('tlsv1.2')},
+    [{versions, emqx_tls_lib:default_versions()},
+     {ciphers, emqx_tls_lib:default_ciphers()},
      {verify, verify_peer},
      {fail_if_no_peer_cert, true},
      {secure_renegotiate, false},
@@ -437,9 +437,6 @@ dtls_opts() ->
     Opts = ssl_opts(),
     lists:keyreplace(versions, 1, Opts, {versions, ['dtlsv1.2', 'dtlsv1']}).
 
-ciphers(Version) ->
-    proplists:get_value(ciphers, emqx_ct_helpers:client_ssl(Version)).
-
 %%--------------------------------------------------------------------
 %% Client-Opts
 

File diff suppressed because it is too large
+ 2 - 1
apps/emqx_management/etc/emqx_management.conf


+ 2 - 1
apps/emqx_stomp/etc/emqx_stomp.conf

@@ -58,7 +58,8 @@ stomp.listener.max_connections = 512
 ## TLS versions only to protect from POODLE attack.
 ##
 ## Value: String, seperated by ','
-## stomp.listener.tls_versions = tlsv1.2,tlsv1.1,tlsv1
+## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
+## stomp.listener.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
 
 ## SSL Handshake timeout.
 ##

+ 6 - 7
apps/emqx_web_hook/src/emqx_web_hook_actions.erl

@@ -354,12 +354,11 @@ pool_opts(Params = #{<<"url">> := URL}) ->
                                                  (_) ->
                                                   true
                                               end, [{keyfile, KeyFile}, {certfile, CertFile}, {cacertfile, CACertFile}]),
-                       TlsVers = ['tlsv1.2', 'tlsv1.1', tlsv1],
-                       NTLSOpts = [{verify, VerifyType},
-                                   {versions, TlsVers},
-                                   {ciphers, lists:foldl(fun(TlsVer, Ciphers) ->
-                                                             Ciphers ++ ssl:cipher_suites(all, TlsVer)
-                                                         end, [], TlsVers)} | TLSOpts],
+                       NTLSOpts = [ {verify, VerifyType}
+                                  , {versions, emqx_tls_lib:default_versions()}
+                                  , {ciphers, emqx_tls_lib:default_ciphers()}
+                                  | TLSOpts
+                                  ],
                        [{transport, ssl}, {transport_opts, [Inet | NTLSOpts]}]
               end,
     [{host, Host},
@@ -397,4 +396,4 @@ test_http_connect(Conf) ->
         Err:Reason:ST ->
            ?LOG(error, "check http_connectivity failed: ~p, ~0p", [Conf, {Err, Reason, ST}]),
            false
-    end.
+    end.

+ 6 - 7
apps/emqx_web_hook/src/emqx_web_hook_app.erl

@@ -75,12 +75,11 @@ translate_env() ->
                        TLSOpts = lists:filter(fun({_K, V}) ->
                                                 V /= <<>> andalso V /= undefined andalso V /= "" andalso true
                                               end, [{keyfile, KeyFile}, {certfile, CertFile}, {cacertfile, CACertFile}]),
-                       TlsVers = ['tlsv1.2','tlsv1.1',tlsv1],
-                       NTLSOpts = [{verify, VerifyType},
-                                   {versions, TlsVers},
-                                   {ciphers, lists:foldl(fun(TlsVer, Ciphers) ->
-                                                               Ciphers ++ ssl:cipher_suites(all, TlsVer)
-                                                           end, [], TlsVers)} | TLSOpts],
+                       NTLSOpts = [ {verify, VerifyType}
+                                  , {versions, emqx_tls_lib:default_versions()}
+                                  , {ciphers, emqx_tls_lib:default_ciphers()}
+                                  | TLSOpts
+                                  ],
                        [{transport, ssl}, {transport_opts, [Inet | NTLSOpts]}]
                 end,
     PoolOpts = [{host, Host},
@@ -114,4 +113,4 @@ parse_host(Host) ->
                 {ok, _} -> {inet6, Host};
                 {error, _} -> {inet, Host}
             end
-    end.
+    end.

+ 5 - 3
etc/emqx.conf

@@ -1317,7 +1317,8 @@ listener.ssl.external.access.1 = allow all
 ## See: http://erlang.org/doc/man/ssl.html
 ##
 ## Value: String, seperated by ','
-## listener.ssl.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1
+## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
+## listener.ssl.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
 
 ## TLS Handshake timeout.
 ##
@@ -1785,7 +1786,7 @@ listener.wss.external.access.1 = allow all
 ## Supported subprotocols
 ##
 ## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
-## listener.ws.external.supported_protocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
+## listener.wss.external.supported_protocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
 
 ## Enable the Proxy Protocol V1/2 support.
 ##
@@ -1806,7 +1807,8 @@ listener.wss.external.access.1 = allow all
 ## See: listener.ssl.$name.tls_versions
 ##
 ## Value: String, seperated by ','
-## listener.wss.external.tls_versions = tlsv1.2,tlsv1.1,tlsv1
+## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
+## listener.wss.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1
 
 ## Path to the file containing the user's private PEM-encoded key.
 ##

+ 6 - 6
src/emqx_tls_lib.erl

@@ -47,7 +47,7 @@ integral_versions(Desired) ->
 
 %% @doc Return a list of default (openssl string format) cipher suites.
 -spec default_ciphers() -> [string()].
-default_ciphers() -> default_ciphers(tls_versions()).
+default_ciphers() -> default_ciphers(default_versions()).
 
 %% @doc Return a list of (openssl string format) cipher suites.
 -spec default_ciphers([ssl:tls_version()]) -> [string()].
@@ -68,7 +68,7 @@ integral_ciphers(Versions, Ciphers) when Ciphers =:= [] orelse Ciphers =:= undef
     integral_ciphers(Versions, default_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_tls1_3_cipher(lists:member('tlsv1.3', Versions), Ciphers));
+    dedup(ensure_tls13_cipher(lists:member('tlsv1.3', Versions), Ciphers));
 integral_ciphers(Versions, Ciphers) when is_binary(Ciphers) ->
     %% parse binary
     integral_ciphers(Versions, binary_to_list(Ciphers));
@@ -78,20 +78,20 @@ 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_tls1_3_cipher(true, Ciphers) ->
+ensure_tls13_cipher(true, Ciphers) ->
     Tls13Ciphers = default_ciphers(['tlsv1.3']),
     case lists:any(fun(C) -> lists:member(C, Tls13Ciphers) end, Ciphers) of
         true  -> Ciphers;
         false -> Tls13Ciphers ++ Ciphers
     end;
-ensure_tls1_3_cipher(false, Ciphers) ->
+ensure_tls13_cipher(false, Ciphers) ->
     Ciphers.
 
 %% tlsv1.3 is available from OTP-22 but we do not want to use until 23.
 default_versions(OtpRelease) when OtpRelease >= 23 ->
-    ['tlsv1.3' | default_tls_versions(22)];
+    ['tlsv1.3' | default_versions(22)];
 default_versions(_) ->
-    ['tlsv1.2','tlsv1.1', tlsv1].
+    ['tlsv1.2', 'tlsv1.1', tlsv1].
 
 %% Deduplicate a list without re-ordering the elements.
 dedup([]) -> [];