emqx_plugin_libs_ssl.erl 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(emqx_plugin_libs_ssl).
  17. -export([save_files_return_opts/2,
  18. save_files_return_opts/3,
  19. save_file/2
  20. ]).
  21. -type file_input_key() :: atom() | binary(). %% <<"file">> | <<"filename">>
  22. -type file_input() :: #{file_input_key() => binary()}.
  23. %% options are below paris
  24. %% <<"keyfile">> => file_input()
  25. %% <<"certfile">> => file_input()
  26. %% <<"cafile">> => file_input() %% backward compatible
  27. %% <<"cacertfile">> => file_input()
  28. %% <<"verify">> => boolean()
  29. %% <<"tls_versions">> => binary()
  30. %% <<"ciphers">> => binary()
  31. -type opts_key() :: binary() | atom().
  32. -type opts_input() :: #{opts_key() => term()}.
  33. -type opt_key() :: keyfile | certfile | cacertfile | verify | versions | ciphers.
  34. -type opt_value() :: term().
  35. -type opts() :: [{opt_key(), opt_value()}].
  36. %% @doc Parse ssl options input.
  37. %% If the input contains file content, save the files in the given dir.
  38. %% Returns ssl options for Erlang's ssl application.
  39. -spec save_files_return_opts(opts_input(), atom() | string() | binary(),
  40. string() | binary()) -> opts().
  41. save_files_return_opts(Options, SubDir, ResId) ->
  42. Dir = filename:join([emqx_config:get([node, data_dir]), SubDir, ResId]),
  43. save_files_return_opts(Options, Dir).
  44. %% @doc Parse ssl options input.
  45. %% If the input contains file content, save the files in the given dir.
  46. %% Returns ssl options for Erlang's ssl application.
  47. -spec save_files_return_opts(opts_input(), file:name_all()) -> opts().
  48. save_files_return_opts(Options, Dir) ->
  49. GetD = fun(Key, Default) -> fuzzy_map_get(Key, Options, Default) end,
  50. Get = fun(Key) -> GetD(Key, undefined) end,
  51. KeyFile = Get(keyfile),
  52. CertFile = Get(certfile),
  53. CAFile = Get(cacertfile),
  54. Key = do_save_file(KeyFile, Dir),
  55. Cert = do_save_file(CertFile, Dir),
  56. CA = do_save_file(CAFile, Dir),
  57. Verify = case GetD(verify, false) of
  58. false -> verify_none;
  59. _ -> verify_peer
  60. end,
  61. SNI = Get(server_name_indication),
  62. Versions = emqx_tls_lib:integral_versions(Get(tls_versions)),
  63. Ciphers = emqx_tls_lib:integral_ciphers(Versions, Get(ciphers)),
  64. filter([{keyfile, Key}, {certfile, Cert}, {cacertfile, CA},
  65. {verify, Verify}, {server_name_indication, SNI}, {versions, Versions}, {ciphers, Ciphers}]).
  66. %% @doc Save a key or certificate file in data dir,
  67. %% and return path of the saved file.
  68. %% empty string is returned if the input is empty.
  69. -spec save_file(file_input(), atom() | string() | binary()) -> string().
  70. save_file(Param, SubDir) ->
  71. Dir = filename:join([emqx_config:get([node, data_dir]), SubDir]),
  72. do_save_file(Param, Dir).
  73. filter([]) -> [];
  74. filter([{_, ""} | T]) -> filter(T);
  75. filter([H | T]) -> [H | filter(T)].
  76. do_save_file(#{filename := FileName, file := Content}, Dir)
  77. when FileName =/= undefined andalso Content =/= undefined ->
  78. do_save_file(ensure_str(FileName), iolist_to_binary(Content), Dir);
  79. do_save_file(FilePath, _) when is_list(FilePath) ->
  80. FilePath;
  81. do_save_file(FilePath, _) when is_binary(FilePath) ->
  82. ensure_str(FilePath);
  83. do_save_file(_, _) -> "".
  84. do_save_file("", _, _Dir) -> ""; %% ignore
  85. do_save_file(_, <<>>, _Dir) -> ""; %% ignore
  86. do_save_file(FileName, Content, Dir) ->
  87. FullFilename = filename:join([Dir, FileName]),
  88. ok = filelib:ensure_dir(FullFilename),
  89. case file:write_file(FullFilename, Content) of
  90. ok ->
  91. ensure_str(FullFilename);
  92. {error, Reason} ->
  93. logger:error("failed_to_save_ssl_file ~s: ~0p", [FullFilename, Reason]),
  94. error({"failed_to_save_ssl_file", FullFilename, Reason})
  95. end.
  96. ensure_str(L) when is_list(L) -> L;
  97. ensure_str(B) when is_binary(B) -> unicode:characters_to_list(B, utf8).
  98. -spec fuzzy_map_get(atom() | binary(), map(), any()) -> any().
  99. fuzzy_map_get(Key, Options, Default) ->
  100. case maps:find(Key, Options) of
  101. {ok, Val} -> Val;
  102. error when is_atom(Key) ->
  103. fuzzy_map_get(atom_to_binary(Key, utf8), Options, Default);
  104. error -> Default
  105. end.