From baf46c9aa2895ca7b00d2df6c535b0ad3f67bd0f Mon Sep 17 00:00:00 2001 From: Andrew Mayorov Date: Tue, 19 Dec 2023 22:48:38 +0100 Subject: [PATCH] test(listen): also change CA on update and verify clients notice --- apps/emqx/test/emqx_listeners_SUITE.erl | 126 +++++++++++++++++------- 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/apps/emqx/test/emqx_listeners_SUITE.erl b/apps/emqx/test/emqx_listeners_SUITE.erl index 203a30cde..1dab8f4c4 100644 --- a/apps/emqx/test/emqx_listeners_SUITE.erl +++ b/apps/emqx/test/emqx_listeners_SUITE.erl @@ -188,38 +188,58 @@ t_ssl_password_cert(Config) -> t_ssl_update_opts(Config) -> PrivDir = ?config(priv_dir, Config), - CACertfile = filename:join(PrivDir, "ca.pem"), Host = "127.0.0.1", Port = emqx_common_test_helpers:select_free_port(ssl), Conf = #{ <<"enable">> => true, <<"bind">> => format_bind({Host, Port}), <<"ssl_options">> => #{ - <<"cacertfile">> => CACertfile, - <<"certfile">> => filename:join(PrivDir, "server.pem"), - <<"keyfile">> => filename:join(PrivDir, "server.key"), + <<"cacertfile">> => filename:join(PrivDir, "ca.pem"), + <<"password">> => ?SERVER_KEY_PASSWORD, + <<"certfile">> => filename:join(PrivDir, "server-password.pem"), + <<"keyfile">> => filename:join(PrivDir, "server-password.key"), <<"verify">> => verify_none } }, ClientSSLOpts = [ {verify, verify_peer}, - {cacertfile, CACertfile}, {customize_hostname_check, [{match_fun, fun(_, _) -> true end}]} ], with_listener(ssl, updated, Conf, fun() -> - C1 = emqtt_connect_ssl(Host, Port, ClientSSLOpts), + %% Client connects successfully. + C1 = emqtt_connect_ssl(Host, Port, [ + {cacertfile, filename:join(PrivDir, "ca.pem")} | ClientSSLOpts + ]), - %% Change the listener SSL configuration. - %% 1. Another set of (password protected) cert/key files. - %% 2. Require peer certificate. + %% Change the listener SSL configuration: another set of cert/key files. + {ok, _} = emqx:update_config( + [listeners, ssl, updated], + {update, #{ + <<"ssl_options">> => #{ + <<"cacertfile">> => filename:join(PrivDir, "ca-next.pem"), + <<"certfile">> => filename:join(PrivDir, "server.pem"), + <<"keyfile">> => filename:join(PrivDir, "server.key") + } + }} + ), + + %% Unable to connect with old SSL options, server's cert is signed by another CA. + ?assertError( + {tls_alert, {unknown_ca, _}}, + emqtt_connect_ssl(Host, Port, [ + {cacertfile, filename:join(PrivDir, "ca.pem")} | ClientSSLOpts + ]) + ), + + C2 = emqtt_connect_ssl(Host, Port, [ + {cacertfile, filename:join(PrivDir, "ca-next.pem")} | ClientSSLOpts + ]), + + %% Change the listener SSL configuration: require peer certificate. {ok, _} = emqx:update_config( [listeners, ssl, updated], {update, #{ <<"ssl_options">> => #{ - <<"password">> => ?SERVER_KEY_PASSWORD, - <<"cacertfile">> => CACertfile, - <<"certfile">> => filename:join(PrivDir, "server-password.pem"), - <<"keyfile">> => filename:join(PrivDir, "server-password.key"), <<"verify">> => verify_peer, <<"fail_if_no_peer_cert">> => true } @@ -229,10 +249,13 @@ t_ssl_update_opts(Config) -> %% Unable to connect with old SSL options, certificate is now required. ?assertError( {ssl_error, _Socket, {tls_alert, {certificate_required, _}}}, - emqtt_connect_ssl(Host, Port, ClientSSLOpts) + emqtt_connect_ssl(Host, Port, [ + {cacertfile, filename:join(PrivDir, "ca-next.pem")} | ClientSSLOpts + ]) ), - C2 = emqtt_connect_ssl(Host, Port, [ + C3 = emqtt_connect_ssl(Host, Port, [ + {cacertfile, filename:join(PrivDir, "ca-next.pem")}, {certfile, filename:join(PrivDir, "client.pem")}, {keyfile, filename:join(PrivDir, "client.key")} | ClientSSLOpts @@ -241,38 +264,38 @@ t_ssl_update_opts(Config) -> %% Both pre- and post-update clients should be alive. ?assertEqual(pong, emqtt:ping(C1)), ?assertEqual(pong, emqtt:ping(C2)), - %% TODO - %% Would be nice to verify that the server picked up new cert/key pair. - %% However, there's no usable API for that in `emqtt` at the moment. - %% ?assertEqual(<<"server">>, esockd_peercert:common_name(Cert1)), - %% ?assertEqual(<<"server-password">>, esockd_peercert:common_name(Cert2)), + ?assertEqual(pong, emqtt:ping(C3)), + ok = emqtt:stop(C1), - ok = emqtt:stop(C2) + ok = emqtt:stop(C2), + ok = emqtt:stop(C3) end). t_wss_update_opts(Config) -> PrivDir = ?config(priv_dir, Config), - CACertfile = filename:join(PrivDir, "ca.pem"), Host = "127.0.0.1", Port = emqx_common_test_helpers:select_free_port(ssl), Conf = #{ <<"enable">> => true, <<"bind">> => format_bind({Host, Port}), <<"ssl_options">> => #{ - <<"cacertfile">> => CACertfile, - <<"certfile">> => filename:join(PrivDir, "server.pem"), - <<"keyfile">> => filename:join(PrivDir, "server.key"), + <<"cacertfile">> => filename:join(PrivDir, "ca.pem"), + <<"certfile">> => filename:join(PrivDir, "server-password.pem"), + <<"keyfile">> => filename:join(PrivDir, "server-password.key"), + <<"password">> => ?SERVER_KEY_PASSWORD, <<"verify">> => verify_none } }, ClientSSLOpts = [ {verify, verify_peer}, - {cacertfile, CACertfile}, {customize_hostname_check, [{match_fun, fun(_, _) -> true end}]} ], with_listener(wss, updated, Conf, fun() -> %% Start a client. - C1 = emqtt_connect_wss(Host, Port, ClientSSLOpts), + C1 = emqtt_connect_wss(Host, Port, [ + {cacertfile, filename:join(PrivDir, "ca.pem")} + | ClientSSLOpts + ]), %% Change the listener SSL configuration. %% 1. Another set of (password protected) cert/key files. @@ -281,10 +304,30 @@ t_wss_update_opts(Config) -> [listeners, wss, updated], {update, #{ <<"ssl_options">> => #{ - <<"password">> => ?SERVER_KEY_PASSWORD, - <<"cacertfile">> => CACertfile, - <<"certfile">> => filename:join(PrivDir, "server-password.pem"), - <<"keyfile">> => filename:join(PrivDir, "server-password.key"), + <<"cacertfile">> => filename:join(PrivDir, "ca-next.pem"), + <<"certfile">> => filename:join(PrivDir, "server.pem"), + <<"keyfile">> => filename:join(PrivDir, "server.key") + } + }} + ), + + %% Unable to connect with old SSL options, server's cert is signed by another CA. + %% Due to a bug `emqtt` exits with `badmatch` in this case. + ?assertExit( + _Badmatch, + emqtt_connect_wss(Host, Port, ClientSSLOpts) + ), + + C2 = emqtt_connect_wss(Host, Port, [ + {cacertfile, filename:join(PrivDir, "ca-next.pem")} + | ClientSSLOpts + ]), + + %% Change the listener SSL configuration: require peer certificate. + {ok, _} = emqx:update_config( + [listeners, wss, updated], + {update, #{ + <<"ssl_options">> => #{ <<"verify">> => verify_peer, <<"fail_if_no_peer_cert">> => true } @@ -292,14 +335,17 @@ t_wss_update_opts(Config) -> ), %% Unable to connect with old SSL options, certificate is now required. + %% Due to a bug `emqtt` does not instantly report that socket was closed. ?assertError( - %% Due to a bug `emqtt` does not instantly report that socket was closed. timeout, - emqtt_connect_wss(Host, Port, ClientSSLOpts) + emqtt_connect_wss(Host, Port, [ + {cacertfile, filename:join(PrivDir, "ca-next.pem")} + | ClientSSLOpts + ]) ), - % C2 = emqx_cth_mqttws:connect(Host, Port, [ - C2 = emqtt_connect_wss(Host, Port, [ + C3 = emqtt_connect_wss(Host, Port, [ + {cacertfile, filename:join(PrivDir, "ca-next.pem")}, {certfile, filename:join(PrivDir, "client.pem")}, {keyfile, filename:join(PrivDir, "client.key")} | ClientSSLOpts @@ -308,8 +354,11 @@ t_wss_update_opts(Config) -> %% Both pre- and post-update clients should be alive. ?assertEqual(pong, emqtt:ping(C1)), ?assertEqual(pong, emqtt:ping(C2)), + ?assertEqual(pong, emqtt:ping(C3)), + ok = emqtt:stop(C1), - ok = emqtt:stop(C2) + ok = emqtt:stop(C2), + ok = emqtt:stop(C3) end). with_listener(Type, Name, Config, Then) -> @@ -380,8 +429,9 @@ t_format_bind(_) -> generate_tls_certs(Config) -> PrivDir = ?config(priv_dir, Config), emqx_common_test_helpers:gen_ca(PrivDir, "ca"), - emqx_common_test_helpers:gen_host_cert("server", "ca", PrivDir, #{}), - emqx_common_test_helpers:gen_host_cert("client", "ca", PrivDir, #{}), + emqx_common_test_helpers:gen_ca(PrivDir, "ca-next"), + emqx_common_test_helpers:gen_host_cert("server", "ca-next", PrivDir, #{}), + emqx_common_test_helpers:gen_host_cert("client", "ca-next", PrivDir, #{}), emqx_common_test_helpers:gen_host_cert("server-password", "ca", PrivDir, #{ password => ?SERVER_KEY_PASSWORD }).