Skip to content

Commit d2cd98a

Browse files
authored
Merge pull request #4468 from esl/MIM-2360_deprecate_fast_tls
MIM-2360 deprecate fast tls for C2S
2 parents d842d81 + 6842617 commit d2cd98a

File tree

10 files changed

+118
-24
lines changed

10 files changed

+118
-24
lines changed

big_tests/tests/login_SUITE.erl

+50-10
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,25 @@ all() ->
3838
{group, login_scram_store_plain},
3939
{group, login_specific_scram},
4040
{group, login_scram_tls},
41+
{group, fast_tls},
4142
{group, messages},
4243
{group, access}
4344
].
4445

4546
groups() ->
4647
[{login, [parallel], all_tests()},
4748
{login_digest, [sequence], digest_tests()},
49+
%% The SCRAM tests below have SCRAM_PLUS tests skipped
50+
%% because SCRAM_PLUS is not implemented for just_tls yet
4851
{login_scram, [parallel], scram_tests()},
4952
{login_scram_store_plain, [parallel], scram_tests()},
5053
{login_scram_tls, [parallel], scram_tests()},
5154
{login_specific_scram, [sequence], configure_specific_scram_test()},
55+
%% rerun SCRAM tests with fast_tls including SCRAM_PLUS tests
56+
{fast_tls, [{group, login_scram},
57+
{group, login_scram_store_plain},
58+
{group, login_scram_tls},
59+
{group, login_specific_scram}]},
5260
{messages, [sequence], [messages_story]},
5361
{access, [], access_tests()}].
5462

@@ -119,6 +127,8 @@ end_per_suite(Config) ->
119127
escalus_fresh:clean(),
120128
escalus:end_per_suite(Config).
121129

130+
init_per_group(fast_tls, ConfigIn) ->
131+
configure_c2s_listener_with_fast_tls(ConfigIn);
122132
init_per_group(login_digest = GroupName, ConfigIn) ->
123133
Config = backup_and_set_options(GroupName, ConfigIn),
124134
case mongoose_helper:supports_sasl_module(cyrsasl_digest) of
@@ -175,6 +185,8 @@ auth_opts(login_scram_store_plain) ->
175185
auth_opts(_GroupName) ->
176186
mongoose_helper:auth_opts_with_password_format(scram).
177187

188+
end_per_group(fast_tls, Config) ->
189+
restore_c2s(Config);
178190
end_per_group(login_digest, Config) ->
179191
mongoose_helper:restore_config(Config),
180192
escalus:delete_users(Config, escalus:get_users([alice, bob]));
@@ -258,12 +270,16 @@ log_one(Config) ->
258270
end).
259271

260272
log_one_scram_plus(Config) ->
261-
escalus:fresh_story(Config, [{neustradamus, 1}], fun(Neustradamus) ->
262-
263-
escalus_client:send(Neustradamus, escalus_stanza:chat_to(Neustradamus, <<"Hi!">>)),
264-
escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Neustradamus))
265-
266-
end).
273+
%% SCRAM PLUS tests are to be run for fast_tls only
274+
case proplists:get_value(fast_tls, Config) of
275+
true ->
276+
escalus:fresh_story(Config, [{neustradamus, 1}], fun(Neustradamus) ->
277+
escalus_client:send(Neustradamus, escalus_stanza:chat_to(Neustradamus, <<"Hi!">>)),
278+
escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Neustradamus))
279+
end);
280+
_ ->
281+
{skip, test_valid_only_for_fast_tls}
282+
end.
267283

