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

5.0 provide defaults for ssl files
This commit is contained in:
Zaiming (Stone) Shi 2022-04-19 11:51:39 +01:00 committed by GitHub
commit fb7c7dffb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 55 additions and 63 deletions

View File

@ -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",

View File

@ -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).
%% @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).
file_content_as_options([], SSL) ->
{ok, SSL};
file_content_as_options([Key | Keys], 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.

View File

@ -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.

View File

@ -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() ->

View File

@ -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">>
}
},