fix(emqx_plugin_libs_ssl): handle relative cert paths
This commit is contained in:
parent
66873af319
commit
0948417db8
|
@ -16,12 +16,13 @@
|
||||||
|
|
||||||
-module(emqx_plugin_libs_ssl).
|
-module(emqx_plugin_libs_ssl).
|
||||||
|
|
||||||
-export([save_files_return_opts/2,
|
-export([
|
||||||
|
save_files_return_opts/2,
|
||||||
save_files_return_opts/3,
|
save_files_return_opts/3,
|
||||||
save_file/2
|
save_file/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-type file_input_key() :: atom() | binary(). %% <<"file">> | <<"filename">>
|
-type file_input_key() :: atom() | binary().
|
||||||
-type file_input() :: #{file_input_key() => binary()}.
|
-type file_input() :: #{file_input_key() => binary()}.
|
||||||
|
|
||||||
%% options are below paris
|
%% options are below paris
|
||||||
|
@ -39,6 +40,8 @@
|
||||||
-type opt_value() :: term().
|
-type opt_value() :: term().
|
||||||
-type opts() :: [{opt_key(), opt_value()}].
|
-type opts() :: [{opt_key(), opt_value()}].
|
||||||
|
|
||||||
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
%% @doc Parse ssl options input.
|
%% @doc Parse ssl options input.
|
||||||
%% If the input contains file content, save the files in the given dir.
|
%% If the input contains file content, save the files in the given dir.
|
||||||
%% Returns ssl options for Erlang's ssl application.
|
%% Returns ssl options for Erlang's ssl application.
|
||||||
|
@ -48,8 +51,11 @@
|
||||||
%% In case it's a map, the file is saved in EMQX's `data_dir'
|
%% In case it's a map, the file is saved in EMQX's `data_dir'
|
||||||
%% (unless `SubDir' is an absolute path).
|
%% (unless `SubDir' is an absolute path).
|
||||||
%% NOTE: This function is now deprecated, use emqx_tls_lib:ensure_ssl_files/2 instead.
|
%% NOTE: This function is now deprecated, use emqx_tls_lib:ensure_ssl_files/2 instead.
|
||||||
-spec save_files_return_opts(opts_input(), atom() | string() | binary(),
|
-spec save_files_return_opts(
|
||||||
string() | binary()) -> opts().
|
opts_input(),
|
||||||
|
atom() | string() | binary(),
|
||||||
|
string() | binary()
|
||||||
|
) -> opts().
|
||||||
save_files_return_opts(Options, SubDir, ResId) ->
|
save_files_return_opts(Options, SubDir, ResId) ->
|
||||||
Dir = filename:join([emqx:data_dir(), SubDir, ResId]),
|
Dir = filename:join([emqx:data_dir(), SubDir, ResId]),
|
||||||
save_files_return_opts(Options, Dir).
|
save_files_return_opts(Options, Dir).
|
||||||
|
@ -70,16 +76,26 @@ save_files_return_opts(Options, Dir) ->
|
||||||
KeyFile = Get(keyfile),
|
KeyFile = Get(keyfile),
|
||||||
CertFile = Get(certfile),
|
CertFile = Get(certfile),
|
||||||
CAFile = Get(cacertfile),
|
CAFile = Get(cacertfile),
|
||||||
Key = do_save_file(KeyFile, Dir),
|
Key = maybe_save_file(KeyFile, Dir),
|
||||||
Cert = do_save_file(CertFile, Dir),
|
Cert = maybe_save_file(CertFile, Dir),
|
||||||
CA = do_save_file(CAFile, Dir),
|
CA = maybe_save_file(CAFile, Dir),
|
||||||
Verify = GetD(verify, verify_none),
|
Verify = GetD(verify, verify_none),
|
||||||
SNI = Get(server_name_indication),
|
SNI =
|
||||||
|
case Get(<<"server_name_indication">>) of
|
||||||
|
undefined -> undefined;
|
||||||
|
SNI0 -> ensure_str(SNI0)
|
||||||
|
end,
|
||||||
Versions = emqx_tls_lib:integral_versions(Get(versions)),
|
Versions = emqx_tls_lib:integral_versions(Get(versions)),
|
||||||
Ciphers = emqx_tls_lib:integral_ciphers(Versions, Get(ciphers)),
|
Ciphers = emqx_tls_lib:integral_ciphers(Versions, Get(ciphers)),
|
||||||
filter([{keyfile, Key}, {certfile, Cert}, {cacertfile, CA},
|
filter([
|
||||||
{verify, Verify}, {server_name_indication, SNI},
|
{keyfile, Key},
|
||||||
{versions, Versions}, {ciphers, Ciphers}]).
|
{certfile, Cert},
|
||||||
|
{cacertfile, CA},
|
||||||
|
{verify, Verify},
|
||||||
|
{server_name_indication, SNI},
|
||||||
|
{versions, Versions},
|
||||||
|
{ciphers, Ciphers}
|
||||||
|
]).
|
||||||
|
|
||||||
%% @doc Save a key or certificate file in data dir,
|
%% @doc Save a key or certificate file in data dir,
|
||||||
%% and return path of the saved file.
|
%% and return path of the saved file.
|
||||||
|
@ -87,32 +103,54 @@ save_files_return_opts(Options, Dir) ->
|
||||||
-spec save_file(file_input(), atom() | string() | binary()) -> string().
|
-spec save_file(file_input(), atom() | string() | binary()) -> string().
|
||||||
save_file(Param, SubDir) ->
|
save_file(Param, SubDir) ->
|
||||||
Dir = filename:join([emqx:data_dir(), SubDir]),
|
Dir = filename:join([emqx:data_dir(), SubDir]),
|
||||||
do_save_file(Param, Dir).
|
maybe_save_file(Param, Dir).
|
||||||
|
|
||||||
filter([]) -> [];
|
filter([]) -> [];
|
||||||
filter([{_, undefined} | T]) -> filter(T);
|
filter([{_, undefined} | T]) -> filter(T);
|
||||||
filter([{_, ""} | T]) -> filter(T);
|
filter([{_, ""} | T]) -> filter(T);
|
||||||
filter([H | T]) -> [H | filter(T)].
|
filter([H | T]) -> [H | filter(T)].
|
||||||
|
|
||||||
do_save_file(#{filename := FileName, file := Content}, Dir)
|
maybe_save_file(#{filename := FileName, file := Content}, Dir) when
|
||||||
when FileName =/= undefined andalso Content =/= undefined ->
|
FileName =/= undefined andalso Content =/= undefined
|
||||||
do_save_file(ensure_str(FileName), iolist_to_binary(Content), Dir);
|
->
|
||||||
do_save_file(FilePath, _) when is_list(FilePath) ->
|
maybe_save_file(ensure_str(FileName), iolist_to_binary(Content), Dir);
|
||||||
|
maybe_save_file(FilePath, _) when is_list(FilePath) ->
|
||||||
FilePath;
|
FilePath;
|
||||||
do_save_file(FilePath, _) when is_binary(FilePath) ->
|
maybe_save_file(FilePath, _) when is_binary(FilePath) ->
|
||||||
ensure_str(FilePath);
|
ensure_str(FilePath);
|
||||||
do_save_file(_, _) -> "".
|
maybe_save_file(_, _) ->
|
||||||
|
"".
|
||||||
|
|
||||||
do_save_file("", _, _Dir) -> ""; %% ignore
|
%% no filename, ignore
|
||||||
do_save_file(_, <<>>, _Dir) -> ""; %% ignore
|
maybe_save_file("", _, _Dir) ->
|
||||||
do_save_file(FileName, Content, Dir) ->
|
"";
|
||||||
|
%% no content, see if file exists
|
||||||
|
maybe_save_file(FileName, <<>>, Dir) ->
|
||||||
|
{ok, Cwd} = file:get_cwd(),
|
||||||
|
%% NOTE: when FileName is an absolute path, filename:join has no effect
|
||||||
|
CwdFile = ensure_str(filename:join([Cwd, FileName])),
|
||||||
|
DataDirFile = ensure_str(filename:join([Dir, FileName])),
|
||||||
|
Possibles =
|
||||||
|
case CwdFile =:= DataDirFile of
|
||||||
|
true -> [CwdFile];
|
||||||
|
false -> [CwdFile, DataDirFile]
|
||||||
|
end,
|
||||||
|
case find_exist_file(FileName, Possibles) of
|
||||||
|
false -> erlang:throw({bad_cert_file, Possibles});
|
||||||
|
Found -> Found
|
||||||
|
end;
|
||||||
|
maybe_save_file(FileName, Content, Dir) ->
|
||||||
FullFilename = filename:join([Dir, FileName]),
|
FullFilename = filename:join([Dir, FileName]),
|
||||||
ok = filelib:ensure_dir(FullFilename),
|
ok = filelib:ensure_dir(FullFilename),
|
||||||
case file:write_file(FullFilename, Content) of
|
case file:write_file(FullFilename, Content) of
|
||||||
ok ->
|
ok ->
|
||||||
ensure_str(FullFilename);
|
ensure_str(FullFilename);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
logger:error("failed_to_save_ssl_file ~ts: ~0p", [FullFilename, Reason]),
|
?SLOG(error, #{
|
||||||
|
msg => "failed_to_save_ssl_file",
|
||||||
|
filename => FullFilename,
|
||||||
|
reason => Reason
|
||||||
|
}),
|
||||||
error({"failed_to_save_ssl_file", FullFilename, Reason})
|
error({"failed_to_save_ssl_file", FullFilename, Reason})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -122,8 +160,18 @@ ensure_str(B) when is_binary(B) -> unicode:characters_to_list(B, utf8).
|
||||||
-spec fuzzy_map_get(atom() | binary(), map(), any()) -> any().
|
-spec fuzzy_map_get(atom() | binary(), map(), any()) -> any().
|
||||||
fuzzy_map_get(Key, Options, Default) ->
|
fuzzy_map_get(Key, Options, Default) ->
|
||||||
case maps:find(Key, Options) of
|
case maps:find(Key, Options) of
|
||||||
{ok, Val} -> Val;
|
{ok, Val} ->
|
||||||
|
Val;
|
||||||
error when is_atom(Key) ->
|
error when is_atom(Key) ->
|
||||||
fuzzy_map_get(atom_to_binary(Key, utf8), Options, Default);
|
fuzzy_map_get(atom_to_binary(Key, utf8), Options, Default);
|
||||||
error -> Default
|
error ->
|
||||||
|
Default
|
||||||
|
end.
|
||||||
|
|
||||||
|
find_exist_file(_Name, []) ->
|
||||||
|
false;
|
||||||
|
find_exist_file(Name, [F | Rest]) ->
|
||||||
|
case filelib:is_regular(F) of
|
||||||
|
true -> F;
|
||||||
|
false -> find_exist_file(Name, Rest)
|
||||||
end.
|
end.
|
||||||
|
|
|
@ -42,7 +42,8 @@ prop_file_or_content() ->
|
||||||
{prop_cert_file_name(), proper_types:binary()}]).
|
{prop_cert_file_name(), proper_types:binary()}]).
|
||||||
|
|
||||||
prop_cert_file_name() ->
|
prop_cert_file_name() ->
|
||||||
proper_types:oneof(["certname1", <<"certname2">>, "", <<>>, undefined]).
|
File = code:which(?MODULE), %% existing
|
||||||
|
proper_types:oneof(["", <<>>, undefined, File]).
|
||||||
|
|
||||||
prop_tls_versions() ->
|
prop_tls_versions() ->
|
||||||
proper_types:oneof(["tlsv1.3",
|
proper_types:oneof(["tlsv1.3",
|
||||||
|
@ -76,3 +77,10 @@ file_or_content({Name, Content}) ->
|
||||||
#{<<"file">> => Content, <<"filename">> => Name};
|
#{<<"file">> => Content, <<"filename">> => Name};
|
||||||
file_or_content(Name) ->
|
file_or_content(Name) ->
|
||||||
Name.
|
Name.
|
||||||
|
|
||||||
|
bad_cert_file_test() ->
|
||||||
|
Input = #{<<"keyfile">> =>
|
||||||
|
#{<<"filename">> => "notafile",
|
||||||
|
<<"file">> => ""}},
|
||||||
|
?assertThrow({bad_cert_file, _},
|
||||||
|
emqx_plugin_libs_ssl:save_files_return_opts(Input, "test-data")).
|
||||||
|
|
Loading…
Reference in New Issue