emqx_restricted_shell.erl 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2021-2023 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_restricted_shell).
  17. -export([local_allowed/3, non_local_allowed/3]).
  18. -export([set_prompt_func/0, prompt_func/1]).
  19. -export([lock/0, unlock/0, is_locked/0]).
  20. -include_lib("emqx/include/logger.hrl").
  21. -define(APP, 'emqx_machine').
  22. -define(IS_LOCKED, 'restricted.is_locked').
  23. -define(MAX_HEAP_SIZE, 1024 * 1024 * 1).
  24. -define(MAX_ARGS_SIZE, 1024 * 10).
  25. -define(RED_BG, "\e[48;2;184;0;0m").
  26. -define(RESET, "\e[0m").
  27. -define(LOCAL_PROHIBITED, [halt, q]).
  28. -define(REMOTE_PROHIBITED, [{erlang, halt}, {c, q}, {init, stop}, {init, restart}, {init, reboot}]).
  29. is_locked() ->
  30. {ok, false} =/= application:get_env(?APP, ?IS_LOCKED).
  31. lock() -> application:set_env(?APP, ?IS_LOCKED, true).
  32. unlock() -> application:set_env(?APP, ?IS_LOCKED, false).
  33. set_prompt_func() ->
  34. shell:prompt_func({?MODULE, prompt_func}).
  35. prompt_func(PropList) ->
  36. Line = proplists:get_value(history, PropList, 1),
  37. Version = emqx_release:version(),
  38. Prefix = emqx_release:edition_vsn_prefix(),
  39. case is_alive() of
  40. true -> io_lib:format(<<"~ts~ts(~s)~w> ">>, [Prefix, Version, node(), Line]);
  41. false -> io_lib:format(<<"~ts~ts ~w> ">>, [Prefix, Version, Line])
  42. end.
  43. local_allowed(MF, Args, State) ->
  44. Allowed = check_allowed(MF, ?LOCAL_PROHIBITED),
  45. log(Allowed, MF, Args),
  46. {is_allowed(Allowed), State}.
  47. non_local_allowed(MF, Args, State) ->
  48. Allow = check_allowed(MF, ?REMOTE_PROHIBITED),
  49. log(Allow, MF, Args),
  50. {is_allowed(Allow), State}.
  51. check_allowed(MF, NotAllowed) ->
  52. case {lists:member(MF, NotAllowed), is_locked()} of
  53. {true, false} -> exempted;
  54. {true, true} -> prohibited;
  55. {false, _} -> ignore
  56. end.
  57. is_allowed(prohibited) -> false;
  58. is_allowed(_) -> true.
  59. limit_warning(MF, Args) ->
  60. max_heap_size_warning(MF, Args),
  61. max_args_warning(MF, Args).
  62. max_args_warning(MF, Args) ->
  63. ArgsSize = erts_debug:flat_size(Args),
  64. case ArgsSize < ?MAX_ARGS_SIZE of
  65. true ->
  66. ok;
  67. false ->
  68. warning("[WARNING] current_args_size:~w, max_args_size:~w", [ArgsSize, ?MAX_ARGS_SIZE]),
  69. ?SLOG(warning, #{
  70. msg => "execute_function_in_shell_max_args_size",
  71. function => MF,
  72. %%args => Args,
  73. args_size => ArgsSize,
  74. max_heap_size => ?MAX_ARGS_SIZE
  75. })
  76. end.
  77. max_heap_size_warning(MF, Args) ->
  78. {heap_size, HeapSize} = erlang:process_info(self(), heap_size),
  79. case HeapSize < ?MAX_HEAP_SIZE of
  80. true ->
  81. ok;
  82. false ->
  83. warning("[WARNING] current_heap_size:~w, max_heap_size_warning:~w", [
  84. HeapSize, ?MAX_HEAP_SIZE
  85. ]),
  86. ?SLOG(warning, #{
  87. msg => "shell_process_exceed_max_heap_size",
  88. current_heap_size => HeapSize,
  89. function => MF,
  90. args => Args,
  91. max_heap_size => ?MAX_HEAP_SIZE
  92. })
  93. end.
  94. log(prohibited, MF, Args) ->
  95. warning("DANGEROUS FUNCTION: FORBIDDEN IN SHELL!!!!!", []),
  96. ?SLOG(error, #{msg => "execute_function_in_shell_prohibited", function => MF, args => Args});
  97. log(exempted, MF, Args) ->
  98. limit_warning(MF, Args),
  99. ?SLOG(error, #{
  100. msg => "execute_dangerous_function_in_shell_exempted", function => MF, args => Args
  101. });
  102. log(ignore, MF, Args) ->
  103. limit_warning(MF, Args).
  104. warning(Format, Args) ->
  105. io:format(?RED_BG ++ Format ++ ?RESET ++ "~n", Args).