Selaa lähdekoodia

Merge pull request #7527 from zmstone/0405-5.0-provide-defaults-for-ssl-files

5.0 provide defaults for ssl files
Zaiming (Stone) Shi 3 vuotta sitten
vanhempi
commit
fb7c7dffb7

+ 16 - 2
apps/emqx/src/emqx_schema.erl

@@ -2071,7 +2071,13 @@ common_ssl_opts_schema(Defaults) ->
 %% @doc Make schema for SSL listener options.
 %% When it's for ranch listener, an extra field `handshake_timeout' is added.
 -spec server_ssl_opts_schema(map(), boolean()) -> hocon_schema:field_schema().
-server_ssl_opts_schema(Defaults, IsRanchListener) ->
+server_ssl_opts_schema(Defaults1, IsRanchListener) ->
+    Defaults0 = #{
+        cacertfile => emqx:cert_file("cacert.pem"),
+        certfile => emqx:cert_file("cert.pem"),
+        keyfile => emqx:cert_file("key.pem")
+    },
+    Defaults = maps:merge(Defaults0, Defaults1),
     D = fun(Field) -> maps:get(to_atom(Field), Defaults, undefined) end,
     Df = fun(Field, Default) -> maps:get(to_atom(Field), Defaults, Default) end,
     common_ssl_opts_schema(Defaults) ++
@@ -2148,7 +2154,15 @@ server_ssl_opts_schema(Defaults, IsRanchListener) ->
 
 %% @doc Make schema for SSL client.
 -spec client_ssl_opts_schema(map()) -> hocon_schema:field_schema().
