feat(dashboard): Update the configuration file to hocon
This commit is contained in:
parent
3b1303340a
commit
569d54a4c0
|
@ -91,6 +91,7 @@ includes() ->
|
||||||
, "emqx_bridge_mqtt"
|
, "emqx_bridge_mqtt"
|
||||||
, "emqx_modules"
|
, "emqx_modules"
|
||||||
, "emqx_management"
|
, "emqx_management"
|
||||||
|
, "emqx_dashboard"
|
||||||
, "emqx_gateway"
|
, "emqx_gateway"
|
||||||
].
|
].
|
||||||
-endif.
|
-endif.
|
||||||
|
|
|
@ -1,130 +1,41 @@
|
||||||
##--------------------------------------------------------------------
|
##--------------------------------------------------------------------
|
||||||
## EMQ X Dashboard
|
## Dashboard for EMQ X
|
||||||
##--------------------------------------------------------------------
|
##--------------------------------------------------------------------
|
||||||
|
|
||||||
## Default user's login name.
|
emqx_dashboard:{
|
||||||
##
|
default_username: "admin"
|
||||||
## Value: String
|
default_password: "public"
|
||||||
dashboard.default_user.login = admin
|
listeners: [
|
||||||
|
{
|
||||||
## Default user's password.
|
num_acceptors: 4
|
||||||
##
|
max_connections: 512
|
||||||
## Value: String
|
protocol: http
|
||||||
dashboard.default_user.password = public
|
port: 18083
|
||||||
|
backlog: 512
|
||||||
##--------------------------------------------------------------------
|
send_timeout: 15s
|
||||||
## HTTP Listener
|
send_timeout_close: true
|
||||||
|
inet6: false
|
||||||
## The port that the Dashboard HTTP listener will bind.
|
ipv6_v6only: false
|
||||||
##
|
}
|
||||||
## Value: Port
|
## ,
|
||||||
##
|
## {
|
||||||
## Examples: 18083
|
## protocol: https
|
||||||
dashboard.listener.http.port = 18083
|
## port: 8081
|
||||||
|
## acceptors: 2
|
||||||
## The acceptor pool for external Dashboard HTTP listener.
|
## backlog: 512
|
||||||
##
|
## send_timeout: 15s
|
||||||
## Value: Number
|
## send_timeout_close: true
|
||||||
dashboard.listener.http.acceptors = 4
|
## inet6: false
|
||||||
|
## ipv6_v6only: false
|
||||||
## Maximum number of concurrent Dashboard HTTP connections.
|
## certfile = "etc/certs/cert.pem"
|
||||||
##
|
## keyfile = "etc/certs/key.pem"
|
||||||
## Value: Number
|
## cacertfile = "etc/certs/cacert.pem"
|
||||||
dashboard.listener.http.max_clients = 512
|
## verify = verify_peer
|
||||||
|
## tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
||||||
## Set up the socket for IPv6.
|
## ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
|
||||||
##
|
## fail_if_no_peer_cert = true
|
||||||
## Value: false | true
|
## inet6 = false
|
||||||
dashboard.listener.http.inet6 = false
|
## ipv6_v6only = false
|
||||||
|
## }
|
||||||
## Listen on IPv4 and IPv6 (false) or only on IPv6 (true). Use with inet6.
|
]
|
||||||
##
|
}
|
||||||
## Value: false | true
|
|
||||||
dashboard.listener.http.ipv6_v6only = false
|
|
||||||
|
|
||||||
##--------------------------------------------------------------------
|
|
||||||
## HTTPS Listener
|
|
||||||
|
|
||||||
## The port that the Dashboard HTTPS listener will bind.
|
|
||||||
##
|
|
||||||
## Value: Port
|
|
||||||
##
|
|
||||||
## Examples: 18084
|
|
||||||
## dashboard.listener.https.port = 18084
|
|
||||||
|
|
||||||
## The acceptor pool for external Dashboard HTTPS listener.
|
|
||||||
##
|
|
||||||
## Value: Number
|
|
||||||
## dashboard.listener.https.acceptors = 2
|
|
||||||
|
|
||||||
## Maximum number of concurrent Dashboard HTTPS connections.
|
|
||||||
##
|
|
||||||
## Value: Number
|
|
||||||
## dashboard.listener.https.max_clients = 512
|
|
||||||
|
|
||||||
## Set up the socket for IPv6.
|
|
||||||
##
|
|
||||||
## Value: false | true
|
|
||||||
## dashboard.listener.https.inet6 = false
|
|
||||||
|
|
||||||
## Listen on IPv4 and IPv6 (false) or only on IPv6 (true). Use with inet6.
|
|
||||||
##
|
|
||||||
## Value: false | true
|
|
||||||
## dashboard.listener.https.ipv6_v6only = false
|
|
||||||
|
|
||||||
## Path to the file containing the user's private PEM-encoded key.
|
|
||||||
##
|
|
||||||
## Value: File
|
|
||||||
## dashboard.listener.https.keyfile = "etc/certs/key.pem"
|
|
||||||
|
|
||||||
## Path to a file containing the user certificate.
|
|
||||||
##
|
|
||||||
## Value: File
|
|
||||||
## dashboard.listener.https.certfile = "etc/certs/cert.pem"
|
|
||||||
|
|
||||||
## Path to the file containing PEM-encoded CA certificates.
|
|
||||||
##
|
|
||||||
## Value: File
|
|
||||||
## dashboard.listener.https.cacertfile = "etc/certs/cacert.pem"
|
|
||||||
|
|
||||||
## See: 'listener.ssl.<name>.dhfile' in emq.conf
|
|
||||||
##
|
|
||||||
## Value: File
|
|
||||||
## dashboard.listener.https.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
|
|
||||||
|
|
||||||
## See: 'listener.ssl.<name>.verify' in emq.conf
|
|
||||||
##
|
|
||||||
## Value: verify_peer | verify_none
|
|
||||||
## dashboard.listener.https.verify = verify_peer
|
|
||||||
|
|
||||||
## See: 'listener.ssl.<name>.fail_if_no_peer_cert' in emq.conf
|
|
||||||
##
|
|
||||||
## Value: false | true
|
|
||||||
## dashboard.listener.https.fail_if_no_peer_cert = true
|
|
||||||
|
|
||||||
## TLS versions only to protect from POODLE attack.
|
|
||||||
##
|
|
||||||
## Value: String, seperated by ','
|
|
||||||
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
|
|
||||||
## dashboard.listener.https.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
|
|
||||||
|
|
||||||
## See: 'listener.ssl.<name>.ciphers' in emq.conf
|
|
||||||
##
|
|
||||||
## Value: Ciphers
|
|
||||||
## dashboard.listener.https.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
|
|
||||||
|
|
||||||
## See: 'listener.ssl.<name>.secure_renegotiate' in emq.conf
|
|
||||||
##
|
|
||||||
## Value: on | off
|
|
||||||
## dashboard.listener.https.secure_renegotiate = off
|
|
||||||
|
|
||||||
## See: 'listener.ssl.<name>.reuse_sessions' in emq.conf
|
|
||||||
##
|
|
||||||
## Value: on | off
|
|
||||||
## dashboard.listener.https.reuse_sessions = on
|
|
||||||
|
|
||||||
## See: 'listener.ssl.<name>.honor_cipher_order' in emq.conf
|
|
||||||
##
|
|
||||||
## Value: on | off
|
|
||||||
## dashboard.listener.https.honor_cipher_order = on
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
%% limitations under the License.
|
%% limitations under the License.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-record(mqtt_admin, {username, password, tags}).
|
-record(mqtt_admin, {username, password, tags, role = undefined}).
|
||||||
|
|
||||||
-type(mqtt_admin() :: #mqtt_admin{}).
|
-type(mqtt_admin() :: #mqtt_admin{}).
|
||||||
|
|
||||||
|
|
|
@ -1,152 +0,0 @@
|
||||||
%%-*- mode: erlang -*-
|
|
||||||
%% emqx_dashboard config mapping
|
|
||||||
|
|
||||||
{mapping, "dashboard.default_user.login", "emqx_dashboard.default_user_username", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.default_user.password", "emqx_dashboard.default_user_passwd", [
|
|
||||||
{datatype, string},
|
|
||||||
{override_env, "EMQX_ADMIN_PASSWORD"}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.http.port", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.http.acceptors", "emqx_dashboard.listeners", [
|
|
||||||
{default, 4},
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.http.max_clients", "emqx_dashboard.listeners", [
|
|
||||||
{default, 512},
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.http.access.$id", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.http.inet6", "emqx_dashboard.listeners", [
|
|
||||||
{default, false},
|
|
||||||
{datatype, {enum, [true, false]}}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.http.ipv6_v6only", "emqx_dashboard.listeners", [
|
|
||||||
{default, false},
|
|
||||||
{datatype, {enum, [true, false]}}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.port", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.acceptors", "emqx_dashboard.listeners", [
|
|
||||||
{default, 8},
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.max_clients", "emqx_dashboard.listeners", [
|
|
||||||
{default, 64},
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.inet6", "emqx_dashboard.listeners", [
|
|
||||||
{default, false},
|
|
||||||
{datatype, {enum, [true, false]}}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.ipv6_v6only", "emqx_dashboard.listeners", [
|
|
||||||
{default, false},
|
|
||||||
{datatype, {enum, [true, false]}}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.tls_versions", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.dhfile", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.keyfile", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.certfile", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.cacertfile", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.verify", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.fail_if_no_peer_cert", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, {enum, [true, false]}}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.ciphers", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.secure_renegotiate", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, flag}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.reuse_sessions", "emqx_dashboard.listeners", [
|
|
||||||
{default, on},
|
|
||||||
{datatype, flag}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "dashboard.listener.https.honor_cipher_order", "emqx_dashboard.listeners", [
|
|
||||||
{datatype, flag}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{translation, "emqx_dashboard.listeners", fun(Conf) ->
|
|
||||||
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
|
|
||||||
LisOpts = fun(Prefix) ->
|
|
||||||
Filter([{num_acceptors, cuttlefish:conf_get(Prefix ++ ".acceptors", Conf)},
|
|
||||||
{max_connections, cuttlefish:conf_get(Prefix ++ ".max_clients", Conf)},
|
|
||||||
{inet6, cuttlefish:conf_get(Prefix ++ ".inet6", Conf)},
|
|
||||||
{ipv6_v6only, cuttlefish:conf_get(Prefix ++ ".ipv6_v6only", Conf)}])
|
|
||||||
end,
|
|
||||||
|
|
||||||
SplitFun = fun(undefined) -> undefined; (S) -> string:tokens(S, ",") end,
|
|
||||||
|
|
||||||
SslOpts = fun(Prefix) ->
|
|
||||||
Versions = case SplitFun(cuttlefish:conf_get(Prefix ++ ".tls_versions", Conf, undefined)) of
|
|
||||||
undefined -> undefined;
|
|
||||||
L -> [list_to_atom(V) || V <- L]
|
|
||||||
end,
|
|
||||||
Filter([{versions, Versions},
|
|
||||||
{ciphers, SplitFun(cuttlefish:conf_get(Prefix ++ ".ciphers", Conf, undefined))},
|
|
||||||
{dhfile, cuttlefish:conf_get(Prefix ++ ".dhfile", Conf, undefined)},
|
|
||||||
{keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)},
|
|
||||||
{certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)},
|
|
||||||
{cacertfile, cuttlefish:conf_get(Prefix ++ ".cacertfile", Conf, undefined)},
|
|
||||||
{verify, cuttlefish:conf_get(Prefix ++ ".verify", Conf, undefined)},
|
|
||||||
{fail_if_no_peer_cert, cuttlefish:conf_get(Prefix ++ ".fail_if_no_peer_cert", Conf, undefined)},
|
|
||||||
{secure_renegotiate, cuttlefish:conf_get(Prefix ++ ".secure_renegotiate", Conf, undefined)},
|
|
||||||
{reuse_sessions, cuttlefish:conf_get(Prefix ++ ".reuse_sessions", Conf, undefined)},
|
|
||||||
{honor_cipher_order, cuttlefish:conf_get(Prefix ++ ".honor_cipher_order", Conf, undefined)}])
|
|
||||||
end,
|
|
||||||
lists:append(
|
|
||||||
lists:map(
|
|
||||||
fun(Proto) ->
|
|
||||||
Prefix = "dashboard.listener." ++ atom_to_list(Proto),
|
|
||||||
case cuttlefish:conf_get(Prefix ++ ".port", Conf, undefined) of
|
|
||||||
undefined -> [];
|
|
||||||
Port ->
|
|
||||||
[{Proto, Port, case Proto of
|
|
||||||
http -> LisOpts(Prefix);
|
|
||||||
https -> LisOpts(Prefix) ++ SslOpts(Prefix)
|
|
||||||
end}]
|
|
||||||
end
|
|
||||||
end, [http, https]))
|
|
||||||
end}.
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{application, emqx_dashboard,
|
{application, emqx_dashboard,
|
||||||
[{description, "EMQ X Web Dashboard"},
|
[{description, "EMQ X Web Dashboard"},
|
||||||
{vsn, "4.4.0"}, % strict semver, bump manually!
|
{vsn, "5.0.0"}, % strict semver, bump manually!
|
||||||
{modules, []},
|
{modules, []},
|
||||||
{registered, [emqx_dashboard_sup]},
|
{registered, [emqx_dashboard_sup]},
|
||||||
{applications, [kernel,stdlib,mnesia,minirest]},
|
{applications, [kernel,stdlib,mnesia,minirest]},
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
%% -*- mode: erlang -*-
|
|
||||||
{VSN,
|
|
||||||
[ {"4.3.0",
|
|
||||||
%% load all plugins
|
|
||||||
%% NOTE: this depends on the fact that emqx_dashboard is always
|
|
||||||
%% the last application gets upgraded
|
|
||||||
[ {apply, {emqx_plugins, load, []}}
|
|
||||||
]},
|
|
||||||
{<<".*">>, []}
|
|
||||||
],
|
|
||||||
[ {"4.3.0",
|
|
||||||
[ {apply, {emqx_plugins, load, []}}
|
|
||||||
]},
|
|
||||||
{<<".*">>, []}
|
|
||||||
]
|
|
||||||
}.
|
|
|
@ -16,114 +16,112 @@
|
||||||
|
|
||||||
-module(emqx_dashboard).
|
-module(emqx_dashboard).
|
||||||
|
|
||||||
-include_lib("emqx/include/emqx.hrl").
|
-define(APP, ?MODULE).
|
||||||
-include_lib("emqx/include/logger.hrl").
|
|
||||||
|
|
||||||
%%-import(proplists, [get_value/3]).
|
|
||||||
|
|
||||||
-export([ start_listeners/0
|
-export([ start_listeners/0
|
||||||
, stop_listeners/0
|
, stop_listeners/0
|
||||||
, start_listener/1
|
, start_listener/1
|
||||||
, stop_listener/1
|
, stop_listener/1]).
|
||||||
]).
|
|
||||||
|
|
||||||
%% for minirest
|
%% Authorization
|
||||||
-export([ filter/1
|
-export([authorize_appid/1]).
|
||||||
, is_authorized/1
|
|
||||||
]).
|
|
||||||
|
|
||||||
-define(APP, ?MODULE).
|
|
||||||
|
-define(BASE_PATH, "/api/v5").
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Start/Stop listeners.
|
%% Start/Stop Listeners
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
start_listeners() ->
|
start_listeners() ->
|
||||||
lists:foreach(fun(Listener) -> start_listener(Listener) end, listeners()).
|
lists:foreach(fun start_listener/1, listeners()).
|
||||||
|
|
||||||
%% Start HTTP Listener
|
|
||||||
start_listener(_) -> ok.
|
|
||||||
%% TODO: V5 API
|
|
||||||
%%start_listener({Proto, Port, Options}) when Proto == http ->
|
|
||||||
%% Dispatch = [{"/", cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}},
|
|
||||||
%% {"/static/[...]", cowboy_static, {priv_dir, emqx_dashboard, "www/static"}},
|
|
||||||
%% {"/api/v4/[...]", minirest, http_handlers()}],
|
|
||||||
%% minirest:start_http(listener_name(Proto), ranch_opts(Port, Options), Dispatch);
|
|
||||||
%%
|
|
||||||
%%start_listener({Proto, Port, Options}) when Proto == https ->
|
|
||||||
%% Dispatch = [{"/", cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}},
|
|
||||||
%% {"/static/[...]", cowboy_static, {priv_dir, emqx_dashboard, "www/static"}},
|
|
||||||
%% {"/api/v4/[...]", minirest, http_handlers()}],
|
|
||||||
%% minirest:start_https(listener_name(Proto), ranch_opts(Port, Options), Dispatch).
|
|
||||||
%%
|
|
||||||
%%ranch_opts(Port, Options0) ->
|
|
||||||
%% NumAcceptors = get_value(num_acceptors, Options0, 4),
|
|
||||||
%% MaxConnections = get_value(max_connections, Options0, 512),
|
|
||||||
%% Options = lists:foldl(fun({K, _V}, Acc) when K =:= max_connections orelse K =:= num_acceptors ->
|
|
||||||
%% Acc;
|
|
||||||
%% ({inet6, true}, Acc) -> [inet6 | Acc];
|
|
||||||
%% ({inet6, false}, Acc) -> Acc;
|
|
||||||
%% ({ipv6_v6only, true}, Acc) -> [{ipv6_v6only, true} | Acc];
|
|
||||||
%% ({ipv6_v6only, false}, Acc) -> Acc;
|
|
||||||
%% ({K, V}, Acc)->
|
|
||||||
%% [{K, V} | Acc]
|
|
||||||
%% end, [], Options0),
|
|
||||||
%% #{num_acceptors => NumAcceptors,
|
|
||||||
%% max_connections => MaxConnections,
|
|
||||||
%% socket_opts => [{port, Port} | Options]}.
|
|
||||||
|
|
||||||
stop_listeners() ->
|
stop_listeners() ->
|
||||||
lists:foreach(fun(Listener) -> stop_listener(Listener) end, listeners()).
|
lists:foreach(fun stop_listener/1, listeners()).
|
||||||
|
|
||||||
stop_listener(_) ->
|
start_listener({Proto, Port, Options}) ->
|
||||||
ok.
|
{ok, _} = application:ensure_all_started(minirest),
|
||||||
%% TODO: V5 API
|
Authorization = {?MODULE, authorize_appid},
|
||||||
%%stop_listener({Proto, _Port, _}) ->
|
RanchOptions = ranch_opts(Port, Options),
|
||||||
%% minirest:stop_http(listener_name(Proto)).
|
GlobalSpec = #{
|
||||||
|
openapi => "3.0.0",
|
||||||
|
info => #{title => "EMQ X Dashboard API", version => "5.0.0"},
|
||||||
|
servers => [#{url => ?BASE_PATH}],
|
||||||
|
components => #{
|
||||||
|
schemas => #{},
|
||||||
|
securitySchemes => #{
|
||||||
|
application => #{
|
||||||
|
type => apiKey,
|
||||||
|
name => "authorization",
|
||||||
|
in => header}}}},
|
||||||
|
Dispatch = [{"/", cowboy_static, {priv_file, emqx_dashboard, "www/index.html"}},
|
||||||
|
{"/static/[...]", cowboy_static, {priv_dir, emqx_dashboard, "www/static"}}],
|
||||||
|
Minirest = #{
|
||||||
|
protocol => Proto,
|
||||||
|
base_path => ?BASE_PATH,
|
||||||
|
apps => apps(),
|
||||||
|
authorization => Authorization,
|
||||||
|
security => [#{application => []}],
|
||||||
|
swagger_global_spec => GlobalSpec,
|
||||||
|
dispatch => Dispatch},
|
||||||
|
MinirestOptions = maps:merge(Minirest, RanchOptions),
|
||||||
|
{ok, _} = minirest:start(listener_name(Proto), MinirestOptions),
|
||||||
|
io:format("Start ~p listener on ~p successfully.~n", [listener_name(Proto), Port]).
|
||||||
|
|
||||||
|
apps() ->
|
||||||
|
[App || {App, _, _} <- application:loaded_applications(),
|
||||||
|
case re:run(atom_to_list(App), "^emqx") of
|
||||||
|
{match,[{0,4}]} -> true;
|
||||||
|
_ -> false
|
||||||
|
end].
|
||||||
|
|
||||||
|
ranch_opts(Port, Options0) ->
|
||||||
|
Options = lists:foldl(
|
||||||
|
fun
|
||||||
|
({K, _V}, Acc) when K =:= max_connections orelse K =:= num_acceptors -> Acc;
|
||||||
|
({inet6, true}, Acc) -> [inet6 | Acc];
|
||||||
|
({inet6, false}, Acc) -> Acc;
|
||||||
|
({ipv6_v6only, true}, Acc) -> [{ipv6_v6only, true} | Acc];
|
||||||
|
({ipv6_v6only, false}, Acc) -> Acc;
|
||||||
|
({K, V}, Acc)->
|
||||||
|
[{K, V} | Acc]
|
||||||
|
end, [], Options0),
|
||||||
|
maps:from_list([{port, Port} | Options]).
|
||||||
|
|
||||||
|
stop_listener({Proto, Port, _}) ->
|
||||||
|
io:format("Stop dashboard listener on ~s successfully.~n",[format(Port)]),
|
||||||
|
minirest:stop(listener_name(Proto)).
|
||||||
|
|
||||||
listeners() ->
|
listeners() ->
|
||||||
application:get_env(?APP, listeners, []).
|
[{Protocol, Port, maps:to_list(maps:without([protocol, port], Map))}
|
||||||
|
|| Map = #{protocol := Protocol,port := Port}
|
||||||
|
<- emqx_config:get([emqx_dashboard, listeners], [])].
|
||||||
|
|
||||||
%%listener_name(Proto) ->
|
listener_name(Proto) ->
|
||||||
%% list_to_atom(atom_to_list(Proto) ++ ":dashboard").
|
list_to_atom(atom_to_list(Proto) ++ ":dashboard").
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
authorize_appid(Req) ->
|
||||||
%% HTTP Handlers and Dispatcher
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
%%http_handlers() ->
|
|
||||||
%% Plugins = lists:map(fun(Plugin) -> Plugin#plugin.name end, emqx_plugins:list()),
|
|
||||||
%% [{"/api/v4/",
|
|
||||||
%% minirest:handler(#{apps => Plugins ++ [emqx_modules],
|
|
||||||
%% filter => fun ?MODULE:filter/1}),
|
|
||||||
%% [{authorization, fun ?MODULE:is_authorized/1}]}].
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Basic Authorization
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
is_authorized(Req) ->
|
|
||||||
is_authorized(binary_to_list(cowboy_req:path(Req)), Req).
|
|
||||||
|
|
||||||
is_authorized("/api/v4/auth", _Req) ->
|
|
||||||
true;
|
|
||||||
is_authorized(_Path, Req) ->
|
|
||||||
case cowboy_req:parse_header(<<"authorization">>, Req) of
|
case cowboy_req:parse_header(<<"authorization">>, Req) of
|
||||||
{basic, Username, Password} ->
|
{basic, Username, Password} ->
|
||||||
case emqx_dashboard_admin:check(iolist_to_binary(Username),
|
case emqx_dashboard_admin:check(iolist_to_binary(Username),
|
||||||
iolist_to_binary(Password)) of
|
iolist_to_binary(Password)) of
|
||||||
ok -> true;
|
ok ->
|
||||||
{error, Reason} ->
|
ok;
|
||||||
?LOG(error, "[Dashboard] Authorization Failure: username=~s, reason=~p",
|
{error, _} ->
|
||||||
[Username, Reason]),
|
{401, #{<<"WWW-Authenticate">> =>
|
||||||
false
|
<<"Basic Realm=\"minirest-server\"">>},
|
||||||
|
<<"UNAUTHORIZED">>}
|
||||||
end;
|
end;
|
||||||
_ -> false
|
_ ->
|
||||||
|
{401, #{<<"WWW-Authenticate">> =>
|
||||||
|
<<"Basic Realm=\"minirest-server\"">>},
|
||||||
|
<<"UNAUTHORIZED">>}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
filter(#{app := emqx_modules}) -> true;
|
format(Port) when is_integer(Port) ->
|
||||||
filter(#{app := App}) ->
|
io_lib:format("0.0.0.0:~w", [Port]);
|
||||||
case emqx_plugins:find_plugin(App) of
|
format({Addr, Port}) when is_list(Addr) ->
|
||||||
false -> false;
|
io_lib:format("~s:~w", [Addr, Port]);
|
||||||
Plugin -> Plugin#plugin.active
|
format({Addr, Port}) when is_tuple(Addr) ->
|
||||||
end.
|
io_lib:format("~s:~w", [inet:ntoa(Addr), Port]).
|
||||||
|
|
|
@ -182,7 +182,7 @@ check(Username, Password) ->
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
%% Add default admin user
|
%% Add default admin user
|
||||||
_ = add_default_user(binenv(default_user_username), binenv(default_user_passwd)),
|
_ = add_default_user(binenv(default_username), binenv(default_password)),
|
||||||
{ok, state}.
|
{ok, state}.
|
||||||
|
|
||||||
handle_call(_Req, _From, State) ->
|
handle_call(_Req, _From, State) ->
|
||||||
|
@ -217,7 +217,7 @@ salt() ->
|
||||||
<<Salt:32>>.
|
<<Salt:32>>.
|
||||||
|
|
||||||
binenv(Key) ->
|
binenv(Key) ->
|
||||||
iolist_to_binary(application:get_env(emqx_dashboard, Key, "")).
|
iolist_to_binary(emqx_config:get([emqx_dashboard, Key], "")).
|
||||||
|
|
||||||
add_default_user(Username, Password) when ?EMPTY_KEY(Username) orelse ?EMPTY_KEY(Password) ->
|
add_default_user(Username, Password) when ?EMPTY_KEY(Username) orelse ?EMPTY_KEY(Password) ->
|
||||||
igonre;
|
igonre;
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-module(emqx_dashboard_schema).
|
||||||
|
|
||||||
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
|
-export([ structs/0
|
||||||
|
, fields/1]).
|
||||||
|
|
||||||
|
structs() -> ["emqx_dashboard"].
|
||||||
|
|
||||||
|
fields("emqx_dashboard") ->
|
||||||
|
[ {listeners, hoconsc:array(hoconsc:union([hoconsc:ref(?MODULE, "http"),
|
||||||
|
hoconsc:ref(?MODULE, "https")]))}
|
||||||
|
, {default_username, fun default_username/1}
|
||||||
|
, {default_password, fun default_password/1}
|
||||||
|
];
|
||||||
|
|
||||||
|
fields("http") ->
|
||||||
|
[ {"protocol", hoconsc:enum([http, https])}
|
||||||
|
, {"port", emqx_schema:t(integer(), undefined, 8081)}
|
||||||
|
, {"num_acceptors", emqx_schema:t(integer(), undefined, 4)}
|
||||||
|
, {"max_connections", emqx_schema:t(integer(), undefined, 512)}
|
||||||
|
, {"backlog", emqx_schema:t(integer(), undefined, 1024)}
|
||||||
|
, {"send_timeout", emqx_schema:t(emqx_schema:duration(), undefined, "15s")}
|
||||||
|
, {"send_timeout_close", emqx_schema:t(boolean(), undefined, true)}
|
||||||
|
, {"inet6", emqx_schema:t(boolean(), undefined, false)}
|
||||||
|
, {"ipv6_v6only", emqx_schema:t(boolean(), undefined, false)}
|
||||||
|
];
|
||||||
|
|
||||||
|
fields("https") ->
|
||||||
|
emqx_schema:ssl(#{enable => true}) ++ fields("http").
|
||||||
|
|
||||||
|
default_username(type) -> string();
|
||||||
|
default_username(default) -> "admin";
|
||||||
|
default_username(nullable) -> false;
|
||||||
|
default_username(_) -> undefined.
|
||||||
|
|
||||||
|
default_password(type) -> string();
|
||||||
|
default_password(default) -> "public";
|
||||||
|
default_password(nullable) -> false;
|
||||||
|
default_password(_) -> undefined.
|
|
@ -63,7 +63,7 @@ start_listener({Proto, Port, Options}) ->
|
||||||
swagger_global_spec => GlobalSpec},
|
swagger_global_spec => GlobalSpec},
|
||||||
MinirestOptions = maps:merge(Minirest, RanchOptions),
|
MinirestOptions = maps:merge(Minirest, RanchOptions),
|
||||||
{ok, _} = minirest:start(listener_name(Proto), MinirestOptions),
|
{ok, _} = minirest:start(listener_name(Proto), MinirestOptions),
|
||||||
io:format("Start ~p listener on ~p successfully.", [listener_name(Proto), Port]).
|
io:format("Start ~p listener on ~p successfully.~n", [listener_name(Proto), Port]).
|
||||||
|
|
||||||
apps() ->
|
apps() ->
|
||||||
Apps = [App || {App, _, _} <- application:loaded_applications(),
|
Apps = [App || {App, _, _} <- application:loaded_applications(),
|
||||||
|
|
|
@ -269,6 +269,7 @@ relx_apps(ReleaseType) ->
|
||||||
, emqx_bridge_mqtt
|
, emqx_bridge_mqtt
|
||||||
, emqx_modules
|
, emqx_modules
|
||||||
, emqx_management
|
, emqx_management
|
||||||
|
, emqx_dashboard
|
||||||
, emqx_retainer
|
, emqx_retainer
|
||||||
, emqx_statsd
|
, emqx_statsd
|
||||||
]
|
]
|
||||||
|
|
|
@ -15,22 +15,12 @@ main(_) ->
|
||||||
{ok, Bin} = file:read_file(BaseConf),
|
{ok, Bin} = file:read_file(BaseConf),
|
||||||
Apps = filelib:wildcard("emqx_*", "apps/"),
|
Apps = filelib:wildcard("emqx_*", "apps/"),
|
||||||
Conf = lists:foldl(fun(App, Acc) ->
|
Conf = lists:foldl(fun(App, Acc) ->
|
||||||
case lists:member(App, ["emqx_exhook",
|
Filename = filename:join([apps, App, "etc", App]) ++ ".conf",
|
||||||
"emqx_exproto",
|
case filelib:is_regular(Filename) of
|
||||||
"emqx_lwm2m",
|
true ->
|
||||||
"emqx_sn",
|
{ok, Bin1} = file:read_file(Filename),
|
||||||
"emqx_coap",
|
[Acc, io_lib:nl(), Bin1];
|
||||||
"emqx_stomp",
|
false -> Acc
|
||||||
"emqx_dashboard"]) of
|
|
||||||
true -> Acc;
|
|
||||||
false ->
|
|
||||||
Filename = filename:join([apps, App, "etc", App]) ++ ".conf",
|
|
||||||
case filelib:is_regular(Filename) of
|
|
||||||
true ->
|
|
||||||
{ok, Bin1} = file:read_file(Filename),
|
|
||||||
[Acc, io_lib:nl(), Bin1];
|
|
||||||
false -> Acc
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end, Bin, Apps),
|
end, Bin, Apps),
|
||||||
ok = file:write_file("apps/emqx/etc/emqx.conf.all", Conf).
|
ok = file:write_file("apps/emqx/etc/emqx.conf.all", Conf).
|
||||||
|
|
Loading…
Reference in New Issue