feat(clear certs): clear certs when deleting instance

This commit is contained in:
zhouzb 2021-09-16 15:13:38 +08:00
parent 6edc9f4221
commit 35a4a05f03
3 changed files with 73 additions and 31 deletions

View File

@ -199,12 +199,15 @@ pre_config_update(UpdateReq, OldConfig) ->
{ok, NewConfig} -> {ok, may_to_map(NewConfig)} {ok, NewConfig} -> {ok, may_to_map(NewConfig)}
end. end.
do_pre_config_update({create_authenticator, _ChainName, Config}, OldConfig) -> do_pre_config_update({create_authenticator, ChainName, Config}, OldConfig) ->
try convert_certs(Config) of try
NConfig -> CertsDir = certs_dir([to_bin(ChainName), generate_id(Config)]),
NConfig = convert_certs(CertsDir, Config),
{ok, OldConfig ++ [NConfig]} {ok, OldConfig ++ [NConfig]}
catch catch
error:{save_cert_to_file, _} = Reason -> error:{save_cert_to_file, _} = Reason ->
{error, Reason};
error:{missing_parameter, _} = Reason ->
{error, Reason} {error, Reason}
end; end;
do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldConfig) -> 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) AuthenticatorID =/= generate_id(OldConfig0)
end, OldConfig), end, OldConfig),
{ok, NewConfig}; {ok, NewConfig};
do_pre_config_update({update_authenticator, _ChainName, AuthenticatorID, Config}, OldConfig) -> do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) ->
try lists:map(fun(OldConfig0) -> try
CertsDir = certs_dir([to_bin(ChainName), AuthenticatorID]),
NewConfig = lists:map(
fun(OldConfig0) ->
case AuthenticatorID =:= generate_id(OldConfig0) of case AuthenticatorID =:= generate_id(OldConfig0) of
true -> convert_certs(Config, OldConfig0); true -> convert_certs(CertsDir, Config, OldConfig0);
false -> OldConfig0 false -> OldConfig0
end end
end, OldConfig) of end, OldConfig),
NewConfig ->
{ok, NewConfig} {ok, NewConfig}
catch catch
error:{save_cert_to_file, _} = Reason -> error:{save_cert_to_file, _} = Reason ->
{error, Reason};
error:{missing_parameter, _} = Reason ->
{error, Reason} {error, Reason}
end; end;
do_pre_config_update({move_authenticator, _ChainName, AuthenticatorID, Position}, OldConfig) -> 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_chain(ChainName),
create_authenticator(ChainName, NConfig); create_authenticator(ChainName, NConfig);
do_post_config_update({delete_authenticator, ChainName, AuthenticatorID}, _NewConfig, _OldConfig, _AppEnvs) -> do_post_config_update({delete_authenticator, ChainName, AuthenticatorID}, _NewConfig, OldConfig, _AppEnvs) ->
delete_authenticator(ChainName, AuthenticatorID); 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) -> do_post_config_update({update_authenticator, ChainName, AuthenticatorID, _Config}, NewConfig, _OldConfig, _AppEnvs) ->
[Config] = lists:filter(fun(NewConfig0) -> [Config] = lists:filter(fun(NewConfig0) ->
@ -449,7 +464,9 @@ generate_id(#{mechanism := Mechanism}) ->
generate_id(#{<<"mechanism">> := Mechanism, <<"backend">> := Backend}) -> generate_id(#{<<"mechanism">> := Mechanism, <<"backend">> := Backend}) ->
<<Mechanism/binary, ":", Backend/binary>>; <<Mechanism/binary, ":", Backend/binary>>;
generate_id(#{<<"mechanism">> := Mechanism}) -> generate_id(#{<<"mechanism">> := Mechanism}) ->
Mechanism. Mechanism;
generate_id(_) ->
error({missing_parameter, mechanism}).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
@ -642,34 +659,46 @@ reply(Reply, State) ->
%% Internal functions %% 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) -> NSSLOPts = lists:foldl(fun(K, Acc) ->
case maps:get(K, Acc, undefined) of case maps:get(K, Acc, undefined) of
undefined -> Acc; undefined -> Acc;
PemBin -> PemBin ->
CertFile = generate_filename(K), CertFile = generate_filename(CertsDir, K),
ok = save_cert_to_file(CertFile, PemBin), ok = save_cert_to_file(CertFile, PemBin),
Acc#{K => CertFile} Acc#{K => CertFile}
end end
end, SSLOpts, [<<"certfile">>, <<"keyfile">>, <<"cacertfile">>]), end, SSLOpts, [<<"certfile">>, <<"keyfile">>, <<"cacertfile">>]),
Config#{<<"ssl">> => NSSLOPts}; Config#{<<"ssl">> => NSSLOPts};
convert_certs(Config) -> convert_certs(_CertsDir, Config) ->
Config. Config.
convert_certs(#{<<"ssl">> := NewSSLOpts} = NewConfig, OldConfig) -> convert_certs(CertsDir, #{<<"ssl">> := NewSSLOpts} = NewConfig, OldConfig) ->
OldSSLOpts = maps:get(<<"ssl">>, OldConfig, #{}), OldSSLOpts = maps:get(<<"ssl">>, OldConfig, #{}),
Diff = diff_certs(NewSSLOpts, OldSSLOpts), Diff = diff_certs(NewSSLOpts, OldSSLOpts),
NSSLOpts = lists:foldl(fun({identical, K}, Acc) -> NSSLOpts = lists:foldl(fun({identical, K}, Acc) ->
Acc#{K => maps:get(K, OldSSLOpts)}; Acc#{K => maps:get(K, OldSSLOpts)};
({_, K}, Acc) -> ({_, K}, Acc) ->
CertFile = generate_filename(K), CertFile = generate_filename(CertsDir, K),
ok = save_cert_to_file(CertFile, maps:get(K, NewSSLOpts)), ok = save_cert_to_file(CertFile, maps:get(K, NewSSLOpts)),
Acc#{K => CertFile} Acc#{K => CertFile}
end, NewSSLOpts, Diff), end, NewSSLOpts, Diff),
NewConfig#{<<"ssl">> => NSSLOpts}; NewConfig#{<<"ssl">> => NSSLOpts};
convert_certs(NewConfig, _OldConfig) -> convert_certs(_CertsDir, NewConfig, _OldConfig) ->
NewConfig. 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) -> save_cert_to_file(Filename, PemBin) ->
case public_key:pem_decode(PemBin) =/= [] of case public_key:pem_decode(PemBin) =/= [] of
true -> true ->
@ -686,13 +715,13 @@ save_cert_to_file(Filename, PemBin) ->
error({save_cert_to_file, invalid_certificate}) error({save_cert_to_file, invalid_certificate})
end. end.
generate_filename(Key) -> generate_filename(CertsDir, Key) ->
Prefix = case Key of Prefix = case Key of
<<"keyfile">> -> "key-"; <<"keyfile">> -> "key-";
<<"certfile">> -> "cert-"; <<"certfile">> -> "cert-";
<<"cacertfile">> -> "cacert-" <<"cacertfile">> -> "cacert-"
end, 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) -> diff_certs(NewSSLOpts, OldSSLOpts) ->
Keys = [<<"cacertfile">>, <<"certfile">>, <<"keyfile">>], Keys = [<<"cacertfile">>, <<"certfile">>, <<"keyfile">>],
@ -900,6 +929,7 @@ to_list(L) when is_list(L) ->
L. L.
to_bin(B) when is_binary(B) -> B; 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). call(Call) -> gen_server:call(?MODULE, Call, infinity).

View File

@ -257,19 +257,22 @@ t_update_config({'end', Config}) ->
?AUTHN:deregister_providers([?config("auth1"), ?config("auth2")]), ?AUTHN:deregister_providers([?config("auth1"), ?config("auth2")]),
ok. ok.
t_convert_cert_options({_, Config}) -> Config; t_convert_certs({_, Config}) -> Config;
t_convert_cert_options(Config) when is_list(Config) -> t_convert_certs(Config) when is_list(Config) ->
Global = <<"mqtt:global">>,
Certs = certs([ {<<"keyfile">>, "key.pem"} Certs = certs([ {<<"keyfile">>, "key.pem"}
, {<<"certfile">>, "cert.pem"} , {<<"certfile">>, "cert.pem"}
, {<<"cacertfile">>, "cacert.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))), ?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, Certs))),
Certs2 = certs([ {<<"keyfile">>, "key.pem"} Certs2 = certs([ {<<"keyfile">>, "key.pem"}
, {<<"certfile">>, "cert.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(false, diff_cert(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, Certs2))),
?assertEqual(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, NCerts2)), ?assertEqual(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, NCerts2)),
?assertEqual(maps:get(<<"certfile">>, NCerts), maps:get(<<"certfile">>, 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"} , {<<"certfile">>, "client-cert.pem"}
, {<<"cacertfile">>, "cacert.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))), ?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts3), maps:get(<<"keyfile">>, Certs3))),
?assertNotEqual(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, NCerts3)), ?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) -> update_config(Path, ConfigRequest) ->
emqx:update_config(Path, ConfigRequest, #{rawconf_with_defaults => true}). emqx:update_config(Path, ConfigRequest, #{rawconf_with_defaults => true}).

View File

@ -1589,6 +1589,11 @@ definitions() ->
type => string, type => string,
example => <<"http://localhost:80">> example => <<"http://localhost:80">>
}, },
refresh_interval => #{
type => integer,
default => 300,
example => 300
},
verify_claims => #{ verify_claims => #{
type => object, type => object,
additionalProperties => #{ additionalProperties => #{