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)
This commit is contained in:
Zaiming Shi 2021-02-09 10:49:35 +01:00
parent 2852ac79d4
commit f000b6583c
16 changed files with 53 additions and 59 deletions

View File

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

View File

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

View File

@ -126,7 +126,7 @@ bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-S
bridge.mqtt.emqx2.keepalive = 60s bridge.mqtt.emqx2.keepalive = 60s
## Supported TLS version ## 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 ## Forwarding topics of the message
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/#

View File

@ -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 ## Key file of Client SSL connection
bridge.mqtt.emqx2.keyfile = etc/certs/client-key.pem 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 ## TTLS PSK password
## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time ## 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 bridge.mqtt.emqx2.keepalive = 60s
## Supported TLS version ## 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 ## Forwarding topics of the message
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/#

View File

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

View File

@ -90,7 +90,7 @@
{mapping, "bridge.mqtt.$name.tls_versions", "emqx_bridge_mqtt.bridges", [ {mapping, "bridge.mqtt.$name.tls_versions", "emqx_bridge_mqtt.bridges", [
{datatype, string}, {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", [ {mapping, "bridge.mqtt.$name.reconnect_interval", "emqx_bridge_mqtt.bridges", [

View File

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

View File

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

View File

@ -105,7 +105,8 @@ dashboard.listener.http.ipv6_v6only = false
## TLS versions only to protect from POODLE attack. ## TLS versions only to protect from POODLE attack.
## ##
## Value: String, seperated by ',' ## 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 ## See: 'listener.ssl.<name>.ciphers' in emq.conf
## ##

View File

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

View File

@ -45,7 +45,8 @@ management.listener.http.ipv6_v6only = false
## management.listener.https.keyfile = etc/certs/key.pem ## management.listener.https.keyfile = etc/certs/key.pem
## management.listener.https.cacertfile = etc/certs/cacert.pem ## management.listener.https.cacertfile = etc/certs/cacert.pem
## management.listener.https.verify = verify_peer ## 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.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.fail_if_no_peer_cert = true
## management.listener.https.inet6 = false ## management.listener.https.inet6 = false

View File

@ -58,7 +58,8 @@ stomp.listener.max_connections = 512
## TLS versions only to protect from POODLE attack. ## TLS versions only to protect from POODLE attack.
## ##
## Value: String, seperated by ',' ## 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. ## SSL Handshake timeout.
## ##

View File

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

View File

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

View File

@ -1317,7 +1317,8 @@ listener.ssl.external.access.1 = allow all
## See: http://erlang.org/doc/man/ssl.html ## See: http://erlang.org/doc/man/ssl.html
## ##
## Value: String, seperated by ',' ## 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. ## TLS Handshake timeout.
## ##
@ -1785,7 +1786,7 @@ listener.wss.external.access.1 = allow all
## Supported subprotocols ## Supported subprotocols
## ##
## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 ## 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. ## Enable the Proxy Protocol V1/2 support.
## ##
@ -1806,7 +1807,8 @@ listener.wss.external.access.1 = allow all
## See: listener.ssl.$name.tls_versions ## See: listener.ssl.$name.tls_versions
## ##
## Value: String, seperated by ',' ## 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. ## Path to the file containing the user's private PEM-encoded key.
## ##

View File

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