refactor: delete default listeners from default config
The new config overriding rule is very much confusing for people who wants to persist listener config changes made from dashboard This commit moves the default values from default config file to schema source code. In order to support build-time cert path at runtime, there is also a naive environment variable interplation feature added.
This commit is contained in:
parent
c58ffce75f
commit
b0f3a654ee
|
@ -1,43 +0,0 @@
|
|||
listeners.tcp.default {
|
||||
bind = "0.0.0.0:1883"
|
||||
max_connections = 1024000
|
||||
}
|
||||
|
||||
listeners.ssl.default {
|
||||
bind = "0.0.0.0:8883"
|
||||
max_connections = 512000
|
||||
ssl_options {
|
||||
keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
}
|
||||
}
|
||||
|
||||
listeners.ws.default {
|
||||
bind = "0.0.0.0:8083"
|
||||
max_connections = 1024000
|
||||
websocket.mqtt_path = "/mqtt"
|
||||
}
|
||||
|
||||
listeners.wss.default {
|
||||
bind = "0.0.0.0:8084"
|
||||
max_connections = 512000
|
||||
websocket.mqtt_path = "/mqtt"
|
||||
ssl_options {
|
||||
keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
}
|
||||
}
|
||||
|
||||
# listeners.quic.default {
|
||||
# enabled = true
|
||||
# bind = "0.0.0.0:14567"
|
||||
# max_connections = 1024000
|
||||
# ssl_options {
|
||||
# verify = verify_none
|
||||
# keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
||||
# certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
||||
# cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
||||
# }
|
||||
# }
|
|
@ -779,6 +779,7 @@ fields("listeners") ->
|
|||
map(name, ref("mqtt_tcp_listener")),
|
||||
#{
|
||||
desc => ?DESC(fields_listeners_tcp),
|
||||
default => default_listener(tcp),
|
||||
required => {false, recursively}
|
||||
}
|
||||
)},
|
||||
|
@ -787,6 +788,7 @@ fields("listeners") ->
|
|||
map(name, ref("mqtt_ssl_listener")),
|
||||
#{
|
||||
desc => ?DESC(fields_listeners_ssl),
|
||||
default => default_listener(ssl),
|
||||
required => {false, recursively}
|
||||
}
|
||||
)},
|
||||
|
@ -795,6 +797,7 @@ fields("listeners") ->
|
|||
map(name, ref("mqtt_ws_listener")),
|
||||
#{
|
||||
desc => ?DESC(fields_listeners_ws),
|
||||
default => default_listener(ws),
|
||||
required => {false, recursively}
|
||||
}
|
||||
)},
|
||||
|
@ -803,6 +806,7 @@ fields("listeners") ->
|
|||
map(name, ref("mqtt_wss_listener")),
|
||||
#{
|
||||
desc => ?DESC(fields_listeners_wss),
|
||||
default => default_listener(wss),
|
||||
required => {false, recursively}
|
||||
}
|
||||
)},
|
||||
|
@ -3083,3 +3087,52 @@ assert_required_field(Conf, Key, ErrorMessage) ->
|
|||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
default_listener(tcp) ->
|
||||
#{
|
||||
<<"default">> =>
|
||||
#{
|
||||
<<"bind">> => <<"0.0.0.0:1883">>,
|
||||
<<"max_connections">> => 1024000
|
||||
}
|
||||
};
|
||||
default_listener(ws) ->
|
||||
#{
|
||||
<<"default">> =>
|
||||
#{
|
||||
<<"bind">> => <<"0.0.0.0:8083">>,
|
||||
<<"max_connections">> => 1024000,
|
||||
<<"websocket">> => #{<<"mqtt_path">> => <<"/mqtt">>}
|
||||
}
|
||||
};
|
||||
default_listener(SSLListener) ->
|
||||
%% The env variable is resolved in emqx_tls_lib
|
||||
CertFile = fun(Name) ->
|
||||
iolist_to_binary("${EMQX_ETC_DIR}/" ++ filename:join(["certs", Name]))
|
||||
end,
|
||||
SslOptions = #{
|
||||
<<"cacertfile">> => CertFile(<<"cacert.pem">>),
|
||||
<<"certfile">> => CertFile(<<"cert.pem">>),
|
||||
<<"keyfile">> => CertFile(<<"key.pem">>)
|
||||
},
|
||||
case SSLListener of
|
||||
ssl ->
|
||||
#{
|
||||
<<"default">> =>
|
||||
#{
|
||||
<<"bind">> => <<"0.0.0.0:8883">>,
|
||||
<<"max_connections">> => 512000,
|
||||
<<"ssl_options">> => SslOptions
|
||||
}
|
||||
};
|
||||
wss ->
|
||||
#{
|
||||
<<"default">> =>
|
||||
#{
|
||||
<<"bind">> => <<"0.0.0.0:8084">>,
|
||||
<<"max_connections">> => 512000,
|
||||
<<"ssl_options">> => SslOptions,
|
||||
<<"websocket">> => #{<<"mqtt_path">> => <<"/mqtt">>}
|
||||
}
|
||||
}
|
||||
end.
|
||||
|
|
|
@ -309,19 +309,19 @@ ensure_ssl_files(Dir, SSL, Opts) ->
|
|||
case ensure_ssl_file_key(SSL, RequiredKeys) of
|
||||
ok ->
|
||||
KeyPaths = ?SSL_FILE_OPT_PATHS ++ ?SSL_FILE_OPT_PATHS_A,
|
||||
ensure_ssl_files(Dir, SSL, KeyPaths, Opts);
|
||||
ensure_ssl_files_per_key(Dir, SSL, KeyPaths, Opts);
|
||||
{error, _} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
ensure_ssl_files(_Dir, SSL, [], _Opts) ->
|
||||
ensure_ssl_files_per_key(_Dir, SSL, [], _Opts) ->
|
||||
{ok, SSL};
|
||||
ensure_ssl_files(Dir, SSL, [KeyPath | KeyPaths], Opts) ->
|
||||
ensure_ssl_files_per_key(Dir, SSL, [KeyPath | KeyPaths], Opts) ->
|
||||
case
|
||||
ensure_ssl_file(Dir, KeyPath, SSL, emqx_utils_maps:deep_get(KeyPath, SSL, undefined), Opts)
|
||||
of
|
||||
{ok, NewSSL} ->
|
||||
ensure_ssl_files(Dir, NewSSL, KeyPaths, Opts);
|
||||
ensure_ssl_files_per_key(Dir, NewSSL, KeyPaths, Opts);
|
||||
{error, Reason} ->
|
||||
{error, Reason#{which_options => [KeyPath]}}
|
||||
end.
|
||||
|
@ -347,7 +347,8 @@ delete_ssl_files(Dir, NewOpts0, OldOpts0) ->
|
|||
delete_old_file(New, Old) when New =:= Old -> ok;
|
||||
delete_old_file(_New, _Old = undefined) ->
|
||||
ok;
|
||||
delete_old_file(_New, Old) ->
|
||||
delete_old_file(_New, Old0) ->
|
||||
Old = resolve_cert_path(Old0),
|
||||
case is_generated_file(Old) andalso filelib:is_regular(Old) andalso file:delete(Old) of
|
||||
ok ->
|
||||
ok;
|
||||
|
@ -355,7 +356,7 @@ delete_old_file(_New, Old) ->
|
|||
false ->
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
?SLOG(error, #{msg => "failed_to_delete_ssl_file", file_path => Old, reason => Reason})
|
||||
?SLOG(error, #{msg => "failed_to_delete_ssl_file", file_path => Old0, reason => Reason})
|
||||
end.
|
||||
|
||||
ensure_ssl_file(_Dir, _KeyPath, SSL, undefined, _Opts) ->
|
||||
|
@ -414,7 +415,8 @@ is_pem(MaybePem) ->
|
|||
%% To make it simple, the file is always overwritten.
|
||||
%% Also a potentially half-written PEM file (e.g. due to power outage)
|
||||
%% can be corrected with an overwrite.
|
||||
save_pem_file(Dir, KeyPath, Pem, DryRun) ->
|
||||
save_pem_file(Dir0, KeyPath, Pem, DryRun) ->
|
||||
Dir = resolve_cert_path(Dir0),
|
||||
Path = pem_file_name(Dir, KeyPath, Pem),
|
||||
case filelib:ensure_dir(Path) of
|
||||
ok when DryRun ->
|
||||
|
@ -472,7 +474,8 @@ 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) ->
|
||||
is_valid_pem_file(Path0) ->
|
||||
Path = resolve_cert_path(Path0),
|
||||
case file:read_file(Path) of
|
||||
{ok, Pem} -> is_pem(Pem) orelse {error, not_pem};
|
||||
{error, Reason} -> {error, Reason}
|
||||
|
@ -513,10 +516,15 @@ do_drop_invalid_certs([KeyPath | KeyPaths], SSL) ->
|
|||
to_server_opts(Type, Opts) ->
|
||||
Versions = integral_versions(Type, maps:get(versions, Opts, undefined)),
|
||||
Ciphers = integral_ciphers(Versions, maps:get(ciphers, Opts, undefined)),
|
||||
filter(
|
||||
maps:to_list(Opts#{
|
||||
keyfile => resolve_cert_path_strict(maps:get(keyfile, Opts, undefined)),
|
||||
certfile => resolve_cert_path_strict(maps:get(certfile, Opts, undefined)),
|
||||
cacertfile => resolve_cert_path_strict(maps:get(cacertfile, Opts, undefined)),
|
||||
ciphers => Ciphers,
|
||||
versions => Versions
|
||||
}).
|
||||
})
|
||||
).
|
||||
|
||||
%% @doc Convert hocon-checked tls client options (map()) to
|
||||
%% proplist accepted by ssl library.
|
||||
|
@ -532,9 +540,9 @@ to_client_opts(Type, Opts) ->
|
|||
Get = fun(Key) -> GetD(Key, undefined) end,
|
||||
case GetD(enable, false) of
|
||||
true ->
|
||||
KeyFile = ensure_str(Get(keyfile)),
|
||||
CertFile = ensure_str(Get(certfile)),
|
||||
CAFile = ensure_str(Get(cacertfile)),
|
||||
KeyFile = resolve_cert_path_strict(Get(keyfile)),
|
||||
CertFile = resolve_cert_path_strict(Get(certfile)),
|
||||
CAFile = resolve_cert_path_strict(Get(cacertfile)),
|
||||
Verify = GetD(verify, verify_none),
|
||||
SNI = ensure_sni(Get(server_name_indication)),
|
||||
Versions = integral_versions(Type, Get(versions)),
|
||||
|
@ -556,6 +564,59 @@ to_client_opts(Type, Opts) ->
|
|||
[]
|
||||
end.
|
||||
|
||||
resolve_cert_path_strict(Path) ->
|
||||
case resolve_cert_path(Path) of
|
||||
undefined ->
|
||||
undefined;
|
||||
ResolvedPath ->
|
||||
case filelib:is_regular(ResolvedPath) of
|
||||
true ->
|
||||
ResolvedPath;
|
||||
false ->
|
||||
PathToLog = ensure_str(Path),
|
||||
LogData =
|
||||
case PathToLog =:= ResolvedPath of
|
||||
true ->
|
||||
#{path => PathToLog};
|
||||
false ->
|
||||
#{path => PathToLog, resolved_path => ResolvedPath}
|
||||
end,
|
||||
?SLOG(error, LogData#{msg => "cert_file_not_found"}),
|
||||
undefined
|
||||
end
|
||||
end.
|
||||
|
||||
resolve_cert_path(undefined) ->
|
||||
undefined;
|
||||
resolve_cert_path(Path) ->
|
||||
case ensure_str(Path) of
|
||||
"$" ++ Maybe ->
|
||||
naive_env_resolver(Maybe);
|
||||
Other ->
|
||||
Other
|
||||
end.
|
||||
|
||||
%% resolves a file path like "ENV_VARIABLE/sub/path" or "{ENV_VARIABLE}/sub/path"
|
||||
%% in windows, it could be "ENV_VARIABLE/sub\path" or "{ENV_VARIABLE}/sub\path"
|
||||
naive_env_resolver(Maybe) ->
|
||||
case string:split(Maybe, "/") of
|
||||
[_] ->
|
||||
Maybe;
|
||||
[Env, SubPath] ->
|
||||
case os:getenv(trim_env_name(Env)) of
|
||||
false ->
|
||||
SubPath;
|
||||
"" ->
|
||||
SubPath;
|
||||
EnvValue ->
|
||||
filename:join(EnvValue, SubPath)
|
||||
end
|
||||
end.
|
||||
|
||||
%% delete the first and last curly braces
|
||||
trim_env_name(Env) ->
|
||||
string:trim(Env, both, "{}").
|
||||
|
||||
filter([]) -> [];
|
||||
filter([{_, undefined} | T]) -> filter(T);
|
||||
filter([{_, ""} | T]) -> filter(T);
|
||||
|
|
Loading…
Reference in New Issue