From 7c61bc18cf6bf81cd5924bb1bbd6bfead005b143 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Sat, 18 Sep 2021 16:59:28 +0800 Subject: [PATCH 01/12] feat(psk): support psk --- apps/emqx/src/emqx_tls_psk.erl | 38 +++++ apps/emqx_psk/data/boot.psk | 2 + apps/emqx_psk/etc/emqx_psk.conf | 7 + apps/emqx_psk/rebar.config | 18 +++ apps/emqx_psk/src/emqx_psk.app.src | 15 ++ apps/emqx_psk/src/emqx_psk.erl | 224 ++++++++++++++++++++++++++ apps/emqx_psk/src/emqx_psk_app.erl | 30 ++++ apps/emqx_psk/src/emqx_psk_schema.erl | 45 ++++++ apps/emqx_psk/src/emqx_psk_sup.erl | 35 ++++ 9 files changed, 414 insertions(+) create mode 100644 apps/emqx/src/emqx_tls_psk.erl create mode 100644 apps/emqx_psk/data/boot.psk create mode 100644 apps/emqx_psk/etc/emqx_psk.conf create mode 100644 apps/emqx_psk/rebar.config create mode 100644 apps/emqx_psk/src/emqx_psk.app.src create mode 100644 apps/emqx_psk/src/emqx_psk.erl create mode 100644 apps/emqx_psk/src/emqx_psk_app.erl create mode 100644 apps/emqx_psk/src/emqx_psk_schema.erl create mode 100644 apps/emqx_psk/src/emqx_psk_sup.erl diff --git a/apps/emqx/src/emqx_tls_psk.erl b/apps/emqx/src/emqx_tls_psk.erl new file mode 100644 index 000000000..7a8238182 --- /dev/null +++ b/apps/emqx/src/emqx_tls_psk.erl @@ -0,0 +1,38 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2019-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_tls_psk). + +-include("logger.hrl"). + +%% SSL PSK Callbacks +-export([lookup/3]). + +-type psk_identity() :: string(). +-type psk_user_state() :: term(). + +-spec lookup(psk, psk_identity(), psk_user_state()) -> {ok, SharedSecret :: binary()} | error. +lookup(psk, PSKIdentity, UserState) -> + try emqx_hooks:run_fold('tls_handshake.psk_lookup', [PSKIdentity], UserState) of + SharedSecret when is_binary(SharedSecret) -> {ok, SharedSecret}; + Error -> + ?LOG(error, "Look PSK for PSKID ~p error: ~p", [PSKIdentity, Error]), + error + catch + Except:Error:Stacktrace -> + ?LOG(error, "Lookup PSK failed, ~0p: ~0p", [{Except,Error}, Stacktrace]), + error + end. diff --git a/apps/emqx_psk/data/boot.psk b/apps/emqx_psk/data/boot.psk new file mode 100644 index 000000000..7e1a1edf5 --- /dev/null +++ b/apps/emqx_psk/data/boot.psk @@ -0,0 +1,2 @@ +myclient1:8c701116e9127c57a99d5563709af3deaca75563e2c4dd0865701ae839fb6d79 +myclient2:d1e617d3b963757bfc21dad3fea169716c3a2f053f23decaea5cdfaabd04bfc4 diff --git a/apps/emqx_psk/etc/emqx_psk.conf b/apps/emqx_psk/etc/emqx_psk.conf new file mode 100644 index 000000000..d964f4e23 --- /dev/null +++ b/apps/emqx_psk/etc/emqx_psk.conf @@ -0,0 +1,7 @@ +psk { + enable = true + + # boot_file = {{ platform_data_dir }}/boot.psk + + # delimiter = ":" +} diff --git a/apps/emqx_psk/rebar.config b/apps/emqx_psk/rebar.config new file mode 100644 index 000000000..73696b033 --- /dev/null +++ b/apps/emqx_psk/rebar.config @@ -0,0 +1,18 @@ +{deps, []}. + +{edoc_opts, [{preprocess, true}]}. +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warnings_as_errors, + warn_unused_import, + warn_obsolete_guard, + debug_info, + {parse_transform}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions]}. + +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. diff --git a/apps/emqx_psk/src/emqx_psk.app.src b/apps/emqx_psk/src/emqx_psk.app.src new file mode 100644 index 000000000..3d749abf6 --- /dev/null +++ b/apps/emqx_psk/src/emqx_psk.app.src @@ -0,0 +1,15 @@ +%% -*- mode: erlang -*- +{application, emqx_psk, + [{description, "EMQ X PSK"}, + {vsn, "5.0.0"}, % strict semver, bump manually! + {modules, []}, + {registered, [emqx_psk_sup]}, + {applications, [kernel,stdlib]}, + {mod, {emqx_psk_app,[]}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx"} + ]} + ]}. diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl new file mode 100644 index 000000000..0968dad4c --- /dev/null +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -0,0 +1,224 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_psk). + +-behaviour(gen_server). + +-include_lib("emqx/include/logger.hrl"). + +-export([ load/0 + , unload/0 + , on_psk_lookup/2 + , import/1 + ]). + +-export([ start_link/0 + , stop/0 + ]). + +%% gen_server callbacks +-export([ init/1 + , handle_call/3 + , handle_cast/2 + , handle_info/2 + , terminate/2 + , code_change/3 + ]). + +-record(psk_entry, {psk_id :: binary(), + shared_secret :: binary()}). + +-export([mnesia/1]). + +-boot_mnesia({mnesia, [boot]}). +-copy_mnesia({mnesia, [copy]}). + +-define(TAB, ?MODULE). +-define(PSK_SHARD, emqx_psk_shard). + +-define(DEFAULT_DELIMITER, <<":">>). + +-define(CR, 13). +-define(LF, 10). + +%%------------------------------------------------------------------------------ +%% Mnesia bootstrap +%%------------------------------------------------------------------------------ + +%% @doc Create or replicate tables. +-spec(mnesia(boot | copy) -> ok). +mnesia(boot) -> + ok = ekka_mnesia:create_table(?TAB, [ + {rlog_shard, ?PSK_SHARD}, + {disc_copies, [node()]}, + {record_name, psk_entry}, + {attributes, record_info(fields, psk_entry)}, + {storage_properties, [{ets, [{read_concurrency, true}]}]}]); + +mnesia(copy) -> + ok = ekka_mnesia:copy_table(?TAB, disc_copies). + +%%------------------------------------------------------------------------------ +%% APIs +%%------------------------------------------------------------------------------ + +load() -> + emqx:hook('tls_handshake.psk_lookup', {?MODULE, on_psk_lookup, []}). + +unload() -> + emqx:unhook('tls_handshake.psk_lookup', {?MODULE, on_psk_lookup, []}). + +on_psk_lookup(PSKIdentity, _UserState) -> + case mnesia:dirty_read(?TAB, PSKIdentity) of + [#psk_entry{shared_secret = SharedSecret}] -> + {stop, SharedSecret}; + _ -> + ignore + end. + +import(SrcFile) -> + gen_server:call(?MODULE, {import, SrcFile}). + +-spec start_link() -> {ok, pid()} | ignore | {error, term()}. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +-spec stop() -> ok. +stop() -> + gen_server:stop(?MODULE). + +%%-------------------------------------------------------------------- +%% gen_server callbacks +%%-------------------------------------------------------------------- + +init(_Opts) -> + case is_enable() of + true -> load(); + false -> ok + end, + case boot_file() of + undefined -> ok; + BootFile -> import_psks(BootFile) + end, + {ok, #{}}. + +handle_call({import, SrcFile}, _From, State) -> + {reply, import_psks(SrcFile), State}; + +handle_call(Req, _From, State) -> + ?LOG(error, "Unexpected call: ~p", [Req]), + {reply, ignored, State}. + +handle_cast(Req, State) -> + ?LOG(error, "Unexpected case: ~p", [Req]), + {noreply, State}. + +handle_info(Info, State) -> + ?LOG(error, "Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + unload(), + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%------------------------------------------------------------------------------ +%% Internal functions +%%------------------------------------------------------------------------------ + +is_enable() -> + emqx_config:get([psk, enable]). + +boot_file() -> + emqx_config:get([psk, boot_file], undefined). + +delimiter() -> + emqx_config:get([psk, delimiter], ?DEFAULT_DELIMITER). + +import_psks(SrcFile) -> + case file:open(SrcFile, [read, raw, binary, read_ahead]) of + {error, Reason} -> + {error, Reason}; + {ok, Io} -> + case Result = import_psks(Io, delimiter()) of + ok -> ignore; + {error, Reason} -> + ?LOG("Failed to import psk from ~s due to ~p", [SrcFile, Reason]) + end, + _ = file:close(Io), + Result + end. + +import_psks(Io, Delimiter) -> + case get_psks(Io, Delimiter, 50) of + {ok, Entries} -> + _ = trans(fun insert_psks/1, Entries), + import_psks(Io, Delimiter); + {eof, Entries} -> + _ = trans(fun insert_psks/1, Entries), + ok; + {error, Reaosn} -> + {error, Reaosn} + end. + +get_psks(Io, Delimiter, Max) -> + get_psks(Io, Delimiter, Max, []). + +get_psks(_Io, _Delimiter, 0, Acc) -> + {ok, Acc}; +get_psks(Io, Delimiter, Remaining, Acc) -> + case file:read_line(Io) of + {ok, Line} -> + case binary:split(Line, Delimiter) of + [PSKIdentity, SharedSecret] -> + NSharedSecret = trim_crlf(SharedSecret), + get_psks(Io, Delimiter, Remaining - 1, [{PSKIdentity, NSharedSecret} | Acc]); + _ -> + {error, {bad_format, Line}} + end; + eof -> + {eof, Acc}; + {error, Reason} -> + {error, Reason} + end. + +insert_psks(Entries) -> + lists:foreach(fun(Entry) -> + insert_psk(Entry) + end, Entries). + +insert_psk({PSKIdentity, SharedSecret}) -> + mnesia:write(?TAB, #psk_entry{psk_id = PSKIdentity, shared_secret = SharedSecret}, write). + +trim_crlf(Bin) -> + Size = byte_size(Bin), + case binary:at(Bin, Size - 1) of + ?LF -> + case binary:at(Bin, Size - 2) of + ?CR -> binary:part(Bin, 0, Size - 2); + _ -> binary:part(Bin, 0, Size - 1) + end; + _ -> Bin + end. + +trans(Fun, Args) -> + case ekka_mnesia:transaction(?PSK_SHARD, Fun, Args) of + {atomic, Res} -> Res; + {aborted, Reason} -> {error, Reason} + end. diff --git a/apps/emqx_psk/src/emqx_psk_app.erl b/apps/emqx_psk/src/emqx_psk_app.erl new file mode 100644 index 000000000..95e947291 --- /dev/null +++ b/apps/emqx_psk/src/emqx_psk_app.erl @@ -0,0 +1,30 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_psk_app). + +-behaviour(application). + +-export([ start/2 + , stop/1 + ]). + +start(_Type, _Args) -> + {ok, Sup} = emqx_psk_sup:start_link(), + {ok, Sup}. + +stop(_State) -> + ok. diff --git a/apps/emqx_psk/src/emqx_psk_schema.erl b/apps/emqx_psk/src/emqx_psk_schema.erl new file mode 100644 index 000000000..c8c8ea7e4 --- /dev/null +++ b/apps/emqx_psk/src/emqx_psk_schema.erl @@ -0,0 +1,45 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_psk_schema). + +-behaviour(hocon_schema). + +-include_lib("typerefl/include/types.hrl"). + +-export([ roots/0 + , fields/1 + ]). + +roots() -> []. + +fields("psk") -> + [ {enable, fun enable/1} + , {boot_file, fun boot_file/1} + , {delimiter, fun delimiter/1} + ]. + +enable(type) -> boolean(); +enable(default) -> false; +enable(_) -> undefined. + +boot_file(type) -> binary(); +boot_file(nullable) -> true; +boot_file(_) -> undefined. + +delimiter(type) -> binary(); +delimiter(default) -> <<":">>; +delimiter(_) -> undefined. diff --git a/apps/emqx_psk/src/emqx_psk_sup.erl b/apps/emqx_psk/src/emqx_psk_sup.erl new file mode 100644 index 000000000..2c5181735 --- /dev/null +++ b/apps/emqx_psk/src/emqx_psk_sup.erl @@ -0,0 +1,35 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_psk_sup). + +-behaviour(supervisor). + +-export([start_link/0]). + +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + {ok, {{one_for_one, 10, 3600}, + [#{id => emqx_psk, + start => {emqx_psk, start_link, []}, + restart => permanent, + shutdown => 5000, + type => worker, + modules => [emqx_psk]}]}}. From 2f18f5e8b528c26da5591ab713eecd94f42b4281 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Sat, 18 Sep 2021 17:27:04 +0800 Subject: [PATCH 02/12] chore(psk): improve configuration file --- apps/emqx_psk/etc/emqx_psk.conf | 16 ++++++++++++++-- apps/emqx_psk/src/emqx_psk.erl | 6 +++--- apps/emqx_psk/src/emqx_psk_schema.erl | 8 ++++---- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/apps/emqx_psk/etc/emqx_psk.conf b/apps/emqx_psk/etc/emqx_psk.conf index d964f4e23..61bfa233c 100644 --- a/apps/emqx_psk/etc/emqx_psk.conf +++ b/apps/emqx_psk/etc/emqx_psk.conf @@ -1,7 +1,19 @@ +##-------------------------------------------------------------------- +## EMQ X PSK +##-------------------------------------------------------------------- + psk { + ## Whether to enable the PSK feature. enable = true - # boot_file = {{ platform_data_dir }}/boot.psk + ## If boot file is specified, emqx will import PSKs from the file + ## into the built-in database at startup for use by the runtime. + ## + ## The file has to be structured line-by-line, each line must be in + ## the format: : + ## boot_file = {{ platform_data_dir }}/boot.psk - # delimiter = ":" + ## Specifies the separator for PSKIdentity and SharedSecret in the boot file. + ## The default is colon (:) + ## separator = ":" } diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl index 0968dad4c..844d5bd0b 100644 --- a/apps/emqx_psk/src/emqx_psk.erl +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -148,15 +148,15 @@ is_enable() -> boot_file() -> emqx_config:get([psk, boot_file], undefined). -delimiter() -> - emqx_config:get([psk, delimiter], ?DEFAULT_DELIMITER). +separator() -> + emqx_config:get([psk, separator], ?DEFAULT_DELIMITER). import_psks(SrcFile) -> case file:open(SrcFile, [read, raw, binary, read_ahead]) of {error, Reason} -> {error, Reason}; {ok, Io} -> - case Result = import_psks(Io, delimiter()) of + case Result = import_psks(Io, separator()) of ok -> ignore; {error, Reason} -> ?LOG("Failed to import psk from ~s due to ~p", [SrcFile, Reason]) diff --git a/apps/emqx_psk/src/emqx_psk_schema.erl b/apps/emqx_psk/src/emqx_psk_schema.erl index c8c8ea7e4..b459e1934 100644 --- a/apps/emqx_psk/src/emqx_psk_schema.erl +++ b/apps/emqx_psk/src/emqx_psk_schema.erl @@ -29,7 +29,7 @@ roots() -> []. fields("psk") -> [ {enable, fun enable/1} , {boot_file, fun boot_file/1} - , {delimiter, fun delimiter/1} + , {separator, fun separator/1} ]. enable(type) -> boolean(); @@ -40,6 +40,6 @@ boot_file(type) -> binary(); boot_file(nullable) -> true; boot_file(_) -> undefined. -delimiter(type) -> binary(); -delimiter(default) -> <<":">>; -delimiter(_) -> undefined. +separator(type) -> binary(); +separator(default) -> <<":">>; +separator(_) -> undefined. From 4ca9628899aabe8188c1bf6651bab5732cbfa3f7 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Thu, 23 Sep 2021 10:07:36 +0800 Subject: [PATCH 03/12] chore(psk): rename boot_file to init_file --- apps/emqx_psk/etc/emqx_psk.conf | 6 +++--- apps/emqx_psk/src/emqx_psk.erl | 6 +++--- apps/emqx_psk/src/emqx_psk_schema.erl | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/emqx_psk/etc/emqx_psk.conf b/apps/emqx_psk/etc/emqx_psk.conf index 61bfa233c..4373b3b50 100644 --- a/apps/emqx_psk/etc/emqx_psk.conf +++ b/apps/emqx_psk/etc/emqx_psk.conf @@ -6,14 +6,14 @@ psk { ## Whether to enable the PSK feature. enable = true - ## If boot file is specified, emqx will import PSKs from the file + ## If init file is specified, emqx will import PSKs from the file ## into the built-in database at startup for use by the runtime. ## ## The file has to be structured line-by-line, each line must be in ## the format: : - ## boot_file = {{ platform_data_dir }}/boot.psk + ## init_file = {{ platform_data_dir }}/init.psk - ## Specifies the separator for PSKIdentity and SharedSecret in the boot file. + ## Specifies the separator for PSKIdentity and SharedSecret in the init file. ## The default is colon (:) ## separator = ":" } diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl index 844d5bd0b..e3dfbbc81 100644 --- a/apps/emqx_psk/src/emqx_psk.erl +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -110,7 +110,7 @@ init(_Opts) -> true -> load(); false -> ok end, - case boot_file() of + case init_file() of undefined -> ok; BootFile -> import_psks(BootFile) end, @@ -145,8 +145,8 @@ code_change(_OldVsn, State, _Extra) -> is_enable() -> emqx_config:get([psk, enable]). -boot_file() -> - emqx_config:get([psk, boot_file], undefined). +init_file() -> + emqx_config:get([psk, init_file], undefined). separator() -> emqx_config:get([psk, separator], ?DEFAULT_DELIMITER). diff --git a/apps/emqx_psk/src/emqx_psk_schema.erl b/apps/emqx_psk/src/emqx_psk_schema.erl index b459e1934..0179ced22 100644 --- a/apps/emqx_psk/src/emqx_psk_schema.erl +++ b/apps/emqx_psk/src/emqx_psk_schema.erl @@ -28,7 +28,7 @@ roots() -> []. fields("psk") -> [ {enable, fun enable/1} - , {boot_file, fun boot_file/1} + , {init_file, fun init_file/1} , {separator, fun separator/1} ]. @@ -36,9 +36,9 @@ enable(type) -> boolean(); enable(default) -> false; enable(_) -> undefined. -boot_file(type) -> binary(); -boot_file(nullable) -> true; -boot_file(_) -> undefined. +init_file(type) -> binary(); +init_file(nullable) -> true; +init_file(_) -> undefined. separator(type) -> binary(); separator(default) -> <<":">>; From e9cd75743271bc458f4aa8a2ee02873d0c1cef89 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Thu, 23 Sep 2021 17:41:58 +0800 Subject: [PATCH 04/12] chore(psk): delete old module and update configuration --- apps/emqx/src/emqx_psk.erl | 39 ----------------------------------- apps/emqx/src/emqx_schema.erl | 2 +- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 apps/emqx/src/emqx_psk.erl diff --git a/apps/emqx/src/emqx_psk.erl b/apps/emqx/src/emqx_psk.erl deleted file mode 100644 index 0c5ca2964..000000000 --- a/apps/emqx/src/emqx_psk.erl +++ /dev/null @@ -1,39 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2019-2021 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_psk). - --include("logger.hrl"). - - -%% SSL PSK Callbacks --export([lookup/3]). - --type psk_identity() :: string(). --type psk_user_state() :: term(). - --spec lookup(psk, psk_identity(), psk_user_state()) -> {ok, SharedSecret :: binary()} | error. -lookup(psk, ClientPSKID, _UserState) -> - try emqx_hooks:run_fold('tls_handshake.psk_lookup', [ClientPSKID], not_found) of - SharedSecret when is_binary(SharedSecret) -> {ok, SharedSecret}; - Error -> - ?LOG(error, "Look PSK for PSKID ~p error: ~p", [ClientPSKID, Error]), - error - catch - Except:Error:Stacktrace -> - ?LOG(error, "Lookup PSK failed, ~0p: ~0p", [{Except,Error}, Stacktrace]), - error - end. diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index 27688a868..d42ef4358 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -999,7 +999,7 @@ In case PSK cipher suites are intended, make sure to configured , {"ciphers", ciphers_schema(D("ciphers"))} , {user_lookup_fun, sc(typerefl:alias("string", any()), - #{ default => "emqx_psk:lookup" + #{ default => "emqx_tls_psk:lookup" , converter => fun ?MODULE:parse_user_lookup_fun/1 }) } From 29277b1d6bf6c2615da8bdc60a174bba6b2b70e2 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Thu, 23 Sep 2021 18:24:01 +0800 Subject: [PATCH 05/12] chore(psk): rename boot.psk to init.psk --- apps/emqx_psk/data/{boot.psk => init.psk} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apps/emqx_psk/data/{boot.psk => init.psk} (100%) diff --git a/apps/emqx_psk/data/boot.psk b/apps/emqx_psk/data/init.psk similarity index 100% rename from apps/emqx_psk/data/boot.psk rename to apps/emqx_psk/data/init.psk From d391690505c7c53875290dc646956c80e0388792 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Fri, 24 Sep 2021 09:29:08 +0800 Subject: [PATCH 06/12] chore(psk): fix bugs --- apps/emqx/test/props/prop_emqx_psk.erl | 2 +- apps/emqx_psk/src/emqx_psk.erl | 20 +++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/apps/emqx/test/props/prop_emqx_psk.erl b/apps/emqx/test/props/prop_emqx_psk.erl index 106de3fda..1d2ce5a12 100644 --- a/apps/emqx/test/props/prop_emqx_psk.erl +++ b/apps/emqx/test/props/prop_emqx_psk.erl @@ -33,7 +33,7 @@ prop_lookup() -> ?ALL({ClientPSKID, UserState}, {client_pskid(), user_state()}, begin - case emqx_psk:lookup(psk, ClientPSKID, UserState) of + case emqx_tls_psk:lookup(psk, ClientPSKID, UserState) of {ok, _Result} -> true; error -> true; _Other -> false diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl index e3dfbbc81..7a38201e6 100644 --- a/apps/emqx_psk/src/emqx_psk.erl +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -106,11 +106,11 @@ stop() -> %%-------------------------------------------------------------------- init(_Opts) -> - case is_enable() of + case get_config(enable) of true -> load(); false -> ok end, - case init_file() of + case get_config(init_file) of undefined -> ok; BootFile -> import_psks(BootFile) end, @@ -142,13 +142,11 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%------------------------------------------------------------------------------ -is_enable() -> - emqx_config:get([psk, enable]). - -init_file() -> - emqx_config:get([psk, init_file], undefined). - -separator() -> +get_config(enable) -> + emqx_config:get([psk, enable]); +get_config(init_file) -> + emqx_config:get([psk, init_file], undefined); +get_config(separator) -> emqx_config:get([psk, separator], ?DEFAULT_DELIMITER). import_psks(SrcFile) -> @@ -156,10 +154,10 @@ import_psks(SrcFile) -> {error, Reason} -> {error, Reason}; {ok, Io} -> - case Result = import_psks(Io, separator()) of + case Result = import_psks(Io, get_config(separator)) of ok -> ignore; {error, Reason} -> - ?LOG("Failed to import psk from ~s due to ~p", [SrcFile, Reason]) + ?LOG(warning, "Failed to import psk from ~s due to ~p", [SrcFile, Reason]) end, _ = file:close(Io), Result From 98ecafccebf13f84518de6e9aa94d42e96ac38a4 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Fri, 24 Sep 2021 14:26:46 +0800 Subject: [PATCH 07/12] chore(psk): fix dialyzer --- apps/emqx_psk/src/emqx_psk.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl index 7a38201e6..20ecf50b1 100644 --- a/apps/emqx_psk/src/emqx_psk.erl +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -106,14 +106,14 @@ stop() -> %%-------------------------------------------------------------------- init(_Opts) -> - case get_config(enable) of - true -> load(); - false -> ok - end, - case get_config(init_file) of - undefined -> ok; - BootFile -> import_psks(BootFile) - end, + _ = case get_config(enable) of + true -> load(); + false -> ok + end, + _ = case get_config(init_file) of + undefined -> ok; + InitFile -> import_psks(InitFile) + end, {ok, #{}}. handle_call({import, SrcFile}, _From, State) -> From f325d7c783bbd2dfee762eaf23db8feabcfa4ffd Mon Sep 17 00:00:00 2001 From: zhouzb Date: Sun, 26 Sep 2021 20:37:00 +0800 Subject: [PATCH 08/12] chore(psk): catch timeout exception to avoid crash --- apps/emqx_psk/src/emqx_psk.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl index 20ecf50b1..3934af01f 100644 --- a/apps/emqx_psk/src/emqx_psk.erl +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -91,7 +91,7 @@ on_psk_lookup(PSKIdentity, _UserState) -> end. import(SrcFile) -> - gen_server:call(?MODULE, {import, SrcFile}). + call({import, SrcFile}). -spec start_link() -> {ok, pid()} | ignore | {error, term()}. start_link() -> @@ -220,3 +220,11 @@ trans(Fun, Args) -> {atomic, Res} -> Res; {aborted, Reason} -> {error, Reason} end. + +call(Request) -> + try + gen_server:call(?MODULE, Request, 10000) + catch + exit:{timeout, _Details} -> + {error, timeout} + end. From 84bb486c62956a510a135952906ea0065efafdbb Mon Sep 17 00:00:00 2001 From: zhouzb Date: Mon, 27 Sep 2021 10:53:25 +0800 Subject: [PATCH 09/12] feat(psk): chunk size can be configured and improve code --- apps/emqx_psk/etc/emqx_psk.conf | 3 ++ apps/emqx_psk/src/emqx_psk.erl | 48 ++++++++++++++++++--------- apps/emqx_psk/src/emqx_psk_schema.erl | 21 ++++++++++-- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/apps/emqx_psk/etc/emqx_psk.conf b/apps/emqx_psk/etc/emqx_psk.conf index 4373b3b50..6851946ec 100644 --- a/apps/emqx_psk/etc/emqx_psk.conf +++ b/apps/emqx_psk/etc/emqx_psk.conf @@ -16,4 +16,7 @@ psk { ## Specifies the separator for PSKIdentity and SharedSecret in the init file. ## The default is colon (:) ## separator = ":" + + ## The size of each chunk used to import to the built-in database from psk file + ## chunk_size = 50 } diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl index 3934af01f..9f4be11f1 100644 --- a/apps/emqx_psk/src/emqx_psk.erl +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -40,7 +40,9 @@ ]). -record(psk_entry, {psk_id :: binary(), - shared_secret :: binary()}). + shared_secret :: binary(), + extra :: term() + }). -export([mnesia/1]). @@ -64,6 +66,7 @@ mnesia(boot) -> ok = ekka_mnesia:create_table(?TAB, [ {rlog_shard, ?PSK_SHARD}, + {type, ordered_set}, {disc_copies, [node()]}, {record_name, psk_entry}, {attributes, record_info(fields, psk_entry)}, @@ -108,7 +111,7 @@ stop() -> init(_Opts) -> _ = case get_config(enable) of true -> load(); - false -> ok + false -> ?SLOG(info, #{msg => "emqx_psk_disabled"}) end, _ = case get_config(init_file) of undefined -> ok; @@ -120,15 +123,15 @@ handle_call({import, SrcFile}, _From, State) -> {reply, import_psks(SrcFile), State}; handle_call(Req, _From, State) -> - ?LOG(error, "Unexpected call: ~p", [Req]), - {reply, ignored, State}. + ?SLOG(info, #{msg => "unexpected_call_discarded", req => Req}), + {reply, {error, unexecpted}, State}. handle_cast(Req, State) -> - ?LOG(error, "Unexpected case: ~p", [Req]), + ?SLOG(info, #{msg => "unexpected_cast_discarded", req => Req}), {noreply, State}. handle_info(Info, State) -> - ?LOG(error, "Unexpected info: ~p", [Info]), + ?SLOG(info, #{msg => "unexpected_info_discarded", info => Info}), {noreply, State}. terminate(_Reason, _State) -> @@ -147,27 +150,40 @@ get_config(enable) -> get_config(init_file) -> emqx_config:get([psk, init_file], undefined); get_config(separator) -> - emqx_config:get([psk, separator], ?DEFAULT_DELIMITER). + emqx_config:get([psk, separator], ?DEFAULT_DELIMITER); +get_config(chunk_size) -> + emqx_config:get([psk, chunk_size]). import_psks(SrcFile) -> case file:open(SrcFile, [read, raw, binary, read_ahead]) of {error, Reason} -> {error, Reason}; {ok, Io} -> - case Result = import_psks(Io, get_config(separator)) of - ok -> ignore; + try import_psks(Io, get_config(separator), get_config(chunk_size)) of + ok -> ok; {error, Reason} -> - ?LOG(warning, "Failed to import psk from ~s due to ~p", [SrcFile, Reason]) - end, - _ = file:close(Io), - Result + ?SLOG(error, #{msg => "failed_to_import_psk_file", + file => SrcFile, + reason => Reason}), + {error, Reason} + catch + Class:Reason:Stacktrace -> + ?SLOG(error, #{msg => "failed_to_import_psk_file", + file => SrcFile, + class => Class, + reason => Reason, + stacktrace => Stacktrace}), + {error, Reason} + after + _ = file:close(Io) + end end. -import_psks(Io, Delimiter) -> - case get_psks(Io, Delimiter, 50) of +import_psks(Io, Delimiter, ChunkSize) -> + case get_psks(Io, Delimiter, ChunkSize) of {ok, Entries} -> _ = trans(fun insert_psks/1, Entries), - import_psks(Io, Delimiter); + import_psks(Io, Delimiter, ChunkSize); {eof, Entries} -> _ = trans(fun insert_psks/1, Entries), ok; diff --git a/apps/emqx_psk/src/emqx_psk_schema.erl b/apps/emqx_psk/src/emqx_psk_schema.erl index 0179ced22..979ff5147 100644 --- a/apps/emqx_psk/src/emqx_psk_schema.erl +++ b/apps/emqx_psk/src/emqx_psk_schema.erl @@ -27,19 +27,34 @@ roots() -> []. fields("psk") -> - [ {enable, fun enable/1} - , {init_file, fun init_file/1} - , {separator, fun separator/1} + [ {enable, fun enable/1} + , {init_file, fun init_file/1} + , {separator, fun separator/1} + , {chunk_size, fun chunk_size/1} ]. enable(type) -> boolean(); +enable(desc) -> <<"Whether to enable tls psk support">>; enable(default) -> false; enable(_) -> undefined. init_file(type) -> binary(); +init_file(desc) -> + <<"If init_file is specified, emqx will import PSKs from the file ", + "into the built-in database at startup for use by the runtime. ", + "The file has to be structured line-by-line, each line must be in ", + "the format: :">>; init_file(nullable) -> true; init_file(_) -> undefined. separator(type) -> binary(); +separator(desc) -> + <<"The separator between PSKIdentity and SharedSecret in the psk file">>; separator(default) -> <<":">>; separator(_) -> undefined. + +chunk_size(type) -> integer(); +chunk_size(desc) -> + <<"The size of each chunk used to import to the built-in database from psk file">>; +chunk_size(default) -> 50; +chunk_size(_) -> undefined. From f9aa3457874b8d0aaa11074b29063ee786874312 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Mon, 27 Sep 2021 15:32:56 +0800 Subject: [PATCH 10/12] chore(psk): improve logs for psk --- apps/emqx/src/emqx_tls_psk.erl | 22 ++++++++++++++++------ apps/emqx_psk/src/emqx_psk.erl | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/apps/emqx/src/emqx_tls_psk.erl b/apps/emqx/src/emqx_tls_psk.erl index 7a8238182..d071dbb9a 100644 --- a/apps/emqx/src/emqx_tls_psk.erl +++ b/apps/emqx/src/emqx_tls_psk.erl @@ -26,13 +26,23 @@ -spec lookup(psk, psk_identity(), psk_user_state()) -> {ok, SharedSecret :: binary()} | error. lookup(psk, PSKIdentity, UserState) -> - try emqx_hooks:run_fold('tls_handshake.psk_lookup', [PSKIdentity], UserState) of - SharedSecret when is_binary(SharedSecret) -> {ok, SharedSecret}; - Error -> - ?LOG(error, "Look PSK for PSKID ~p error: ~p", [PSKIdentity, Error]), + try emqx_hooks:run_fold('tls_handshake.psk_lookup', [PSKIdentity, UserState], normal) of + {ok, SharedSecret} when is_binary(SharedSecret) -> + {ok, SharedSecret}; + normal -> + ?SLOG(info, #{msg => "psk_identity_not_found", + psk_identity => PSKIdentity}), + error; + {error, Reason} -> + ?SLOG(warning, #{msg => "psk_identity_not_found", + psk_identity => PSKIdentity, + reason => Reason}), error catch - Except:Error:Stacktrace -> - ?LOG(error, "Lookup PSK failed, ~0p: ~0p", [{Except,Error}, Stacktrace]), + Class:Reason:Stacktrace -> + ?SLOG(error, #{msg => "lookup_psk_failed", + class => Class, + reason => Reason, + stacktrace => Stacktrace}), error end. diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl index 9f4be11f1..cb7daa94e 100644 --- a/apps/emqx_psk/src/emqx_psk.erl +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -88,7 +88,7 @@ unload() -> on_psk_lookup(PSKIdentity, _UserState) -> case mnesia:dirty_read(?TAB, PSKIdentity) of [#psk_entry{shared_secret = SharedSecret}] -> - {stop, SharedSecret}; + {stop, {ok, SharedSecret}}; _ -> ignore end. From 74c9a38e9fe180ca0f0d45019e30cacf45163e5c Mon Sep 17 00:00:00 2001 From: zhouzb Date: Mon, 27 Sep 2021 21:42:32 +0800 Subject: [PATCH 11/12] fix(psk): fix bugs and add test case --- apps/emqx/src/emqx_schema.erl | 2 +- apps/emqx/src/emqx_tls_psk.erl | 4 +- apps/emqx_machine/src/emqx_machine.erl | 1 + apps/emqx_machine/src/emqx_machine_schema.erl | 1 + apps/emqx_psk/etc/emqx_psk.conf | 4 +- apps/emqx_psk/src/emqx_psk.erl | 11 ++- apps/emqx_psk/src/emqx_psk_schema.erl | 2 +- apps/emqx_psk/test/data/init.psk | 2 + apps/emqx_psk/test/emqx_psk_SUITE.erl | 85 +++++++++++++++++++ rebar.config.erl | 1 + 10 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 apps/emqx_psk/test/data/init.psk create mode 100644 apps/emqx_psk/test/emqx_psk_SUITE.erl diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl index d42ef4358..53321cd01 100644 --- a/apps/emqx/src/emqx_schema.erl +++ b/apps/emqx/src/emqx_schema.erl @@ -1300,7 +1300,7 @@ parse_user_lookup_fun(StrConf) -> [ModStr, FunStr] = string:tokens(StrConf, ":"), Mod = list_to_atom(ModStr), Fun = list_to_atom(FunStr), - {fun Mod:Fun/3, <<>>}. + {fun Mod:Fun/3, undefined}. validate_ciphers(Ciphers) -> All = ssl:cipher_suites(all, 'tlsv1.3', openssl) ++ diff --git a/apps/emqx/src/emqx_tls_psk.erl b/apps/emqx/src/emqx_tls_psk.erl index d071dbb9a..baad60385 100644 --- a/apps/emqx/src/emqx_tls_psk.erl +++ b/apps/emqx/src/emqx_tls_psk.erl @@ -25,8 +25,8 @@ -type psk_user_state() :: term(). -spec lookup(psk, psk_identity(), psk_user_state()) -> {ok, SharedSecret :: binary()} | error. -lookup(psk, PSKIdentity, UserState) -> - try emqx_hooks:run_fold('tls_handshake.psk_lookup', [PSKIdentity, UserState], normal) of +lookup(psk, PSKIdentity, _UserState) -> + try emqx_hooks:run_fold('tls_handshake.psk_lookup', [PSKIdentity], normal) of {ok, SharedSecret} when is_binary(SharedSecret) -> {ok, SharedSecret}; normal -> diff --git a/apps/emqx_machine/src/emqx_machine.erl b/apps/emqx_machine/src/emqx_machine.erl index 97125d79f..091ce3250 100644 --- a/apps/emqx_machine/src/emqx_machine.erl +++ b/apps/emqx_machine/src/emqx_machine.erl @@ -149,6 +149,7 @@ reboot_apps() -> , emqx_rule_actions , emqx_authn , emqx_authz + , emqx_psk ]. sorted_reboot_apps() -> diff --git a/apps/emqx_machine/src/emqx_machine_schema.erl b/apps/emqx_machine/src/emqx_machine_schema.erl index 1637ca0e6..9cda0e8c3 100644 --- a/apps/emqx_machine/src/emqx_machine_schema.erl +++ b/apps/emqx_machine/src/emqx_machine_schema.erl @@ -53,6 +53,7 @@ , emqx_prometheus_schema , emqx_rule_engine_schema , emqx_exhook_schema + , emqx_psk_schema ]). namespace() -> undefined. diff --git a/apps/emqx_psk/etc/emqx_psk.conf b/apps/emqx_psk/etc/emqx_psk.conf index 6851946ec..80b29bfd4 100644 --- a/apps/emqx_psk/etc/emqx_psk.conf +++ b/apps/emqx_psk/etc/emqx_psk.conf @@ -4,14 +4,14 @@ psk { ## Whether to enable the PSK feature. - enable = true + enable = false ## If init file is specified, emqx will import PSKs from the file ## into the built-in database at startup for use by the runtime. ## ## The file has to be structured line-by-line, each line must be in ## the format: : - ## init_file = {{ platform_data_dir }}/init.psk + ## init_file = "{{ platform_data_dir }}/init.psk" ## Specifies the separator for PSKIdentity and SharedSecret in the init file. ## The default is colon (:) diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl index cb7daa94e..ce374e31a 100644 --- a/apps/emqx_psk/src/emqx_psk.erl +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -157,6 +157,9 @@ get_config(chunk_size) -> import_psks(SrcFile) -> case file:open(SrcFile, [read, raw, binary, read_ahead]) of {error, Reason} -> + ?SLOG(error, #{msg => "failed_to_open_psk_file", + file => SrcFile, + reason => Reason}), {error, Reason}; {ok, Io} -> try import_psks(Io, get_config(separator), get_config(chunk_size)) of @@ -167,10 +170,10 @@ import_psks(SrcFile) -> reason => Reason}), {error, Reason} catch - Class:Reason:Stacktrace -> + Exception:Reason:Stacktrace -> ?SLOG(error, #{msg => "failed_to_import_psk_file", file => SrcFile, - class => Class, + exception => Exception, reason => Reason, stacktrace => Stacktrace}), {error, Reason} @@ -182,10 +185,10 @@ import_psks(SrcFile) -> import_psks(Io, Delimiter, ChunkSize) -> case get_psks(Io, Delimiter, ChunkSize) of {ok, Entries} -> - _ = trans(fun insert_psks/1, Entries), + _ = trans(fun insert_psks/1, [Entries]), import_psks(Io, Delimiter, ChunkSize); {eof, Entries} -> - _ = trans(fun insert_psks/1, Entries), + _ = trans(fun insert_psks/1, [Entries]), ok; {error, Reaosn} -> {error, Reaosn} diff --git a/apps/emqx_psk/src/emqx_psk_schema.erl b/apps/emqx_psk/src/emqx_psk_schema.erl index 979ff5147..cce51d3fa 100644 --- a/apps/emqx_psk/src/emqx_psk_schema.erl +++ b/apps/emqx_psk/src/emqx_psk_schema.erl @@ -24,7 +24,7 @@ , fields/1 ]). -roots() -> []. +roots() -> ["psk"]. fields("psk") -> [ {enable, fun enable/1} diff --git a/apps/emqx_psk/test/data/init.psk b/apps/emqx_psk/test/data/init.psk new file mode 100644 index 000000000..7e1a1edf5 --- /dev/null +++ b/apps/emqx_psk/test/data/init.psk @@ -0,0 +1,2 @@ +myclient1:8c701116e9127c57a99d5563709af3deaca75563e2c4dd0865701ae839fb6d79 +myclient2:d1e617d3b963757bfc21dad3fea169716c3a2f053f23decaea5cdfaabd04bfc4 diff --git a/apps/emqx_psk/test/emqx_psk_SUITE.erl b/apps/emqx_psk/test/emqx_psk_SUITE.erl new file mode 100644 index 000000000..fa24322a2 --- /dev/null +++ b/apps/emqx_psk/test/emqx_psk_SUITE.erl @@ -0,0 +1,85 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_psk_SUITE). + +-compile(nowarn_export_all). +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +all() -> + emqx_ct:all(?MODULE). + +init_per_suite(Config) -> + meck:new(emqx_config, [non_strict, passthrough, no_history, no_link]), + meck:expect(emqx_config, get, fun([psk, enable]) -> true; + ([psk, chunk_size]) -> 50; + (KeyPath) -> meck:passthrough([KeyPath]) + end), + meck:expect(emqx_config, get, fun([psk, init_file], _) -> + filename:join([code:lib_dir(emqx_psk, test), "data/init.psk"]); + ([psk, separator], _) -> <<":">>; + (KeyPath, Default) -> meck:passthrough([KeyPath, Default]) + end), + emqx_ct_helpers:start_apps([emqx_psk]), + Config. + +end_per_suite(_) -> + meck:unload(emqx_config), + emqx_ct_helpers:stop_apps([emqx_psk]), + ok. + +t_psk_lookup(_) -> + + PSKIdentity1 = <<"myclient1">>, + SharedSecret1 = <<"8c701116e9127c57a99d5563709af3deaca75563e2c4dd0865701ae839fb6d79">>, + ?assertEqual({stop, {ok, SharedSecret1}}, emqx_psk:on_psk_lookup(PSKIdentity1, any)), + + PSKIdentity2 = <<"myclient2">>, + SharedSecret2 = <<"d1e617d3b963757bfc21dad3fea169716c3a2f053f23decaea5cdfaabd04bfc4">>, + ?assertEqual({stop, {ok, SharedSecret2}}, emqx_psk:on_psk_lookup(PSKIdentity2, any)), + + ?assertEqual(ignore, emqx_psk:on_psk_lookup(<<"myclient3">>, any)), + + ClientLookup = fun(psk, undefined, _) -> {ok, SharedSecret1}; + (psk, _, _) -> error + end, + + ClientTLSOpts = #{ versions => ['tlsv1.2'] + , ciphers => ["PSK-AES256-CBC-SHA"] + , psk_identity => "myclient1" + , verify => verify_none + , user_lookup_fun => {ClientLookup, undefined} + }, + + ServerTLSOpts = #{ versions => ['tlsv1.2'] + , ciphers => ["PSK-AES256-CBC-SHA"] + , verify => verify_none + , reuseaddr => true + , user_lookup_fun => {fun emqx_tls_psk:lookup/3, undefined} + }, + emqx_config:put([listeners, ssl ,default, ssl], ServerTLSOpts), + emqx_listeners:restart_listener('ssl:default'), + + {ok, Socket} = ssl:connect("127.0.0.1", 8883, maps:to_list(ClientTLSOpts)), + ssl:close(Socket), + + ClientTLSOpts1 = ClientTLSOpts#{psk_identity => "myclient2"}, + ?assertMatch({error, _}, ssl:connect("127.0.0.1", 8883, maps:to_list(ClientTLSOpts1))), + + ok. + diff --git a/rebar.config.erl b/rebar.config.erl index 4888fafe0..6fb10f265 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -279,6 +279,7 @@ relx_apps(ReleaseType) -> , emqx_retainer , emqx_statsd , emqx_prometheus + , emqx_psk ] ++ [quicer || is_quicer_supported()] ++ [emqx_license || is_enterprise()] From 2cc2cd283172830f29a0dc1d5481aba8cbb74144 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Mon, 27 Sep 2021 22:06:28 +0800 Subject: [PATCH 12/12] chore(psk): return line number rather than the content of line --- apps/emqx_psk/src/emqx_psk.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/emqx_psk/src/emqx_psk.erl b/apps/emqx_psk/src/emqx_psk.erl index ce374e31a..9ea25cdeb 100644 --- a/apps/emqx_psk/src/emqx_psk.erl +++ b/apps/emqx_psk/src/emqx_psk.erl @@ -162,7 +162,7 @@ import_psks(SrcFile) -> reason => Reason}), {error, Reason}; {ok, Io} -> - try import_psks(Io, get_config(separator), get_config(chunk_size)) of + try import_psks(Io, get_config(separator), get_config(chunk_size), 0) of ok -> ok; {error, Reason} -> ?SLOG(error, #{msg => "failed_to_import_psk_file", @@ -182,14 +182,16 @@ import_psks(SrcFile) -> end end. -import_psks(Io, Delimiter, ChunkSize) -> +import_psks(Io, Delimiter, ChunkSize, NChunk) -> case get_psks(Io, Delimiter, ChunkSize) of {ok, Entries} -> _ = trans(fun insert_psks/1, [Entries]), - import_psks(Io, Delimiter, ChunkSize); + import_psks(Io, Delimiter, ChunkSize, NChunk + 1); {eof, Entries} -> _ = trans(fun insert_psks/1, [Entries]), ok; + {error, {bad_format, {line, N}}} -> + {error, {bad_format, {line, NChunk * ChunkSize + N}}}; {error, Reaosn} -> {error, Reaosn} end. @@ -207,7 +209,7 @@ get_psks(Io, Delimiter, Remaining, Acc) -> NSharedSecret = trim_crlf(SharedSecret), get_psks(Io, Delimiter, Remaining - 1, [{PSKIdentity, NSharedSecret} | Acc]); _ -> - {error, {bad_format, Line}} + {error, {bad_format, {line, length(Acc) + 1}}} end; eof -> {eof, Acc};