From cf1d98adc60da93d5dc61c13e0112e27ba881d08 Mon Sep 17 00:00:00 2001 From: JianBo He Date: Tue, 26 Oct 2021 16:40:19 +0800 Subject: [PATCH] chore(gw): save cert files --- apps/emqx_gateway/src/emqx_gateway_conf.erl | 82 +++++- apps/emqx_gateway/src/emqx_gateway_schema.erl | 8 +- .../test/emqx_gateway_conf_SUITE.erl | 241 +++++++++++++++++- .../test/emqx_gateway_test_utils.erl | 33 ++- 4 files changed, 343 insertions(+), 21 deletions(-) diff --git a/apps/emqx_gateway/src/emqx_gateway_conf.erl b/apps/emqx_gateway/src/emqx_gateway_conf.erl index 71b517730..b5417b0d6 100644 --- a/apps/emqx_gateway/src/emqx_gateway_conf.erl +++ b/apps/emqx_gateway/src/emqx_gateway_conf.erl @@ -17,6 +17,8 @@ %% @doc The gateway configuration management module -module(emqx_gateway_conf). +-include_lib("emqx/include/logger.hrl"). + %% Load/Unload -export([ load/0 , unload/0 @@ -107,6 +109,8 @@ update_gateway(GwName, Conf0) -> <<"listeners">>, <<"authentication">>], Conf0), update({?FUNCTION_NAME, bin(GwName), Conf}). +%% FIXME: delete cert files ?? + -spec unload_gateway(atom_or_bin()) -> ok_or_err(). unload_gateway(GwName) -> update({?FUNCTION_NAME, bin(GwName)}). @@ -247,7 +251,8 @@ bin(B) when is_binary(B) -> pre_config_update({load_gateway, GwName, Conf}, RawConf) -> case maps:get(GwName, RawConf, undefined) of undefined -> - {ok, emqx_map_lib:deep_merge(RawConf, #{GwName => Conf})}; + NConf = tune_gw_certs(fun convert_certs/2, GwName, Conf), + {ok, emqx_map_lib:deep_merge(RawConf, #{GwName => NConf})}; _ -> {error, already_exist} end; @@ -261,13 +266,18 @@ pre_config_update({update_gateway, GwName, Conf}, RawConf) -> {ok, emqx_map_lib:deep_merge(RawConf, #{GwName => NConf})} end; pre_config_update({unload_gateway, GwName}, RawConf) -> + _ = tune_gw_certs(fun clear_certs/2, + GwName, + maps:get(GwName, RawConf, #{}) + ), {ok, maps:remove(GwName, RawConf)}; pre_config_update({add_listener, GwName, {LType, LName}, Conf}, RawConf) -> case emqx_map_lib:deep_get( [GwName, <<"listeners">>, LType, LName], RawConf, undefined) of undefined -> - NListener = #{LType => #{LName => Conf}}, + NConf = convert_certs(certs_dir(GwName), Conf), + NListener = #{LType => #{LName => NConf}}, {ok, emqx_map_lib:deep_merge( RawConf, #{GwName => #{<<"listeners">> => NListener}})}; @@ -279,16 +289,23 @@ pre_config_update({update_listener, GwName, {LType, LName}, Conf}, RawConf) -> [GwName, <<"listeners">>, LType, LName], RawConf, undefined) of undefined -> {error, not_found}; - _OldConf -> - NListener = #{LType => #{LName => Conf}}, + OldConf -> + NConf = convert_certs(certs_dir(GwName), Conf, OldConf), + NListener = #{LType => #{LName => NConf}}, {ok, emqx_map_lib:deep_merge( RawConf, #{GwName => #{<<"listeners">> => NListener}})} end; pre_config_update({remove_listener, GwName, {LType, LName}}, RawConf) -> - {ok, emqx_map_lib:deep_remove( - [GwName, <<"listeners">>, LType, LName], RawConf)}; + Path = [GwName, <<"listeners">>, LType, LName], + case emqx_map_lib:deep_get(Path, RawConf, undefined) of + undefined -> + {ok, RawConf}; + OldConf -> + clear_certs(certs_dir(GwName), OldConf), + {ok, emqx_map_lib:deep_remove(Path, RawConf)} + end; pre_config_update({add_authn, GwName, Conf}, RawConf) -> case emqx_map_lib:deep_get( @@ -382,3 +399,56 @@ post_config_update(Req, NewConfig, OldConfig, _AppEnvs) when is_tuple(Req) -> end; post_config_update(_Req, _NewConfig, _OldConfig, _AppEnvs) -> ok. + +%%-------------------------------------------------------------------- +%% Internal funcs +%%-------------------------------------------------------------------- + + +tune_gw_certs(Fun, GwName, Conf) -> + SubDir = certs_dir(GwName), + case maps:get(<<"listeners">>, Conf, undefined) of + undefined -> Conf; + Liss -> + maps:put(<<"listeners">>, + maps:map(fun(_, Lis) -> + maps:map(fun(_, LisConf) -> + erlang:apply(Fun, [SubDir, LisConf]) + end, Lis) + end, Liss), + Conf) + end. + +certs_dir(GwName) when is_binary(GwName) -> + GwName. + +convert_certs(SubDir, Conf) -> + case emqx_tls_lib:ensure_ssl_files( + SubDir, + maps:get(<<"ssl">>, Conf, undefined) + ) of + {ok, SSL} -> + new_ssl_config(Conf, SSL); + {error, Reason} -> + ?SLOG(error, Reason#{msg => bad_ssl_config}), + throw({bad_ssl_config, Reason}) + end. + +convert_certs(SubDir, NConf, OConf) -> + OSSL = maps:get(<<"ssl">>, OConf, undefined), + NSSL = maps:get(<<"ssl">>, NConf, undefined), + case emqx_tls_lib:ensure_ssl_files(SubDir, NSSL) of + {ok, NSSL1} -> + ok = emqx_tls_lib:delete_ssl_files(SubDir, NSSL1, OSSL), + new_ssl_config(NConf, NSSL1); + {error, Reason} -> + ?SLOG(error, Reason#{msg => bad_ssl_config}), + throw({bad_ssl_config, Reason}) + end. + +new_ssl_config(Conf, undefined) -> Conf; +new_ssl_config(Conf, SSL) -> Conf#{<<"ssl">> => SSL}. + +clear_certs(SubDir, Conf) -> + SSL = maps:get(<<"ssl">>, Conf, undefined), + ok = emqx_tls_lib:delete_ssl_files(SubDir, undefined, SSL). diff --git a/apps/emqx_gateway/src/emqx_gateway_schema.erl b/apps/emqx_gateway/src/emqx_gateway_schema.erl index bdc3bf659..71fac2743 100644 --- a/apps/emqx_gateway/src/emqx_gateway_schema.erl +++ b/apps/emqx_gateway/src/emqx_gateway_schema.erl @@ -340,7 +340,7 @@ After succeed observe a resource of LwM2M client, Gateway will send the notifyev fields(translator) -> [ {topic, sc(binary())} - , {qos, sc(range(0, 2), 0)} + , {qos, sc(range(0, 2), #{default => 0})} ]; fields(udp_listeners) -> @@ -362,7 +362,7 @@ fields(udp_tcp_listeners) -> fields(tcp_listener) -> [ %% some special confs for tcp listener - {acceptors, sc(integer(), 16)} + {acceptors, sc(integer(), #{default => 16})} ] ++ tcp_opts() ++ proxy_protocol_opts() ++ @@ -390,11 +390,11 @@ fields(dtls_listener) -> [{dtls, sc(ref(dtls_opts), #{desc => "DTLS listener options"})}]; fields(udp_opts) -> - [ {active_n, sc(integer(), 100)} + [ {active_n, sc(integer(), #{default => 100})} , {recbuf, sc(bytesize())} , {sndbuf, sc(bytesize())} , {buffer, sc(bytesize())} - , {reuseaddr, sc(boolean(), true)} + , {reuseaddr, sc(boolean(), #{default => true})} ]; fields(dtls_opts) -> diff --git a/apps/emqx_gateway/test/emqx_gateway_conf_SUITE.erl b/apps/emqx_gateway/test/emqx_gateway_conf_SUITE.erl index 58af7c91e..15dded923 100644 --- a/apps/emqx_gateway/test/emqx_gateway_conf_SUITE.erl +++ b/apps/emqx_gateway/test/emqx_gateway_conf_SUITE.erl @@ -49,6 +49,133 @@ init_per_testcase(_CaseName, Conf) -> %% Cases %%-------------------------------------------------------------------- +-define(SVR_CA, +<<"-----BEGIN CERTIFICATE----- +MIIDUTCCAjmgAwIBAgIJAPPYCjTmxdt/MA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV +BAYTAkNOMREwDwYDVQQIDAhoYW5nemhvdTEMMAoGA1UECgwDRU1RMQ8wDQYDVQQD +DAZSb290Q0EwHhcNMjAwNTA4MDgwNjUyWhcNMzAwNTA2MDgwNjUyWjA/MQswCQYD +VQQGEwJDTjERMA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UE +AwwGUm9vdENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzcgVLex1 +EZ9ON64EX8v+wcSjzOZpiEOsAOuSXOEN3wb8FKUxCdsGrsJYB7a5VM/Jot25Mod2 +juS3OBMg6r85k2TWjdxUoUs+HiUB/pP/ARaaW6VntpAEokpij/przWMPgJnBF3Ur +MjtbLayH9hGmpQrI5c2vmHQ2reRZnSFbY+2b8SXZ+3lZZgz9+BaQYWdQWfaUWEHZ +uDaNiViVO0OT8DRjCuiDp3yYDj3iLWbTA/gDL6Tf5XuHuEwcOQUrd+h0hyIphO8D +tsrsHZ14j4AWYLk1CPA6pq1HIUvEl2rANx2lVUNv+nt64K/Mr3RnVQd9s8bK+TXQ +KGHd2Lv/PALYuwIDAQABo1AwTjAdBgNVHQ4EFgQUGBmW+iDzxctWAWxmhgdlE8Pj +EbQwHwYDVR0jBBgwFoAUGBmW+iDzxctWAWxmhgdlE8PjEbQwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAQEAGbhRUjpIred4cFAFJ7bbYD9hKu/yzWPWkMRa +ErlCKHmuYsYk+5d16JQhJaFy6MGXfLgo3KV2itl0d+OWNH0U9ULXcglTxy6+njo5 +CFqdUBPwN1jxhzo9yteDMKF4+AHIxbvCAJa17qcwUKR5MKNvv09C6pvQDJLzid7y +E2dkgSuggik3oa0427KvctFf8uhOV94RvEDyqvT5+pgNYZ2Yfga9pD/jjpoHEUlo +88IGU8/wJCx3Ds2yc8+oBg/ynxG8f/HmCC1ET6EHHoe2jlo8FpU/SgGtghS1YL30 +IWxNsPrUP+XsZpBJy/mvOhE5QXo6Y35zDqqj8tI7AGmAWu22jg== +-----END CERTIFICATE----- +">>). + +-define(SVR_CERT, +<<"-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIBAjANBgkqhkiG9w0BAQsFADA/MQswCQYDVQQGEwJDTjER +MA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UEAwwGUm9vdENB +MB4XDTIwMDUwODA4MDcwNVoXDTMwMDUwNjA4MDcwNVowPzELMAkGA1UEBhMCQ04x +ETAPBgNVBAgMCGhhbmd6aG91MQwwCgYDVQQKDANFTVExDzANBgNVBAMMBlNlcnZl +cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALNeWT3pE+QFfiRJzKmn +AMUrWo3K2j/Tm3+Xnl6WLz67/0rcYrJbbKvS3uyRP/stXyXEKw9CepyQ1ViBVFkW +Aoy8qQEOWFDsZc/5UzhXUnb6LXr3qTkFEjNmhj+7uzv/lbBxlUG1NlYzSeOB6/RT +8zH/lhOeKhLnWYPXdXKsa1FL6ij4X8DeDO1kY7fvAGmBn/THh1uTpDizM4YmeI+7 +4dmayA5xXvARte5h4Vu5SIze7iC057N+vymToMk2Jgk+ZZFpyXrnq+yo6RaD3ANc +lrc4FbeUQZ5a5s5Sxgs9a0Y3WMG+7c5VnVXcbjBRz/aq2NtOnQQjikKKQA8GF080 +BQkCAwEAAaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL +BQADggEBAJefnMZpaRDHQSNUIEL3iwGXE9c6PmIsQVE2ustr+CakBp3TZ4l0enLt +iGMfEVFju69cO4oyokWv+hl5eCMkHBf14Kv51vj448jowYnF1zmzn7SEzm5Uzlsa +sqjtAprnLyof69WtLU1j5rYWBuFX86yOTwRAFNjm9fvhAcrEONBsQtqipBWkMROp +iUYMkRqbKcQMdwxov+lHBYKq9zbWRoqLROAn54SRqgQk6c15JdEfgOOjShbsOkIH +UhqcwRkQic7n1zwHVGVDgNIZVgmJ2IdIWBlPEC7oLrRrBD/X1iEEXtKab6p5o22n +KB5mN+iQaE+Oe2cpGKZJiJRdM+IqDDQ= +-----END CERTIFICATE----- +">>). + +-define(SVR_KEY, +<<"-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAs15ZPekT5AV+JEnMqacAxStajcraP9Obf5eeXpYvPrv/Stxi +sltsq9Le7JE/+y1fJcQrD0J6nJDVWIFUWRYCjLypAQ5YUOxlz/lTOFdSdvotevep +OQUSM2aGP7u7O/+VsHGVQbU2VjNJ44Hr9FPzMf+WE54qEudZg9d1cqxrUUvqKPhf +wN4M7WRjt+8AaYGf9MeHW5OkOLMzhiZ4j7vh2ZrIDnFe8BG17mHhW7lIjN7uILTn +s36/KZOgyTYmCT5lkWnJeuer7KjpFoPcA1yWtzgVt5RBnlrmzlLGCz1rRjdYwb7t +zlWdVdxuMFHP9qrY206dBCOKQopADwYXTzQFCQIDAQABAoIBAQCuvCbr7Pd3lvI/ +n7VFQG+7pHRe1VKwAxDkx2t8cYos7y/QWcm8Ptwqtw58HzPZGWYrgGMCRpzzkRSF +V9g3wP1S5Scu5C6dBu5YIGc157tqNGXB+SpdZddJQ4Nc6yGHXYERllT04ffBGc3N +WG/oYS/1cSteiSIrsDy/91FvGRCi7FPxH3wIgHssY/tw69s1Cfvaq5lr2NTFzxIG +xCvpJKEdSfVfS9I7LYiymVjst3IOR/w76/ZFY9cRa8ZtmQSWWsm0TUpRC1jdcbkm +ZoJptYWlP+gSwx/fpMYftrkJFGOJhHJHQhwxT5X/ajAISeqjjwkWSEJLwnHQd11C +Zy2+29lBAoGBANlEAIK4VxCqyPXNKfoOOi5dS64NfvyH4A1v2+KaHWc7lqaqPN49 +ezfN2n3X+KWx4cviDD914Yc2JQ1vVJjSaHci7yivocDo2OfZDmjBqzaMp/y+rX1R +/f3MmiTqMa468rjaxI9RRZu7vDgpTR+za1+OBCgMzjvAng8dJuN/5gjlAoGBANNY +uYPKtearBmkqdrSV7eTUe49Nhr0XotLaVBH37TCW0Xv9wjO2xmbm5Ga/DCtPIsBb +yPeYwX9FjoasuadUD7hRvbFu6dBa0HGLmkXRJZTcD7MEX2Lhu4BuC72yDLLFd0r+ +Ep9WP7F5iJyagYqIZtz+4uf7gBvUDdmvXz3sGr1VAoGAdXTD6eeKeiI6PlhKBztF +zOb3EQOO0SsLv3fnodu7ZaHbUgLaoTMPuB17r2jgrYM7FKQCBxTNdfGZmmfDjlLB +0xZ5wL8ibU30ZXL8zTlWPElST9sto4B+FYVVF/vcG9sWeUUb2ncPcJ/Po3UAktDG +jYQTTyuNGtSJHpad/YOZctkCgYBtWRaC7bq3of0rJGFOhdQT9SwItN/lrfj8hyHA +OjpqTV4NfPmhsAtu6j96OZaeQc+FHvgXwt06cE6Rt4RG4uNPRluTFgO7XYFDfitP +vCppnoIw6S5BBvHwPP+uIhUX2bsi/dm8vu8tb+gSvo4PkwtFhEr6I9HglBKmcmog +q6waEQKBgHyecFBeM6Ls11Cd64vborwJPAuxIW7HBAFj/BS99oeG4TjBx4Sz2dFd +rzUibJt4ndnHIvCN8JQkjNG14i9hJln+H3mRss8fbZ9vQdqG+2vOWADYSzzsNI55 +RFY7JjluKcVkp/zCDeUxTU3O6sS+v6/3VE11Cob6OYQx3lN5wrZ3 +-----END RSA PRIVATE KEY----- +">>). + +-define(SVR_CERT2, +<<"-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MQswCQYDVQQGEwJDTjER +MA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UEAwwGUm9vdENB +MB4XDTIwMDUwODA4MDY1N1oXDTMwMDUwNjA4MDY1N1owPzELMAkGA1UEBhMCQ04x +ETAPBgNVBAgMCGhhbmd6aG91MQwwCgYDVQQKDANFTVExDzANBgNVBAMMBkNsaWVu +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMy4hoksKcZBDbY680u6 +TS25U51nuB1FBcGMlF9B/t057wPOlxF/OcmbxY5MwepS41JDGPgulE1V7fpsXkiW +1LUimYV/tsqBfymIe0mlY7oORahKji7zKQ2UBIVFhdlvQxunlIDnw6F9popUgyHt +dMhtlgZK8oqRwHxO5dbfoukYd6J/r+etS5q26sgVkf3C6dt0Td7B25H9qW+f7oLV +PbcHYCa+i73u9670nrpXsC+Qc7Mygwa2Kq/jwU+ftyLQnOeW07DuzOwsziC/fQZa +nbxR+8U9FNftgRcC3uP/JMKYUqsiRAuaDokARZxVTV5hUElfpO6z6/NItSDvvh3i +eikCAwEAAaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL +BQADggEBABchYxKo0YMma7g1qDswJXsR5s56Czx/I+B41YcpMBMTrRqpUC0nHtLk +M7/tZp592u/tT8gzEnQjZLKBAhFeZaR3aaKyknLqwiPqJIgg0pgsBGITrAK3Pv4z +5/YvAJJKgTe5UdeTz6U4lvNEux/4juZ4pmqH4qSFJTOzQS7LmgSmNIdd072rwXBd +UzcSHzsJgEMb88u/LDLjj1pQ7AtZ4Tta8JZTvcgBFmjB0QUi6fgkHY6oGat/W4kR +jSRUBlMUbM/drr2PVzRc2dwbFIl3X+ZE6n5Sl3ZwRAC/s92JU6CPMRW02muVu6xl +goraNgPISnrbpR6KjxLZkVembXzjNNc= +-----END CERTIFICATE----- +">>). + +-define(SVR_KEY2, +<<"-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAzLiGiSwpxkENtjrzS7pNLblTnWe4HUUFwYyUX0H+3TnvA86X +EX85yZvFjkzB6lLjUkMY+C6UTVXt+mxeSJbUtSKZhX+2yoF/KYh7SaVjug5FqEqO +LvMpDZQEhUWF2W9DG6eUgOfDoX2milSDIe10yG2WBkryipHAfE7l1t+i6Rh3on+v +561LmrbqyBWR/cLp23RN3sHbkf2pb5/ugtU9twdgJr6Lve73rvSeulewL5BzszKD +BrYqr+PBT5+3ItCc55bTsO7M7CzOIL99BlqdvFH7xT0U1+2BFwLe4/8kwphSqyJE +C5oOiQBFnFVNXmFQSV+k7rPr80i1IO++HeJ6KQIDAQABAoIBAGWgvPjfuaU3qizq +uti/FY07USz0zkuJdkANH6LiSjlchzDmn8wJ0pApCjuIE0PV/g9aS8z4opp5q/gD +UBLM/a8mC/xf2EhTXOMrY7i9p/I3H5FZ4ZehEqIw9sWKK9YzC6dw26HabB2BGOnW +5nozPSQ6cp2RGzJ7BIkxSZwPzPnVTgy3OAuPOiJytvK+hGLhsNaT+Y9bNDvplVT2 +ZwYTV8GlHZC+4b2wNROILm0O86v96O+Qd8nn3fXjGHbMsAnONBq10bZS16L4fvkH +5G+W/1PeSXmtZFppdRRDxIW+DWcXK0D48WRliuxcV4eOOxI+a9N2ZJZZiNLQZGwg +w3A8+mECgYEA8HuJFrlRvdoBe2U/EwUtG74dcyy30L4yEBnN5QscXmEEikhaQCfX +Wm6EieMcIB/5I5TQmSw0cmBMeZjSXYoFdoI16/X6yMMuATdxpvhOZGdUGXxhAH+x +xoTUavWZnEqW3fkUU71kT5E2f2i+0zoatFESXHeslJyz85aAYpP92H0CgYEA2e5A +Yozt5eaA1Gyhd8SeptkEU4xPirNUnVQHStpMWUb1kzTNXrPmNWccQ7JpfpG6DcYl +zUF6p6mlzY+zkMiyPQjwEJlhiHM2NlL1QS7td0R8ewgsFoyn8WsBI4RejWrEG9td +EDniuIw+pBFkcWthnTLHwECHdzgquToyTMjrBB0CgYEA28tdGbrZXhcyAZEhHAZA +Gzog+pKlkpEzeonLKIuGKzCrEKRecIK5jrqyQsCjhS0T7ZRnL4g6i0s+umiV5M5w +fcc292pEA1h45L3DD6OlKplSQVTv55/OYS4oY3YEJtf5mfm8vWi9lQeY8sxOlQpn +O+VZTdBHmTC8PGeTAgZXHZUCgYA6Tyv88lYowB7SN2qQgBQu8jvdGtqhcs/99GCr +H3N0I69LPsKAR0QeH8OJPXBKhDUywESXAaEOwS5yrLNP1tMRz5Vj65YUCzeDG3kx +gpvY4IMp7ArX0bSRvJ6mYSFnVxy3k174G3TVCfksrtagHioVBGQ7xUg5ltafjrms +n8l55QKBgQDVzU8tQvBVqY8/1lnw11Vj4fkE/drZHJ5UkdC1eenOfSWhlSLfUJ8j +ds7vEWpRPPoVuPZYeR1y78cyxKe1GBx6Wa2lF5c7xjmiu0xbRnrxYeLolce9/ntp +asClqpnHT8/VJYTD7Kqj0fouTTZf0zkig/y+2XERppd8k+pSKjUCPQ== +-----END RSA PRIVATE KEY----- +">>). + -define(CONF_STOMP_BAISC_1, #{ <<"idle_timeout">> => <<"10s">>, <<"mountpoint">> => <<"t/">>, @@ -73,6 +200,31 @@ init_per_testcase(_CaseName, Conf) -> -define(CONF_STOMP_LISTENER_2, #{ <<"bind">> => <<"61614">> }). +-define(CONF_STOMP_LISTENER_SSL, + #{ <<"bind">> => <<"61614">>, + <<"ssl">> => + #{ <<"cacertfile">> => ?SVR_CA, + <<"certfile">> => ?SVR_CERT, + <<"keyfile">> => ?SVR_KEY + } + }). +-define(CONF_STOMP_LISTENER_SSL_2, + #{ <<"bind">> => <<"61614">>, + <<"ssl">> => + #{ <<"cacertfile">> => ?SVR_CA, + <<"certfile">> => ?SVR_CERT2, + <<"keyfile">> => ?SVR_KEY2 + } + }). +-define(CERTS_PATH(CertName), filename:join(["../../lib/emqx/etc/certs/", CertName])). +-define(CONF_STOMP_LISTENER_SSL_PATH, + #{ <<"bind">> => <<"61614">>, + <<"ssl">> => + #{ <<"cacertfile">> => ?CERTS_PATH("cacert.pem"), + <<"certfile">> => ?CERTS_PATH("cert.pem"), + <<"keyfile">> => ?CERTS_PATH("key.pem") + } + }). -define(CONF_STOMP_AUTHN_1, #{ <<"mechanism">> => <<"password-based">>, <<"backend">> => <<"built-in-database">>, @@ -92,7 +244,6 @@ t_load_unload_gateway(_) -> StompConf2 = compose(?CONF_STOMP_BAISC_2, ?CONF_STOMP_AUTHN_1, ?CONF_STOMP_LISTENER_1), - ok = emqx_gateway_conf:load_gateway(stomp, StompConf1), {error, already_exist} = emqx_gateway_conf:load_gateway(stomp, StompConf1), @@ -210,6 +361,87 @@ t_load_remove_listener_authn(_) -> ), ok. +t_load_gateway_with_certs_content(_) -> + StompConf = compose_ssl_listener( + ?CONF_STOMP_BAISC_1, + ?CONF_STOMP_LISTENER_SSL + ), + ok = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), + assert_confs(StompConf, emqx:get_raw_config([gateway, stomp])), + SslConf = emqx_map_lib:deep_get( + [<<"listeners">>, <<"ssl">>, <<"default">>, <<"ssl">>], + emqx:get_raw_config([gateway, stomp]) + ), + ok = emqx_gateway_conf:unload_gateway(<<"stomp">>), + assert_ssl_confs_files_deleted(SslConf), + ?assertException(error, {config_not_found, [gateway, stomp]}, + emqx:get_raw_config([gateway, stomp])), + ok. + +%% TODO: Comment out this test case for now, because emqx_tls_lib +%% will delete the configured certificate file. + +%t_load_gateway_with_certs_path(_) -> +% StompConf = compose_ssl_listener( +% ?CONF_STOMP_BAISC_1, +% ?CONF_STOMP_LISTENER_SSL_PATH +% ), +% ok = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), +% assert_confs(StompConf, emqx:get_raw_config([gateway, stomp])), +% SslConf = emqx_map_lib:deep_get( +% [<<"listeners">>, <<"ssl">>, <<"default">>, <<"ssl">>], +% emqx:get_raw_config([gateway, stomp]) +% ), +% ok = emqx_gateway_conf:unload_gateway(<<"stomp">>), +% assert_ssl_confs_files_deleted(SslConf), +% ?assertException(error, {config_not_found, [gateway, stomp]}, +% emqx:get_raw_config([gateway, stomp])), +% ok. + +t_add_listener_with_certs_content(_) -> + StompConf = ?CONF_STOMP_BAISC_1, + StompConf1 = compose_ssl_listener( + ?CONF_STOMP_BAISC_1, + ?CONF_STOMP_LISTENER_SSL + ), + ok = emqx_gateway_conf:load_gateway(<<"stomp">>, StompConf), + assert_confs(StompConf, emqx:get_raw_config([gateway, stomp])), + + ok = emqx_gateway_conf:add_listener( + <<"stomp">>, {<<"ssl">>, <<"default">>}, ?CONF_STOMP_LISTENER_SSL), + assert_confs( + maps:merge(StompConf, ssl_listener(?CONF_STOMP_LISTENER_SSL)), + emqx:get_raw_config([gateway, stomp])), + + ok = emqx_gateway_conf:update_listener( + <<"stomp">>, {<<"ssl">>, <<"default">>}, ?CONF_STOMP_LISTENER_SSL_2), + assert_confs( + maps:merge(StompConf, ssl_listener(?CONF_STOMP_LISTENER_SSL_2)), + emqx:get_raw_config([gateway, stomp])), + + SslConf = emqx_map_lib:deep_get( + [<<"listeners">>, <<"ssl">>, <<"default">>, <<"ssl">>], + emqx:get_raw_config([gateway, stomp]) + ), + ok = emqx_gateway_conf:remove_listener( + <<"stomp">>, {<<"ssl">>, <<"default">>}), + assert_ssl_confs_files_deleted(SslConf), + {error, not_found} = + emqx_gateway_conf:update_listener( + <<"stomp">>, {<<"ssl">>, <<"default">>}, ?CONF_STOMP_LISTENER_SSL_2), + ?assertException( + error, {config_not_found, [gateway, stomp, listeners, ssl, default]}, + emqx:get_raw_config([gateway, stomp, listeners, ssl, default]) + ), + ok. + +assert_ssl_confs_files_deleted(SslConf) when is_map(SslConf) -> + Ks = [<<"cacertfile">>, <<"certfile">>, <<"keyfile">>], + lists:foreach(fun(K) -> + Path = maps:get(K, SslConf), + {error, enoent} = file:read_file(Path) + end, Ks). + %%-------------------------------------------------------------------- %% Utils @@ -221,6 +453,9 @@ compose(Basic, Authn, Listener) -> compose_listener(Basic, Listener) -> maps:merge(Basic, listener(Listener)). +compose_ssl_listener(Basic, Listener) -> + maps:merge(Basic, ssl_listener(Listener)). + compose_authn(Basic, Authn) -> maps:merge(Basic, #{<<"authentication">> => Authn}). @@ -232,3 +467,7 @@ compose_listener_authn(Basic, Listener, Authn) -> listener(L) -> #{<<"listeners">> => [L#{<<"type">> => <<"tcp">>, <<"name">> => <<"default">>}]}. + +ssl_listener(L) -> + #{<<"listeners">> => [L#{<<"type">> => <<"ssl">>, + <<"name">> => <<"default">>}]}. diff --git a/apps/emqx_gateway/test/emqx_gateway_test_utils.erl b/apps/emqx_gateway/test/emqx_gateway_test_utils.erl index d7fd12c3d..329e97e8f 100644 --- a/apps/emqx_gateway/test/emqx_gateway_test_utils.erl +++ b/apps/emqx_gateway/test/emqx_gateway_test_utils.erl @@ -21,7 +21,7 @@ assert_confs(Expected0, Effected) -> Expected = maybe_unconvert_listeners(Expected0), - case do_assert_confs(Expected, Effected) of + case do_assert_confs(root, Expected, Effected) of false -> io:format(standard_error, "Expected config: ~p,\n" "Effected config: ~p", @@ -31,23 +31,36 @@ assert_confs(Expected0, Effected) -> ok end. -do_assert_confs(Expected, Effected) when is_map(Expected), - is_map(Effected) -> +do_assert_confs(_Key, Expected, Effected) when is_map(Expected), + is_map(Effected) -> Ks1 = maps:keys(Expected), lists:all(fun(K) -> - do_assert_confs(maps:get(K, Expected), + do_assert_confs(K, + maps:get(K, Expected), maps:get(K, Effected, undefined)) end, Ks1); -do_assert_confs([Expected|More1], [Effected|More2]) -> - do_assert_confs(Expected, Effected) andalso do_assert_confs(More1, More2); -do_assert_confs([], []) -> +do_assert_confs(Key, Expected, Effected) when Key == <<"cacertfile">>; + Key == <<"certfile">>; + Key == <<"keyfile">> -> + case Expected == Effected of + true -> true; + false -> + case file:read_file(Effected) of + {ok, Content} -> Expected == Content; + _ -> false + end + end; +do_assert_confs(Key, [Expected|More1], [Effected|More2]) -> + do_assert_confs(Key, Expected, Effected) + andalso do_assert_confs(Key, More1, More2); +do_assert_confs(_Key, [], []) -> true; -do_assert_confs(Expected, Effected) -> +do_assert_confs(Key, Expected, Effected) -> Res = Expected =:= Effected, Res == false andalso - ct:pal("Errors: conf not match, " - "expected: ~p, got: ~p~n", [Expected, Effected]), + ct:pal("Errors: ~p value not match, " + "expected: ~p, got: ~p~n", [Key, Expected, Effected]), Res. maybe_unconvert_listeners(Conf) when is_map(Conf) ->