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:
commit
fb7c7dffb7
|
@ -2071,7 +2071,13 @@ common_ssl_opts_schema(Defaults) ->
|
||||||
%% @doc Make schema for SSL listener options.
|
%% @doc Make schema for SSL listener options.
|
||||||
%% When it's for ranch listener, an extra field `handshake_timeout' is added.
|
%% When it's for ranch listener, an extra field `handshake_timeout' is added.
|
||||||
-spec server_ssl_opts_schema(map(), boolean()) -> hocon_schema:field_schema().
|
-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,
|
D = fun(Field) -> maps:get(to_atom(Field), Defaults, undefined) end,
|
||||||
Df = fun(Field, Default) -> maps:get(to_atom(Field), Defaults, Default) end,
|
Df = fun(Field, Default) -> maps:get(to_atom(Field), Defaults, Default) end,
|
||||||
common_ssl_opts_schema(Defaults) ++
|
common_ssl_opts_schema(Defaults) ++
|
||||||
|
@ -2148,7 +2154,15 @@ server_ssl_opts_schema(Defaults, IsRanchListener) ->
|
||||||
|
|
||||||
%% @doc Make schema for SSL client.
|
%% @doc Make schema for SSL client.
|
||||||
-spec client_ssl_opts_schema(map()) -> hocon_schema:field_schema().
|
-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) ++
|
common_ssl_opts_schema(Defaults) ++
|
||||||
[
|
[
|
||||||
{"server_name_indication",
|
{"server_name_indication",
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
-export([
|
-export([
|
||||||
ensure_ssl_files/2,
|
ensure_ssl_files/2,
|
||||||
delete_ssl_files/3,
|
delete_ssl_files/3,
|
||||||
file_content_as_options/1
|
drop_invalid_certs/1,
|
||||||
|
is_valid_pem_file/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
|
@ -40,10 +41,11 @@
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-define(IS_TRUE(Val), ((Val =:= true) or (Val =:= <<"true">>))).
|
-define(IS_TRUE(Val), ((Val =:= true) orelse (Val =:= <<"true">>))).
|
||||||
-define(IS_FALSE(Val), ((Val =:= false) or (Val =:= <<"false">>))).
|
-define(IS_FALSE(Val), ((Val =:= false) orelse (Val =:= <<"false">>))).
|
||||||
|
|
||||||
-define(SSL_FILE_OPT_NAMES, [<<"keyfile">>, <<"certfile">>, <<"cacertfile">>]).
|
-define(SSL_FILE_OPT_NAMES, [<<"keyfile">>, <<"certfile">>, <<"cacertfile">>]).
|
||||||
|
-define(SSL_FILE_OPT_NAMES_A, [keyfile, certfile, cacertfile]).
|
||||||
|
|
||||||
%% non-empty string
|
%% non-empty string
|
||||||
-define(IS_STRING(L), (is_list(L) andalso L =/= [] andalso is_integer(hd(L)))).
|
-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) ->
|
hex_str(Bin) ->
|
||||||
iolist_to_binary([io_lib:format("~2.16.0b", [X]) || <<X:8>> <= 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) ->
|
is_valid_pem_file(Path) ->
|
||||||
case file:read_file(Path) of
|
case file:read_file(Path) of
|
||||||
{ok, Pem} -> is_pem(Pem) orelse {error, not_pem};
|
{ok, Pem} -> is_pem(Pem) orelse {error, not_pem};
|
||||||
{error, Reason} -> {error, Reason}
|
{error, Reason} -> {error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc This is to return SSL file content in management APIs.
|
%% @doc Input and output are both HOCON-checked maps, with invalid SSL
|
||||||
file_content_as_options(undefined) ->
|
%% file options dropped.
|
||||||
undefined;
|
%% This is to give a feedback to the front-end or management API caller
|
||||||
file_content_as_options(#{<<"enable">> := False} = SSL) when ?IS_FALSE(False) ->
|
%% so they are forced to upload a cert file, or use an existing file path.
|
||||||
{ok, maps:without(?SSL_FILE_OPT_NAMES, SSL)};
|
-spec drop_invalid_certs(map()) -> map().
|
||||||
file_content_as_options(#{<<"enable">> := True} = SSL) when ?IS_TRUE(True) ->
|
drop_invalid_certs(#{enable := False} = SSL) when ?IS_FALSE(False) ->
|
||||||
file_content_as_options(?SSL_FILE_OPT_NAMES, SSL).
|
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) ->
|
drop_invalid_certs([], SSL) ->
|
||||||
{ok, SSL};
|
SSL;
|
||||||
file_content_as_options([Key | Keys], SSL) ->
|
drop_invalid_certs([Key | Keys], SSL) ->
|
||||||
case maps:get(Key, SSL, undefined) of
|
case maps:get(Key, SSL, undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
file_content_as_options(Keys, SSL);
|
drop_invalid_certs(Keys, SSL);
|
||||||
Path ->
|
Path ->
|
||||||
case file:read_file(Path) of
|
case is_valid_pem_file(Path) of
|
||||||
{ok, Bin} ->
|
true -> SSL;
|
||||||
file_content_as_options(Keys, SSL#{Key => Bin});
|
{error, _} -> maps:without([Key], SSL)
|
||||||
{error, Reason} ->
|
|
||||||
{error, #{
|
|
||||||
file_path => Path,
|
|
||||||
reason => Reason
|
|
||||||
}}
|
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
|
@ -1202,25 +1202,8 @@ fill_defaults(Configs) when is_list(Configs) ->
|
||||||
fill_defaults(Config) ->
|
fill_defaults(Config) ->
|
||||||
emqx_authn:check_config(Config, #{only_fill_defaults => true}).
|
emqx_authn:check_config(Config, #{only_fill_defaults => true}).
|
||||||
|
|
||||||
convert_certs(#{ssl := #{enable := true} = SSLOpts} = Config) ->
|
convert_certs(#{ssl := SSL} = Config) when SSL =/= undefined ->
|
||||||
NSSLOpts = lists:foldl(
|
Config#{ssl := emqx_tls_lib:drop_invalid_certs(SSL)};
|
||||||
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(Config) ->
|
convert_certs(Config) ->
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -214,7 +214,7 @@ sources(get, _) ->
|
||||||
])
|
])
|
||||||
end;
|
end;
|
||||||
(Source, AccIn) ->
|
(Source, AccIn) ->
|
||||||
lists:append(AccIn, [read_certs(Source)])
|
lists:append(AccIn, [drop_invalid_certs(Source)])
|
||||||
end,
|
end,
|
||||||
[],
|
[],
|
||||||
get_raw_sources()
|
get_raw_sources()
|
||||||
|
@ -248,7 +248,7 @@ source(get, #{bindings := #{type := Type}}) ->
|
||||||
}}
|
}}
|
||||||
end;
|
end;
|
||||||
[Source] ->
|
[Source] ->
|
||||||
{200, read_certs(Source)}
|
{200, drop_invalid_certs(Source)}
|
||||||
end;
|
end;
|
||||||
source(put, #{bindings := #{type := <<"file">>}, body := #{<<"type">> := <<"file">>} = Body}) ->
|
source(put, #{bindings := #{type := <<"file">>}, body := #{<<"type">> := <<"file">>} = Body}) ->
|
||||||
update_authz_file(Body);
|
update_authz_file(Body);
|
||||||
|
@ -498,15 +498,9 @@ update_config(Cmd, Sources) ->
|
||||||
}}
|
}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
read_certs(#{<<"ssl">> := SSL} = Source) ->
|
drop_invalid_certs(#{<<"ssl">> := SSL} = Source) when SSL =/= undefined ->
|
||||||
case emqx_tls_lib:file_content_as_options(SSL) of
|
Source#{<<"ssl">> => emqx_tls_lib:drop_invalid_certs(SSL)};
|
||||||
{error, Reason} ->
|
drop_invalid_certs(Source) ->
|
||||||
?SLOG(error, Reason#{msg => failed_to_read_ssl_file}),
|
|
||||||
throw(failed_to_read_ssl_file);
|
|
||||||
{ok, NewSSL} ->
|
|
||||||
Source#{<<"ssl">> => NewSSL}
|
|
||||||
end;
|
|
||||||
read_certs(Source) ->
|
|
||||||
Source.
|
Source.
|
||||||
|
|
||||||
parameters_field() ->
|
parameters_field() ->
|
||||||
|
|
|
@ -94,9 +94,6 @@
|
||||||
>>
|
>>
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-define(MATCH_RSA_KEY, <<"-----BEGIN RSA PRIVATE KEY", _/binary>>).
|
|
||||||
-define(MATCH_CERT, <<"-----BEGIN CERTIFICATE", _/binary>>).
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
|
@ -279,9 +276,9 @@ t_api(_) ->
|
||||||
<<"type">> := <<"mongodb">>,
|
<<"type">> := <<"mongodb">>,
|
||||||
<<"ssl">> := #{
|
<<"ssl">> := #{
|
||||||
<<"enable">> := <<"true">>,
|
<<"enable">> := <<"true">>,
|
||||||
<<"cacertfile">> := ?MATCH_CERT,
|
<<"cacertfile">> := _,
|
||||||
<<"certfile">> := ?MATCH_CERT,
|
<<"certfile">> := _,
|
||||||
<<"keyfile">> := ?MATCH_RSA_KEY,
|
<<"keyfile">> := _,
|
||||||
<<"verify">> := <<"verify_none">>
|
<<"verify">> := <<"verify_none">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -313,9 +310,9 @@ t_api(_) ->
|
||||||
<<"type">> := <<"mongodb">>,
|
<<"type">> := <<"mongodb">>,
|
||||||
<<"ssl">> := #{
|
<<"ssl">> := #{
|
||||||
<<"enable">> := <<"true">>,
|
<<"enable">> := <<"true">>,
|
||||||
<<"cacertfile">> := ?MATCH_CERT,
|
<<"cacertfile">> := _,
|
||||||
<<"certfile">> := ?MATCH_CERT,
|
<<"certfile">> := _,
|
||||||
<<"keyfile">> := ?MATCH_RSA_KEY,
|
<<"keyfile">> := _,
|
||||||
<<"verify">> := <<"verify_none">>
|
<<"verify">> := <<"verify_none">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue