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
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},

View File

@ -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

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
## 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/#

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
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/#

View File

@ -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

View File

@ -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", [

View File

@ -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.

View File

@ -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,

View File

@ -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
##

View File

@ -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

View File

@ -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

View File

@ -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.
##

View File

@ -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.

View File

@ -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.

View File

@ -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.
##

View File

@ -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([]) -> [];