From f000b6583c32de333bc7a9ffa647c2ddcc01a07c Mon Sep 17 00:00:00 2001 From: Zaiming Shi Date: Tue, 9 Feb 2021 10:49:35 +0100 Subject: [PATCH] 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) --- .../emqx_auth_http/src/emqx_auth_http_app.erl | 9 ++++---- apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf | 2 +- apps/emqx_bridge_mqtt/README.md | 2 +- apps/emqx_bridge_mqtt/docs/guide.rst | 8 +++---- .../etc/emqx_bridge_mqtt.conf | 1 + .../priv/emqx_bridge_mqtt.schema | 2 +- .../src/emqx_bridge_mqtt_actions.erl | 21 ++++++++----------- apps/emqx_coap/priv/emqx_coap.schema | 5 +---- apps/emqx_dashboard/etc/emqx_dashboard.conf | 3 ++- apps/emqx_exproto/test/emqx_exproto_SUITE.erl | 7 ++----- apps/emqx_management/etc/emqx_management.conf | 3 ++- apps/emqx_stomp/etc/emqx_stomp.conf | 3 ++- .../src/emqx_web_hook_actions.erl | 13 ++++++------ apps/emqx_web_hook/src/emqx_web_hook_app.erl | 13 ++++++------ etc/emqx.conf | 8 ++++--- src/emqx_tls_lib.erl | 12 +++++------ 16 files changed, 53 insertions(+), 59 deletions(-) diff --git a/apps/emqx_auth_http/src/emqx_auth_http_app.erl b/apps/emqx_auth_http/src/emqx_auth_http_app.erl index ba88ca3be..9b6b265d4 100644 --- a/apps/emqx_auth_http/src/emqx_auth_http_app.erl +++ b/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}, diff --git a/apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf b/apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf index ef8e7533a..b2eec355b 100644 --- a/apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf +++ b/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 diff --git a/apps/emqx_bridge_mqtt/README.md b/apps/emqx_bridge_mqtt/README.md index 6656aa36f..456fae584 100644 --- a/apps/emqx_bridge_mqtt/README.md +++ b/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/# diff --git a/apps/emqx_bridge_mqtt/docs/guide.rst b/apps/emqx_bridge_mqtt/docs/guide.rst index 73350ca1f..47235aa7d 100644 --- a/apps/emqx_bridge_mqtt/docs/guide.rst +++ b/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/# diff --git a/apps/emqx_bridge_mqtt/etc/emqx_bridge_mqtt.conf b/apps/emqx_bridge_mqtt/etc/emqx_bridge_mqtt.conf index ec5656af4..db59270c0 100644 --- a/apps/emqx_bridge_mqtt/etc/emqx_bridge_mqtt.conf +++ b/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 diff --git a/apps/emqx_bridge_mqtt/priv/emqx_bridge_mqtt.schema b/apps/emqx_bridge_mqtt/priv/emqx_bridge_mqtt.schema index 301737afd..3168bfc14 100644 --- a/apps/emqx_bridge_mqtt/priv/emqx_bridge_mqtt.schema +++ b/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", [ diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_actions.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_actions.erl index d63f20141..87bc6c694 100644 --- a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_actions.erl +++ b/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. diff --git a/apps/emqx_coap/priv/emqx_coap.schema b/apps/emqx_coap/priv/emqx_coap.schema index 465979964..da367f098 100644 --- a/apps/emqx_coap/priv/emqx_coap.schema +++ b/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, diff --git a/apps/emqx_dashboard/etc/emqx_dashboard.conf b/apps/emqx_dashboard/etc/emqx_dashboard.conf index 21dbbd04a..933b9885d 100644 --- a/apps/emqx_dashboard/etc/emqx_dashboard.conf +++ b/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..ciphers' in emq.conf ## diff --git a/apps/emqx_exproto/test/emqx_exproto_SUITE.erl b/apps/emqx_exproto/test/emqx_exproto_SUITE.erl index 2ad79b77d..c5cabe154 100644 --- a/apps/emqx_exproto/test/emqx_exproto_SUITE.erl +++ b/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 diff --git a/apps/emqx_management/etc/emqx_management.conf b/apps/emqx_management/etc/emqx_management.conf index 4bb3f83dc..aa737add9 100644 --- a/apps/emqx_management/etc/emqx_management.conf +++ b/apps/emqx_management/etc/emqx_management.conf @@ -45,7 +45,8 @@ management.listener.http.ipv6_v6only = false ## management.listener.https.keyfile = etc/certs/key.pem ## management.listener.https.cacertfile = etc/certs/cacert.pem ## management.listener.https.verify = verify_peer -## management.listener.https.tls_versions = tlsv1.2,tlsv1.1,tlsv1 +## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier +## management.listener.https.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 ## management.listener.https.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,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA ## management.listener.https.fail_if_no_peer_cert = true ## management.listener.https.inet6 = false diff --git a/apps/emqx_stomp/etc/emqx_stomp.conf b/apps/emqx_stomp/etc/emqx_stomp.conf index def48fbc8..b404e99ee 100644 --- a/apps/emqx_stomp/etc/emqx_stomp.conf +++ b/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. ## diff --git a/apps/emqx_web_hook/src/emqx_web_hook_actions.erl b/apps/emqx_web_hook/src/emqx_web_hook_actions.erl index bcd29bd28..f5d27f09c 100644 --- a/apps/emqx_web_hook/src/emqx_web_hook_actions.erl +++ b/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. \ No newline at end of file + end. diff --git a/apps/emqx_web_hook/src/emqx_web_hook_app.erl b/apps/emqx_web_hook/src/emqx_web_hook_app.erl index c8083e195..d31df9b93 100644 --- a/apps/emqx_web_hook/src/emqx_web_hook_app.erl +++ b/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. \ No newline at end of file + end. diff --git a/etc/emqx.conf b/etc/emqx.conf index cb8c27b16..33db7abb6 100644 --- a/etc/emqx.conf +++ b/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. ## diff --git a/src/emqx_tls_lib.erl b/src/emqx_tls_lib.erl index d78331af0..6153160e7 100644 --- a/src/emqx_tls_lib.erl +++ b/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([]) -> [];