268284
log_one_digest(Config) ->
269285
log_one([{escalus_auth_method, <<"DIGEST-MD5">>} | Config]).
@@ -427,10 +443,28 @@ configure_c2s_listener(Config) ->
427443
C2SPort = ct:get_config({hosts, mim, c2s_port}),
428444
[C2SListener = #{tls := TLSOpts}] =
429445
mongoose_helper:get_listeners(mim(), #{port => C2SPort, module => mongoose_c2s_listener}),
446+
%% If in fast_tls group, retrieve fast_tls options from config, otherwise use the ones from node.
447+
%% We do this, as in fast_tls group init we have already restarted c2s listener on node
448+
%% and there is a race condition in retrieving them from node,
449+
%% so it is better to take them from config
450+
TLSOpts1 = proplists:get_value(tls_opts, Config, TLSOpts),
430451
%% replace starttls with tls
431-
NewTLSOpts = TLSOpts#{mode := tls},
452+
NewTLSOpts = TLSOpts1#{mode := tls},
432453
mongoose_helper:restart_listener(mim(), C2SListener#{tls := NewTLSOpts}),
433-
[{c2s_listener, C2SListener} | Config].
454+
[{c2s_listener, C2SListener#{tls := TLSOpts1}} | Config].
455+
456+
configure_c2s_listener_with_fast_tls(Config) ->
457+
C2SPort = ct:get_config({hosts, mim, c2s_port}),
458+
[C2SListener = #{tls := _TLSOpts}] =
459+
mongoose_helper:get_listeners(mim(), #{port => C2SPort, module => mongoose_c2s_listener}),
460+
NewTLSOpts = #{module => fast_tls,mode => starttls,
461+
certfile => "priv/ssl/fake_server.pem",
462+
dhfile => "priv/ssl/fake_dh_server.pem",
463+
ciphers => "TLSv1.2:TLSv1.3",
464+
protocol_options => ["no_sslv2","no_sslv3","no_tlsv1","no_tlsv1_1"],
465+
verify_mode => none},
466+
mongoose_helper:restart_listener(mim(), C2SListener#{tls := NewTLSOpts}),
467+
[{c2s_listener, C2SListener}, {fast_tls, true}, {tls_opts, NewTLSOpts}| Config].
434468

435469
create_tls_users(Config) ->
436470
Config1 = escalus:create_users(Config, escalus:get_users([alice, neustradamus])),
@@ -517,8 +551,14 @@ configure_and_fail_log_scram(Config, Sha, Mech) ->
517551
{expected_challenge, _, _} = fail_log_one([{escalus_auth_method, Mech} | Config]).
518552

519553
configure_scram_plus_and_fail_log_scram(Config, Sha, Mech) ->
520-
set_scram_sha(Config, Sha),
521-
{expected_challenge, _, _} = fail_log_one_scram_plus([{escalus_auth_method, Mech} | Config]).
554+
%% SCRAM PLUS tests are to be run for fast_tls only
555+
case proplists:get_value(fast_tls, Config) of
556+
true ->
557+
set_scram_sha(Config, Sha),
558+
{expected_challenge, _, _} = fail_log_one_scram_plus([{escalus_auth_method, Mech} | Config]);
559+
_ ->
560+
{skip, test_valid_only_for_fast_tls}
561+
end.
522562

523563
set_scram_sha(Config, Sha) ->
524564
NewAuthOpts = mongoose_helper:auth_opts_with_password_format({scram, [Sha]}),

doc/configuration/TLS-hardening.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ The former one is used primarily by MIM dependencies, while the latter is used o
1616
None of them is strictly better than the other.
1717
Below you may find a summary of the differences between them.
1818

19-
* `fast_tls` is faster
19+
* `fast_tls` used to be faster, however with the progress of OTP TLS implementation
20+
and additional optimisations applied in MongooseIM this is no longer true.
21+
* `just_tls` may use slightly more processor time than `fast_tls`.
2022
* There are options that OTP TLS (a.k.a `just_tls` in the C2S listener configuration) supports exclusively:
2123
* Immediate connection drop when the client certificate is invalid
2224
* Certificate Revocation Lists
@@ -48,7 +50,7 @@ The remaining valid values are: `'tlsv1.1'`, `tlsv1`, `sslv3`.
4850

4951
This setting affects the following MongooseIM components:
5052

51-
* Raw XMPP over TCP connections, if a C2S listener is configured to use `just_tls`
53+
* Raw XMPP over TCP connections (C2S listener) in the default configuration uses `just_tls`
5254
* All outgoing connections (databases, AMQP, SIP etc.)
5355
* HTTP endpoints
5456

@@ -60,7 +62,8 @@ By default, MongooseIM sets this option to `TLSv1.2:TLSv1.3` for each component.
6062

6163
The list below enumerates all components that use Fast TLS and describes how to change this string.
6264

63-
* `listen.c2s` - main user session abstraction + XMPP over TCP listener
65+
* `listen.c2s` - main user session abstraction + XMPP over TCP listener, when configured to use `fast_tls`
66+
* Note that usage of `fast_tls` for C2S has been deprecated
6467
* Please consult the respective section in [Listener modules](../listeners/listen-c2s.md#listenc2stlsprotocol_options-only-for-fast_tls).
6568
* `listen.s2s` - incoming S2S connections (XMPP Federation)
6669
* Please consult the respective section in [Listener modules](../listeners/listen-s2s.md#tls-options-for-s2s).

doc/configuration/configuration-files.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ By default only the following applications can be found there:
6363

6464
TLS is configured in one of two ways: some modules need a private key and certificate (chain) in __separate__ files, while others need both in a __single__ file. This is because recent additions use OTP's `ssl` library, while older modules use `p1_tls`, respectively.
6565

66-
* Client-to-server connections need both in the __same__ `.pem` file
6766
* Server-to-server connections need both in the __same__ `.pem` file
67+
* Client-to-server connections need them in __separate__ files, unless `fast_tls` is used
6868
* BOSH, WebSockets and REST APIs need them in __separate__ files
6969

7070
In order to create private key & certificate bundle, you may simply concatenate them.

doc/listeners/listen-c2s.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,12 @@ This option determines how clients are supposed to set up the TLS encryption:
8989

9090
### `listen.c2s.tls.module`
9191
* **Syntax:** string, one of `"just_tls"`, `"fast_tls"`
92-
* **Default:** `"fast_tls"`
92+
* **Default:** `"just_tls"`
9393
* **Example:** `tls.module = "just_tls"`
9494

95-
By default, the TLS library used for C2S connections is `fast_tls`, which uses OpenSSL-based NIFs. It is possible to change it to `just_tls` - Erlang TLS implementation provided by OTP. Some TLS-related options described here have different formats for these two libraries.
95+
By default, the TLS library used for C2S connections is `just_tls` - Erlang TLS implementation provided by OTP.
96+
Usage of `fast_tls`, which uses OpenSSL-based NIFs for C2S is deprecated, however it is still possible to use this option.
97+
Some TLS-related options described here have different formats for these two libraries.
9698

9799
### `listen.c2s.tls.verify_mode`
98100
* **Syntax:** string, one of `"peer"`, `"selfsigned_peer"`, `"none"`
@@ -162,6 +164,16 @@ Password to the X509 PEM file with the private key.
162164
* **Default:** `true`
163165
* **Example:** `tls.disconnect_on_failure = false`
164166

167+
This option specifies what happens when client certificate is verified during TLS handshake.
168+
It therefore only applies when client certificate verification is enabled, that is `tls.verify_mode` is set to `"peer"` or `"selfsigned_peer"`.
169+
170+
When set to `true`, client verification is performed during TLS handshake and in case of error the connection is aborted.
171+
Additionally empty client certificate is treated as an error.
172+
173+
When set to `false`, TLS handshake will succeed even if there were errors in client certificate verification.
174+
This allows to use other methods of authentication (like SASL) later as part of XMPP stream.
175+
The above behaviour is the same as default `fast_tls` behaviour (not aborting TLS connection on verification errors).
176+
165177
### `listen.c2s.tls.versions` - only for `just_tls`
166178
* **Syntax:** array of strings
167179
* **Default:** not set, all supported versions are accepted

doc/migrations/6.3.1_6.x.y.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
## Change of the default TLS library used for C2S connections
2+
3+
As of this release, usage of `fast_tls` for Client to Server connections (C2S) has been deprecated.
4+
`fast_tls` will be removed in a future release.
5+
6+
From now on the default TLS library for C2S is `just_tls`, which uses TLS implementation from Erlang OTP.
7+
In our load tests `just_tls` is as performant as `fast_tls` and also has better standards compliance.
8+
9+
This deprecation affects only C2S, `fast_tls` remains as a TLS implementation for S2S.
10+
11+
To continue using `fast_tls` for C2S in existing deployment after upgrade, make sure the
12+
option `tls.module` is set to `fast_tls` in `listen.c2s` section of your MongooseIM config.
13+
14+
### Channel binding for TLS
15+
16+
Note that `just_tls` currently does not implement `channel binding` for TLS, which is required for SCRAM_PLUS
17+
authentication methods. If you depend on using SCRAM_PLUS for authentication, you need to use `fast_tls`.
18+
We do plan to implement `channel binding` for `just_tls` (only for TLS 1.3) in the future.
19+
20+
### TLS handshake
21+
22+
There is a difference between `fast_tls` and `just_tls` in client authentication behaviour during TLS handshake.
23+
24+
`fast_tls` doesn't verify client certificate during TLS handshake and relies on other mechanisms, like SASL,
25+
to authenticate client. It may involve client certificate, but is executed after TLS handshake succeeded,
26+
and in case of invalid certificate will result in an error reported in message stream.
27+
28+
`just_tls` by default verifies client certificate during TLS handshake
29+
and aborts connection when client certificate is invalid. This is realised by the default settings in
30+
`just_tls` of `verify_mode` set to `peer` and `disconnect_on_failure` set to `true`.
31+
32+
If you want to have the same behaviour for `just_tls` as it was in `fast_tls` regarding TLS handshake,
33+
set `tls.disconnect_on_failure` to `false`. This is required for example when using SASL for client authentication.
34+
35+
It is also possible to completely disable client certificate verification during TLS
36+
handshake in `just_tls` by setting `tls.verify_mode` to `none`.
37+
38+
For more information regarding configuration of TLS for C2S see [Listener modules](../listeners/listen-c2s/#tls-options-for-c2s)

rel/mim1.vars-toml.config

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
max_stanza_size = 65536
7171
tls.certfile = \"priv/ssl/fake_server.pem\"
7272
tls.cacertfile = \"priv/ssl/cacert.pem\"
73+
tls.disconnect_on_failure = false
7374
tls.mode = \"tls\""}.
7475
{listen_service,
7576
"[[listen.service]]

src/config/mongoose_config_spec.erl

+1-1
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ tls(c2s, common) ->
698698
validate = {enum, [fast_tls, just_tls]}},
699699
<<"mode">> => #option{type = atom,
700700
validate = {enum, [tls, starttls, starttls_required]}}},
701-
defaults = #{<<"module">> => fast_tls,
701+
defaults = #{<<"module">> => just_tls,
702702
<<"mode">> => starttls},
703703
process = fun ?MODULE:process_c2s_tls/1};
704704
tls(c2s, just_tls) ->

test/common/config_parser_helper.erl

+2-2
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ options("mongooseim-pgsql") ->
156156
access => c2s,
157157
shaper => c2s_shaper,
158158
max_stanza_size => 65536,
159-
tls => #{certfile => "priv/dc1.pem", dhfile => "priv/dh.pem",
159+
tls => #{certfile => "priv/cert.pem", keyfile => "priv/dc1.pem",
160160
cacertfile => "priv/ca.pem"}
161161
}),
162162
config([listen, c2s],
@@ -1166,7 +1166,7 @@ default_config([listen, c2s]) ->
11661166
access => all,
11671167
shaper => none};
11681168
default_config([listen, c2s, tls]) ->
1169-
default_c2s_tls(fast_tls);
1169+
default_c2s_tls(just_tls);
11701170
default_config([listen, s2s] = P) ->
11711171
(common_xmpp_listener_config())#{module => ejabberd_s2s_in,
11721172
shaper => none,

test/config_parser_SUITE.erl

+3-3
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,8 @@ listen_c2s(_Config) ->
508508

509509
listen_c2s_fast_tls(_Config) ->
510510
T = fun(Opts) -> listen_raw(c2s, #{<<"port">> => 5222,
511-
<<"tls">> => Opts}) end,
511+
<<"tls">> => maps:merge(
512+
#{<<"module">> => <<"fast_tls">>}, Opts)}) end,
512513
P = [listen, 1, tls],
513514
M = tls_ca_raw(),
514515
?cfg(P, maps:merge(default_c2s_tls(fast_tls), tls_ca()), T(M)),
@@ -525,7 +526,7 @@ listen_c2s_fast_tls(_Config) ->
525526

526527
listen_c2s_just_tls(_Config) ->
527528
T = fun(Opts) -> listen_raw(c2s, #{<<"port">> => 5222,
528-
<<"tls">> => Opts#{<<"module">> => <<"just_tls">>}}) end,
529+
<<"tls">> => Opts}) end,
529530
P = [listen, 1, tls],
530531
M = tls_ca_raw(),
531532
?cfg(P, maps:merge(default_c2s_tls(just_tls), tls_ca()), T(M)),
@@ -569,7 +570,6 @@ listen_s2s_cacertfile_verify(_Config) ->
569570
?err([#{reason := missing_cacertfile}], T(<<"required">>, #{})),
570571
?err([#{reason := missing_cacertfile}], T(<<"required_trusted">>, #{})),
571572
%% setting `verify_mode` to `none` turns off `cacertfile` validation
572-
VerifyModeNone = #{verify_mode => none},
573573
VerifyModeNoneRaw = #{<<"verify_mode">> => <<"none">>},
574574
ConfigWithVerifyModeNone = maps:merge(default_config([listen, s2s, tls]),
575575
#{verify_mode => none}),

test/config_parser_SUITE_data/mongooseim-pgsql.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@
8484
shaper = "c2s_shaper"
8585
max_stanza_size = 65536
8686
tls.mode = "starttls"
87-
tls.certfile = "priv/dc1.pem"
88-
tls.dhfile = "priv/dh.pem"
87+
tls.certfile = "priv/cert.pem"
88+
tls.keyfile = "priv/dc1.pem"
8989
tls.cacertfile = "priv/ca.pem"
9090

9191
[[listen.c2s]]

0 commit comments

Comments
 (0)