-client_ssl_opts_schema(Defaults) ->
+client_ssl_opts_schema(Defaults1) ->
+    %% assert
+    true = lists:all(fun(K) -> is_atom(K) end, maps:keys(Defaults1)),
+    Defaults0 = #{
+        cacertfile => emqx:cert_file("cacert.pem"),
+        certfile => emqx:cert_file("client-cert.pem"),
+        keyfile => emqx:cert_file("client-key.pem")
+    },
+    Defaults = maps:merge(Defaults0, Defaults1),
     common_ssl_opts_schema(Defaults) ++
         [
             {"server_name_indication",

+ 27 - 23
apps/emqx/src/emqx_tls_lib.erl

@@ -31,7 +31,8 @@
 -export([
     ensure_ssl_files/2,
     delete_ssl_files/3,
-    file_content_as_options/1
+    drop_invalid_certs/1,
+    is_valid_pem_file/1
 ]).
 
 -export([
@@ -40,10 +41,11 @@
 
 -include("logger.hrl").
 
--define(IS_TRUE(Val), ((Val =:= true) or (Val =:= <<"true">>))).
--define(IS_FALSE(Val), ((Val =:= false) or (Val =:= <<"false">>))).
+-define(IS_TRUE(Val), ((Val =:= true) orelse (Val =:= <<"true">>))).
+-define(IS_FALSE(Val), ((Val =:= false) orelse (Val =:= <<"false">>))).
 
 -define(SSL_FILE_OPT_NAMES, [<<"keyfile">>, <<"certfile">>, <<"cacertfile">>]).
+-define(SSL_FILE_OPT_NAMES_A, [keyfile, certfile, cacertfile]).
 
 %% non-empty string
 -define(IS_STRING(L), (is_list(L) andalso L =/= [] andalso is_integer(hd(L)))).
@@ -398,35 +400,37 @@ pem_file_name(Dir, Key, Pem) ->
 hex_str(Bin) ->
     iolist_to_binary([io_lib:format("~2.16.0b", [X]) || <<X:8>> <= Bin]).
 
+%% @doc Returns 'true' when the file is a valid pem, otherwise {error, Reason}.
 is_valid_pem_file(Path) ->
     case file:read_file(Path) of
         {ok, Pem} -> is_pem(Pem) orelse {error, not_pem};
         {error, Reason} -> {error, Reason}
     end.
 
-%% @doc This is to return SSL file content in management APIs.
-file_content_as_options(undefined) ->
-    undefined;
-file_content_as_options(#{<<"enable">> := False} = SSL) when ?IS_FALSE(False) ->
-    {ok, maps:without(?SSL_FILE_OPT_NAMES, SSL)};
-file_content_as_options(#{<<"enable">> := True} = SSL) when ?IS_TRUE(True) ->
-    file_content_as_options(?SSL_FILE_OPT_NAMES, SSL).
-
-file_content_as_options([], SSL) ->
-    {ok, SSL};
-file_content_as_options([Key | Keys], SSL) ->
+%% @doc Input and output are both HOCON-checked maps, with invalid SSL
+%% file options dropped.
+%% This is to give a feedback to the front-end or management API caller
+%% so they are forced to upload a cert file, or use an existing file path.
+-spec drop_invalid_certs(map()) -> map().
+drop_invalid_certs(#{enable := False} = SSL) when ?IS_FALSE(False) ->
+    maps:without(?SSL_FILE_OPT_NAMES_A, SSL);
+drop_invalid_certs(#{<<"enable">> := False} = SSL) when ?IS_FALSE(False) ->
+    maps:without(?SSL_FILE_OPT_NAMES, SSL);
+drop_invalid_certs(#{enable := True} = SSL) when ?IS_TRUE(True) ->
+    drop_invalid_certs(?SSL_FILE_OPT_NAMES_A, SSL);
+drop_invalid_certs(#{<<"enable">> := True} = SSL) when ?IS_TRUE(True) ->
+    drop_invalid_certs(?SSL_FILE_OPT_NAMES, SSL).
+
+drop_invalid_certs([], SSL) ->
+    SSL;
+drop_invalid_certs([Key | Keys], SSL) ->
     case maps:get(Key, SSL, undefined) of
         undefined ->
-            file_content_as_options(Keys, SSL);
+            drop_invalid_certs(Keys, SSL);
         Path ->
-            case file:read_file(Path) of
-                {ok, Bin} ->
-                    file_content_as_options(Keys, SSL#{Key => Bin});
-                {error, Reason} ->
-                    {error, #{
-                        file_path => Path,
-                        reason => Reason
-                    }}
+            case is_valid_pem_file(Path) of
+                true -> SSL;
+                {error, _} -> maps:without([Key], SSL)
             end
     end.
 

+ 2 - 19
apps/emqx_authn/src/emqx_authn_api.erl

@@ -1202,25 +1202,8 @@ fill_defaults(Configs) when is_list(Configs) ->
 fill_defaults(Config) ->
     emqx_authn:check_config(Config, #{only_fill_defaults => true}).
 
-convert_certs(#{ssl := #{enable := true} = SSLOpts} = Config) ->
-    NSSLOpts = lists:foldl(
-        fun(K, Acc) ->
-            case maps:get(K, Acc, undefined) of
-                undefined ->
-                    Acc;
-                Filename ->
-                    case file:read_file(Filename) of
-                        {ok, Bin} ->
-                            Acc#{K => Bin};
-                        {error, _} ->
-                            Acc#{K => Filename}
-                    end
-            end
-        end,
-        SSLOpts,
-        [certfile, keyfile, cacertfile]
-    ),
-    Config#{ssl => NSSLOpts};
+convert_certs(#{ssl := SSL} = Config) when SSL =/= undefined ->
+    Config#{ssl := emqx_tls_lib:drop_invalid_certs(SSL)};
 convert_certs(Config) ->
     Config.
 

+ 5 - 11
apps/emqx_authz/src/emqx_authz_api_sources.erl

@@ -214,7 +214,7 @@ sources(get, _) ->
                         ])
                 end;
             (Source, AccIn) ->
-                lists:append(AccIn, [read_certs(Source)])
+                lists:append(AccIn, [drop_invalid_certs(Source)])
         end,
         [],
         get_raw_sources()
@@ -248,7 +248,7 @@ source(get, #{bindings := #{type := Type}}) ->
                     }}
             end;
         [Source] ->
-            {200, read_certs(Source)}
+            {200, drop_invalid_certs(Source)}
     end;
 source(put, #{bindings := #{type := <<"file">>}, body := #{<<"type">> := <<"file">>} = Body}) ->
     update_authz_file(Body);
@@ -498,15 +498,9 @@ update_config(Cmd, Sources) ->
             }}
     end.
 
-read_certs(#{<<"ssl">> := SSL} = Source) ->
-    case emqx_tls_lib:file_content_as_options(SSL) of
-        {error, Reason} ->
-            ?SLOG(error, Reason#{msg => failed_to_read_ssl_file}),
-            throw(failed_to_read_ssl_file);
-        {ok, NewSSL} ->
-            Source#{<<"ssl">> => NewSSL}
-    end;
-read_certs(Source) ->
+drop_invalid_certs(#{<<"ssl">> := SSL} = Source) when SSL =/= undefined ->
+    Source#{<<"ssl">> => emqx_tls_lib:drop_invalid_certs(SSL)};
+drop_invalid_certs(Source) ->
     Source.
 
 parameters_field() ->

+ 6 - 9
apps/emqx_authz/test/emqx_authz_api_sources_SUITE.erl

@@ -94,9 +94,6 @@
         >>
 }).
 
--define(MATCH_RSA_KEY, <<"-----BEGIN RSA PRIVATE KEY", _/binary>>).
--define(MATCH_CERT, <<"-----BEGIN CERTIFICATE", _/binary>>).
-
 all() ->
     emqx_common_test_helpers:all(?MODULE).
 
@@ -279,9 +276,9 @@ t_api(_) ->
             <<"type">> := <<"mongodb">>,
             <<"ssl">> := #{
                 <<"enable">> := <<"true">>,
-                <<"cacertfile">> := ?MATCH_CERT,
-                <<"certfile">> := ?MATCH_CERT,
-                <<"keyfile">> := ?MATCH_RSA_KEY,
+                <<"cacertfile">> := _,
+                <<"certfile">> := _,
+                <<"keyfile">> := _,
                 <<"verify">> := <<"verify_none">>
             }
         },
@@ -313,9 +310,9 @@ t_api(_) ->
             <<"type">> := <<"mongodb">>,
             <<"ssl">> := #{
                 <<"enable">> := <<"true">>,
-                <<"cacertfile">> := ?MATCH_CERT,
-                <<"certfile">> := ?MATCH_CERT,
-                <<"keyfile">> := ?MATCH_RSA_KEY,
+                <<"cacertfile">> := _,
+                <<"certfile">> := _,
+                <<"keyfile">> := _,
                 <<"verify">> := <<"verify_none">>
             }
         },