From 35a4a05f0380de8a473c29a0fc9c234dbe69e404 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Thu, 16 Sep 2021 15:13:38 +0800 Subject: [PATCH] feat(clear certs): clear certs when deleting instance --- apps/emqx/src/emqx_authentication.erl | 80 ++++++++++++++------ apps/emqx/test/emqx_authentication_SUITE.erl | 19 +++-- apps/emqx_authn/src/emqx_authn_api.erl | 5 ++ 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/apps/emqx/src/emqx_authentication.erl b/apps/emqx/src/emqx_authentication.erl index f047a1e41..8202ca2b4 100644 --- a/apps/emqx/src/emqx_authentication.erl +++ b/apps/emqx/src/emqx_authentication.erl @@ -199,12 +199,15 @@ pre_config_update(UpdateReq, OldConfig) -> {ok, NewConfig} -> {ok, may_to_map(NewConfig)} end. -do_pre_config_update({create_authenticator, _ChainName, Config}, OldConfig) -> - try convert_certs(Config) of - NConfig -> - {ok, OldConfig ++ [NConfig]} +do_pre_config_update({create_authenticator, ChainName, Config}, OldConfig) -> + try + CertsDir = certs_dir([to_bin(ChainName), generate_id(Config)]), + NConfig = convert_certs(CertsDir, Config), + {ok, OldConfig ++ [NConfig]} catch error:{save_cert_to_file, _} = Reason -> + {error, Reason}; + error:{missing_parameter, _} = Reason -> {error, Reason} end; do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldConfig) -> @@ -212,17 +215,21 @@ do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldCon AuthenticatorID =/= generate_id(OldConfig0) end, OldConfig), {ok, NewConfig}; -do_pre_config_update({update_authenticator, _ChainName, AuthenticatorID, Config}, OldConfig) -> - try lists:map(fun(OldConfig0) -> - case AuthenticatorID =:= generate_id(OldConfig0) of - true -> convert_certs(Config, OldConfig0); - false -> OldConfig0 - end - end, OldConfig) of - NewConfig -> - {ok, NewConfig} +do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) -> + try + CertsDir = certs_dir([to_bin(ChainName), AuthenticatorID]), + NewConfig = lists:map( + fun(OldConfig0) -> + case AuthenticatorID =:= generate_id(OldConfig0) of + true -> convert_certs(CertsDir, Config, OldConfig0); + false -> OldConfig0 + end + end, OldConfig), + {ok, NewConfig} catch error:{save_cert_to_file, _} = Reason -> + {error, Reason}; + error:{missing_parameter, _} = Reason -> {error, Reason} end; do_pre_config_update({move_authenticator, _ChainName, AuthenticatorID, Position}, OldConfig) -> @@ -254,8 +261,16 @@ do_post_config_update({create_authenticator, ChainName, Config}, _NewConfig, _Ol _ = create_chain(ChainName), create_authenticator(ChainName, NConfig); -do_post_config_update({delete_authenticator, ChainName, AuthenticatorID}, _NewConfig, _OldConfig, _AppEnvs) -> - delete_authenticator(ChainName, AuthenticatorID); +do_post_config_update({delete_authenticator, ChainName, AuthenticatorID}, _NewConfig, OldConfig, _AppEnvs) -> + case delete_authenticator(ChainName, AuthenticatorID) of + ok -> + [Config] = [Config0 || Config0 <- OldConfig, AuthenticatorID == generate_id(Config0)], + CertsDir = certs_dir([to_bin(ChainName), AuthenticatorID]), + clear_certs(CertsDir, Config), + ok; + {error, Reason} -> + {error, Reason} + end; do_post_config_update({update_authenticator, ChainName, AuthenticatorID, _Config}, NewConfig, _OldConfig, _AppEnvs) -> [Config] = lists:filter(fun(NewConfig0) -> @@ -449,7 +464,9 @@ generate_id(#{mechanism := Mechanism}) -> generate_id(#{<<"mechanism">> := Mechanism, <<"backend">> := Backend}) -> <>; generate_id(#{<<"mechanism">> := Mechanism}) -> - Mechanism. + Mechanism; +generate_id(_) -> + error({missing_parameter, mechanism}). %%-------------------------------------------------------------------- %% gen_server callbacks @@ -642,34 +659,46 @@ reply(Reply, State) -> %% Internal functions %%------------------------------------------------------------------------------ -convert_certs(#{<<"ssl">> := SSLOpts} = Config) -> +certs_dir(Dirs) when is_list(Dirs) -> + to_bin(filename:join([emqx:get_config([node, data_dir]), "certs/authn"] ++ Dirs)). + +convert_certs(CertsDir, #{<<"ssl">> := SSLOpts} = Config) -> NSSLOPts = lists:foldl(fun(K, Acc) -> case maps:get(K, Acc, undefined) of undefined -> Acc; PemBin -> - CertFile = generate_filename(K), + CertFile = generate_filename(CertsDir, K), ok = save_cert_to_file(CertFile, PemBin), Acc#{K => CertFile} end end, SSLOpts, [<<"certfile">>, <<"keyfile">>, <<"cacertfile">>]), Config#{<<"ssl">> => NSSLOPts}; -convert_certs(Config) -> +convert_certs(_CertsDir, Config) -> Config. -convert_certs(#{<<"ssl">> := NewSSLOpts} = NewConfig, OldConfig) -> +convert_certs(CertsDir, #{<<"ssl">> := NewSSLOpts} = NewConfig, OldConfig) -> OldSSLOpts = maps:get(<<"ssl">>, OldConfig, #{}), Diff = diff_certs(NewSSLOpts, OldSSLOpts), NSSLOpts = lists:foldl(fun({identical, K}, Acc) -> Acc#{K => maps:get(K, OldSSLOpts)}; ({_, K}, Acc) -> - CertFile = generate_filename(K), + CertFile = generate_filename(CertsDir, K), ok = save_cert_to_file(CertFile, maps:get(K, NewSSLOpts)), Acc#{K => CertFile} end, NewSSLOpts, Diff), NewConfig#{<<"ssl">> => NSSLOpts}; -convert_certs(NewConfig, _OldConfig) -> +convert_certs(_CertsDir, NewConfig, _OldConfig) -> NewConfig. +clear_certs(CertsDir, #{<<"ssl">> := SSLOpts}) -> + lists:foreach( + fun({_, Filename}) -> + _ = file:delete(filename:join([CertsDir, Filename])) + end, + maps:to_list(maps:with([<<"certfile">>, <<"keyfile">>, <<"cacertfile">>], SSLOpts))); +clear_certs(_CertsDir, _Config) -> + ok. + save_cert_to_file(Filename, PemBin) -> case public_key:pem_decode(PemBin) =/= [] of true -> @@ -686,13 +715,13 @@ save_cert_to_file(Filename, PemBin) -> error({save_cert_to_file, invalid_certificate}) end. -generate_filename(Key) -> +generate_filename(CertsDir, Key) -> Prefix = case Key of <<"keyfile">> -> "key-"; <<"certfile">> -> "cert-"; <<"cacertfile">> -> "cacert-" end, - to_bin(filename:join([emqx:get_config([node, data_dir]), "certs/authn", Prefix ++ emqx_misc:gen_id() ++ ".pem"])). + to_bin(filename:join([CertsDir, Prefix ++ emqx_misc:gen_id() ++ ".pem"])). diff_certs(NewSSLOpts, OldSSLOpts) -> Keys = [<<"cacertfile">>, <<"certfile">>, <<"keyfile">>], @@ -900,6 +929,7 @@ to_list(L) when is_list(L) -> L. to_bin(B) when is_binary(B) -> B; -to_bin(L) when is_list(L) -> list_to_binary(L). +to_bin(L) when is_list(L) -> list_to_binary(L); +to_bin(A) when is_atom(A) -> atom_to_binary(A). call(Call) -> gen_server:call(?MODULE, Call, infinity). diff --git a/apps/emqx/test/emqx_authentication_SUITE.erl b/apps/emqx/test/emqx_authentication_SUITE.erl index 15aecc269..2184cbba8 100644 --- a/apps/emqx/test/emqx_authentication_SUITE.erl +++ b/apps/emqx/test/emqx_authentication_SUITE.erl @@ -257,19 +257,22 @@ t_update_config({'end', Config}) -> ?AUTHN:deregister_providers([?config("auth1"), ?config("auth2")]), ok. -t_convert_cert_options({_, Config}) -> Config; -t_convert_cert_options(Config) when is_list(Config) -> +t_convert_certs({_, Config}) -> Config; +t_convert_certs(Config) when is_list(Config) -> + Global = <<"mqtt:global">>, Certs = certs([ {<<"keyfile">>, "key.pem"} , {<<"certfile">>, "cert.pem"} , {<<"cacertfile">>, "cacert.pem"} ]), - #{<<"ssl">> := NCerts} = ?AUTHN:convert_certs(#{<<"ssl">> => Certs}), + + CertsDir = ?AUTHN:certs_dir([Global, <<"password-based:built-in-database">>]), + #{<<"ssl">> := NCerts} = ?AUTHN:convert_certs(CertsDir, #{<<"ssl">> => Certs}), ?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, Certs))), Certs2 = certs([ {<<"keyfile">>, "key.pem"} , {<<"certfile">>, "cert.pem"} ]), - #{<<"ssl">> := NCerts2} = ?AUTHN:convert_certs(#{<<"ssl">> => Certs2}, #{<<"ssl">> => NCerts}), + #{<<"ssl">> := NCerts2} = ?AUTHN:convert_certs(CertsDir, #{<<"ssl">> => Certs2}, #{<<"ssl">> => NCerts}), ?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, Certs2))), ?assertEqual(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, NCerts2)), ?assertEqual(maps:get(<<"certfile">>, NCerts), maps:get(<<"certfile">>, NCerts2)), @@ -278,10 +281,14 @@ t_convert_cert_options(Config) when is_list(Config) -> , {<<"certfile">>, "client-cert.pem"} , {<<"cacertfile">>, "cacert.pem"} ]), - #{<<"ssl">> := NCerts3} = ?AUTHN:convert_certs(#{<<"ssl">> => Certs3}, #{<<"ssl">> => NCerts2}), + #{<<"ssl">> := NCerts3} = ?AUTHN:convert_certs(CertsDir, #{<<"ssl">> => Certs3}, #{<<"ssl">> => NCerts2}), ?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts3), maps:get(<<"keyfile">>, Certs3))), ?assertNotEqual(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, NCerts3)), - ?assertNotEqual(maps:get(<<"certfile">>, NCerts2), maps:get(<<"certfile">>, NCerts3)). + ?assertNotEqual(maps:get(<<"certfile">>, NCerts2), maps:get(<<"certfile">>, NCerts3)), + + ?assertEqual(true, filelib:is_regular(maps:get(<<"keyfile">>, NCerts3))), + ?AUTHN:clear_certs(CertsDir, #{<<"ssl">> => NCerts3}), + ?assertEqual(false, filelib:is_regular(maps:get(<<"keyfile">>, NCerts3))). update_config(Path, ConfigRequest) -> emqx:update_config(Path, ConfigRequest, #{rawconf_with_defaults => true}). diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl index 93e8c4746..0eb2f5cfa 100644 --- a/apps/emqx_authn/src/emqx_authn_api.erl +++ b/apps/emqx_authn/src/emqx_authn_api.erl @@ -1589,6 +1589,11 @@ definitions() -> type => string, example => <<"http://localhost:80">> }, + refresh_interval => #{ + type => integer, + default => 300, + example => 300 + }, verify_claims => #{ type => object, additionalProperties => #{