chore(gw): rename emqx_gateway to gateway
This commit is contained in:
parent
602f0ebb60
commit
e6a2770e2f
|
@ -1,252 +0,0 @@
|
||||||
##====================================================================
|
|
||||||
## EMQ X ExProto
|
|
||||||
##====================================================================
|
|
||||||
|
|
||||||
exproto.server.http.port = 9100
|
|
||||||
|
|
||||||
exproto.server.https.port = 9101
|
|
||||||
exproto.server.https.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
|
||||||
exproto.server.https.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
|
||||||
exproto.server.https.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
|
||||||
|
|
||||||
##--------------------------------------------------------------------
|
|
||||||
## Listeners
|
|
||||||
##--------------------------------------------------------------------
|
|
||||||
|
|
||||||
##--------------------------------------------------------------------
|
|
||||||
## MQTT/TCP - External TCP Listener for MQTT Protocol
|
|
||||||
|
|
||||||
## The IP address and port that the listener will bind.
|
|
||||||
##
|
|
||||||
## Value: <tcp|ssl|udp|dtls>://<ip>:<port>
|
|
||||||
##
|
|
||||||
## Examples: "tcp://0.0.0.0:7993" | "ssl://127.0.0.1:7994"
|
|
||||||
exproto.listener.protoname.endpoint = "tcp://0.0.0.0:7993"
|
|
||||||
|
|
||||||
## The ConnectionHandler server address
|
|
||||||
##
|
|
||||||
exproto.listener.protoname.connection_handler_url = "http://127.0.0.1:9001"
|
|
||||||
|
|
||||||
#exproto.listener.protoname.connection_handler_certfile =
|
|
||||||
#exproto.listener.protoname.connection_handler_cacertfile =
|
|
||||||
#exproto.listener.protoname.connection_handler_keyfile =
|
|
||||||
|
|
||||||
## The acceptor pool for external MQTT/TCP listener.
|
|
||||||
##
|
|
||||||
## Value: Number
|
|
||||||
exproto.listener.protoname.acceptors = 8
|
|
||||||
|
|
||||||
## Maximum number of concurrent MQTT/TCP connections.
|
|
||||||
##
|
|
||||||
## Value: Number
|
|
||||||
exproto.listener.protoname.max_connections = 1024000
|
|
||||||
|
|
||||||
## Maximum external connections per second.
|
|
||||||
##
|
|
||||||
## Value: Number
|
|
||||||
exproto.listener.protoname.max_conn_rate = 1000
|
|
||||||
|
|
||||||
## Specify the {active, N} option for the external MQTT/TCP Socket.
|
|
||||||
##
|
|
||||||
## Value: Number
|
|
||||||
exproto.listener.protoname.active_n = 100
|
|
||||||
|
|
||||||
## Idle timeout
|
|
||||||
##
|
|
||||||
## Value: Duration
|
|
||||||
exproto.listener.protoname.idle_timeout = 30s
|
|
||||||
|
|
||||||
## The access control rules for the MQTT/TCP listener.
|
|
||||||
##
|
|
||||||
## See: https://github.com/emqtt/esockd#allowdeny
|
|
||||||
##
|
|
||||||
## Value: Authorization Rule
|
|
||||||
##
|
|
||||||
## Example: "allow 192.168.0.0/24"
|
|
||||||
exproto.listener.protoname.access.1 = "allow all"
|
|
||||||
|
|
||||||
## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed
|
|
||||||
## behind HAProxy or Nginx.
|
|
||||||
##
|
|
||||||
## See: https://www.haproxy.com/blog/haproxy/proxy-protocol/
|
|
||||||
##
|
|
||||||
## Value: on | off
|
|
||||||
## exproto.listener.protoname.proxy_protocol = on
|
|
||||||
|
|
||||||
## Sets the timeout for proxy protocol. EMQ X will close the TCP connection
|
|
||||||
## if no proxy protocol packet recevied within the timeout.
|
|
||||||
##
|
|
||||||
## Value: Duration
|
|
||||||
#exproto.listener.protoname.proxy_protocol_timeout = 3s
|
|
||||||
|
|
||||||
## The TCP backlog defines the maximum length that the queue of pending
|
|
||||||
## connections can grow to.
|
|
||||||
##
|
|
||||||
## Value: Number >= 0
|
|
||||||
exproto.listener.protoname.backlog = 1024
|
|
||||||
|
|
||||||
## The TCP send timeout for external MQTT connections.
|
|
||||||
##
|
|
||||||
## Value: Duration
|
|
||||||
exproto.listener.protoname.send_timeout = 15s
|
|
||||||
|
|
||||||
## Close the TCP connection if send timeout.
|
|
||||||
##
|
|
||||||
## Value: on | off
|
|
||||||
exproto.listener.protoname.send_timeout_close = on
|
|
||||||
|
|
||||||
## The TCP receive buffer(os kernel) for MQTT connections.
|
|
||||||
##
|
|
||||||
## See: http://erlang.org/doc/man/inet.html
|
|
||||||
##
|
|
||||||
## Value: Bytes
|
|
||||||
#exproto.listener.protoname.recbuf = 2KB
|
|
||||||
|
|
||||||
## The TCP send buffer(os kernel) for MQTT connections.
|
|
||||||
##
|
|
||||||
## See: http://erlang.org/doc/man/inet.html
|
|
||||||
##
|
|
||||||
## Value: Bytes
|
|
||||||
#exproto.listener.protoname.sndbuf = 2KB
|
|
||||||
|
|
||||||
## The size of the user-level software buffer used by the driver.
|
|
||||||
## Not to be confused with options sndbuf and recbuf, which correspond
|
|
||||||
## to the Kernel socket buffers. It is recommended to have val(buffer)
|
|
||||||
## >= max(val(sndbuf),val(recbuf)) to avoid performance issues because
|
|
||||||
## of unnecessary copying. val(buffer) is automatically set to the above
|
|
||||||
## maximum when values sndbuf or recbuf are set.
|
|
||||||
##
|
|
||||||
## See: http://erlang.org/doc/man/inet.html
|
|
||||||
##
|
|
||||||
## Value: Bytes
|
|
||||||
#exproto.listener.protoname.buffer = 2KB
|
|
||||||
|
|
||||||
## Sets the 'buffer = max(sndbuf, recbuf)' if this option is enabled.
|
|
||||||
##
|
|
||||||
## Value: on | off
|
|
||||||
#exproto.listener.protoname.tune_buffer = off
|
|
||||||
|
|
||||||
## The TCP_NODELAY flag for MQTT connections. Small amounts of data are
|
|
||||||
## sent immediately if the option is enabled.
|
|
||||||
##
|
|
||||||
## Value: true | false
|
|
||||||
exproto.listener.protoname.nodelay = true
|
|
||||||
|
|
||||||
## The SO_REUSEADDR flag for TCP listener.
|
|
||||||
##
|
|
||||||
## Value: true | false
|
|
||||||
exproto.listener.protoname.reuseaddr = true
|
|
||||||
|
|
||||||
|
|
||||||
##--------------------------------------------------------------------
|
|
||||||
## TLS/DTLS options
|
|
||||||
|
|
||||||
## TLS versions only to protect from POODLE attack.
|
|
||||||
##
|
|
||||||
## See: http://erlang.org/doc/man/ssl.html
|
|
||||||
##
|
|
||||||
## Value: String, seperated by ','
|
|
||||||
#exproto.listener.protoname.tls_versions = "tlsv1.2,tlsv1.1,tlsv1"
|
|
||||||
|
|
||||||
## Path to the file containing the user's private PEM-encoded key.
|
|
||||||
##
|
|
||||||
## See: http://erlang.org/doc/man/ssl.html
|
|
||||||
##
|
|
||||||
## Value: File
|
|
||||||
#exproto.listener.protoname.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
|
|
||||||
|
|
||||||
## Path to a file containing the user certificate.
|
|
||||||
##
|
|
||||||
## See: http://erlang.org/doc/man/ssl.html
|
|
||||||
##
|
|
||||||
## Value: File
|
|
||||||
#exproto.listener.protoname.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
|
|
||||||
|
|
||||||
## Path to the file containing PEM-encoded CA certificates. The CA certificates
|
|
||||||
## are used during server authentication and when building the client certificate chain.
|
|
||||||
##
|
|
||||||
## Value: File
|
|
||||||
#exproto.listener.protoname.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
|
|
||||||
|
|
||||||
## The Ephemeral Diffie-Helman key exchange is a very effective way of
|
|
||||||
## ensuring Forward Secrecy by exchanging a set of keys that never hit
|
|
||||||
## the wire. Since the DH key is effectively signed by the private key,
|
|
||||||
## it needs to be at least as strong as the private key. In addition,
|
|
||||||
## the default DH groups that most of the OpenSSL installations have
|
|
||||||
## are only a handful (since they are distributed with the OpenSSL
|
|
||||||
## package that has been built for the operating system it’s running on)
|
|
||||||
## and hence predictable (not to mention, 1024 bits only).
|
|
||||||
## In order to escape this situation, first we need to generate a fresh,
|
|
||||||
## strong DH group, store it in a file and then use the option above,
|
|
||||||
## to force our SSL application to use the new DH group. Fortunately,
|
|
||||||
## OpenSSL provides us with a tool to do that. Simply run:
|
|
||||||
## openssl dhparam -out dh-params.pem 2048
|
|
||||||
##
|
|
||||||
## Value: File
|
|
||||||
#exproto.listener.protoname.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
|
|
||||||
|
|
||||||
## A server only does x509-path validation in mode verify_peer,
|
|
||||||
## as it then sends a certificate request to the client (this
|
|
||||||
## message is not sent if the verify option is verify_none).
|
|
||||||
## You can then also want to specify option fail_if_no_peer_cert.
|
|
||||||
## More information at: http://erlang.org/doc/man/ssl.html
|
|
||||||
##
|
|
||||||
## Value: verify_peer | verify_none
|
|
||||||
#exproto.listener.protoname.verify = verify_peer
|
|
||||||
|
|
||||||
## Used together with {verify, verify_peer} by an SSL server. If set to true,
|
|
||||||
## the server fails if the client does not have a certificate to send, that is,
|
|
||||||
## sends an empty certificate.
|
|
||||||
##
|
|
||||||
## Value: true | false
|
|
||||||
#exproto.listener.protoname.fail_if_no_peer_cert = true
|
|
||||||
|
|
||||||
## This is the single most important configuration option of an Erlang SSL
|
|
||||||
## application. Ciphers (and their ordering) define the way the client and
|
|
||||||
## server encrypt information over the wire, from the initial Diffie-Helman
|
|
||||||
## key exchange, the session key encryption ## algorithm and the message
|
|
||||||
## digest algorithm. Selecting a good cipher suite is critical for the
|
|
||||||
## application’s data security, confidentiality and performance.
|
|
||||||
##
|
|
||||||
## The cipher list above offers:
|
|
||||||
##
|
|
||||||
## A good balance between compatibility with older browsers.
|
|
||||||
## It can get stricter for Machine-To-Machine scenarios.
|
|
||||||
## Perfect Forward Secrecy.
|
|
||||||
## No old/insecure encryption and HMAC algorithms
|
|
||||||
##
|
|
||||||
## Most of it was copied from Mozilla’s Server Side TLS article
|
|
||||||
##
|
|
||||||
## Value: Ciphers
|
|
||||||
#exproto.listener.protoname.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"
|
|
||||||
|
|
||||||
## Ciphers for TLS PSK.
|
|
||||||
## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot
|
|
||||||
## be configured at the same time.
|
|
||||||
## See 'https://tools.ietf.org/html/rfc4279#section-2'.
|
|
||||||
#exproto.listener.protoname.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
|
|
||||||
|
|
||||||
## SSL parameter renegotiation is a feature that allows a client and a server
|
|
||||||
## to renegotiate the parameters of the SSL connection on the fly.
|
|
||||||
## RFC 5746 defines a more secure way of doing this. By enabling secure renegotiation,
|
|
||||||
## you drop support for the insecure renegotiation, prone to MitM attacks.
|
|
||||||
##
|
|
||||||
## Value: on | off
|
|
||||||
#exproto.listener.protoname.secure_renegotiate = off
|
|
||||||
|
|
||||||
## A performance optimization setting, it allows clients to reuse
|
|
||||||
## pre-existing sessions, instead of initializing new ones.
|
|
||||||
## Read more about it here.
|
|
||||||
##
|
|
||||||
## See: http://erlang.org/doc/man/ssl.html
|
|
||||||
##
|
|
||||||
## Value: on | off
|
|
||||||
#exproto.listener.protoname.reuse_sessions = on
|
|
||||||
|
|
||||||
## An important security setting, it forces the cipher to be set based
|
|
||||||
## on the server-specified order instead of the client-specified order,
|
|
||||||
## hence enforcing the (usually more properly configured) security
|
|
||||||
## ordering of the server administrator.
|
|
||||||
##
|
|
||||||
## Value: on | off
|
|
||||||
#exproto.listener.protoname.honor_cipher_order = on
|
|
|
@ -2,9 +2,7 @@
|
||||||
## EMQ X Gateway configurations
|
## EMQ X Gateway configurations
|
||||||
##--------------------------------------------------------------------
|
##--------------------------------------------------------------------
|
||||||
|
|
||||||
## TODO:
|
gateway: {
|
||||||
|
|
||||||
emqx_gateway: {
|
|
||||||
|
|
||||||
stomp.1: {
|
stomp.1: {
|
||||||
frame: {
|
frame: {
|
||||||
|
|
|
@ -1,364 +0,0 @@
|
||||||
%% -*-: erlang -*-
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Services
|
|
||||||
|
|
||||||
{mapping, "exproto.server.http.port", "emqx_exproto.servers", [
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.server.https.port", "emqx_exproto.servers", [
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.server.https.cacertfile", "emqx_exproto.servers", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.server.https.certfile", "emqx_exproto.servers", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.server.https.keyfile", "emqx_exproto.servers", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{translation, "emqx_exproto.servers", fun(Conf) ->
|
|
||||||
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
|
|
||||||
Http = case cuttlefish:conf_get("exproto.server.http.port", Conf, undefined) of
|
|
||||||
undefined -> [];
|
|
||||||
P1 -> [{http, P1, []}]
|
|
||||||
end,
|
|
||||||
Https = case cuttlefish:conf_get("exproto.server.https.port", Conf, undefined) of
|
|
||||||
undefined -> [];
|
|
||||||
P2 ->
|
|
||||||
[{https, P2,
|
|
||||||
Filter([{ssl, true},
|
|
||||||
{certfile, cuttlefish:conf_get("exproto.server.https.certfile", Conf)},
|
|
||||||
{keyfile, cuttlefish:conf_get("exproto.server.https.keyfile", Conf)},
|
|
||||||
{cacertfile, cuttlefish:conf_get("exproto.server.https.cacertfile", Conf)}])}]
|
|
||||||
end,
|
|
||||||
Http ++ Https
|
|
||||||
end}.
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Listeners
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.endpoint", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.connection_handler_url", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.connection_handler_certfile", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.connection_handler_cacertfile", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.connection_handler_keyfile", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.acceptors", "emqx_exproto.listeners", [
|
|
||||||
{default, 8},
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.max_connections", "emqx_exproto.listeners", [
|
|
||||||
{default, 1024},
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.max_conn_rate", "emqx_exproto.listeners", [
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.active_n", "emqx_exproto.listeners", [
|
|
||||||
{default, 100},
|
|
||||||
{datatype, integer}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.idle_timeout", "emqx_exproto.listeners", [
|
|
||||||
{default, "30s"},
|
|
||||||
{datatype, {duration, ms}}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.access.$id", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.proxy_protocol", "emqx_exproto.listeners", [
|
|
||||||
{datatype, flag}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.proxy_protocol_timeout", "emqx_exproto.listeners", [
|
|
||||||
{datatype, {duration, ms}}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.backlog", "emqx_exproto.listeners", [
|
|
||||||
{datatype, integer},
|
|
||||||
{default, 1024}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.send_timeout", "emqx_exproto.listeners", [
|
|
||||||
{datatype, {duration, ms}},
|
|
||||||
{default, "15s"}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.send_timeout_close", "emqx_exproto.listeners", [
|
|
||||||
{datatype, flag},
|
|
||||||
{default, on}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.recbuf", "emqx_exproto.listeners", [
|
|
||||||
{datatype, bytesize},
|
|
||||||
hidden
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.sndbuf", "emqx_exproto.listeners", [
|
|
||||||
{datatype, bytesize},
|
|
||||||
hidden
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.buffer", "emqx_exproto.listeners", [
|
|
||||||
{datatype, bytesize},
|
|
||||||
hidden
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.tune_buffer", "emqx_exproto.listeners", [
|
|
||||||
{datatype, flag},
|
|
||||||
hidden
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.nodelay", "emqx_exproto.listeners", [
|
|
||||||
{datatype, {enum, [true, false]}},
|
|
||||||
hidden
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.reuseaddr", "emqx_exproto.listeners", [
|
|
||||||
{datatype, {enum, [true, false]}},
|
|
||||||
hidden
|
|
||||||
]}.
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% TLS Options
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.tls_versions", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.ciphers", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.psk_ciphers", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.dhfile", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.keyfile", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.certfile", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.cacertfile", "emqx_exproto.listeners", [
|
|
||||||
{datatype, string}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.verify", "emqx_exproto.listeners", [
|
|
||||||
{datatype, atom}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.fail_if_no_peer_cert", "emqx_exproto.listeners", [
|
|
||||||
{datatype, {enum, [true, false]}}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.secure_renegotiate", "emqx_exproto.listeners", [
|
|
||||||
{datatype, flag}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.reuse_sessions", "emqx_exproto.listeners", [
|
|
||||||
{default, on},
|
|
||||||
{datatype, flag}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{mapping, "exproto.listener.$proto.honor_cipher_order", "emqx_exproto.listeners", [
|
|
||||||
{datatype, flag}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
{translation, "emqx_exproto.listeners", fun(Conf) ->
|
|
||||||
|
|
||||||
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
|
|
||||||
|
|
||||||
Atom = fun(undefined) -> undefined; (S) -> list_to_atom(S) end,
|
|
||||||
|
|
||||||
Access = fun(S) ->
|
|
||||||
[A, CIDR] = string:tokens(S, " "),
|
|
||||||
{list_to_atom(A), case CIDR of "all" -> all; _ -> CIDR end}
|
|
||||||
end,
|
|
||||||
|
|
||||||
AccOpts = fun(Prefix) ->
|
|
||||||
case cuttlefish_variable:filter_by_prefix(Prefix ++ ".access", Conf) of
|
|
||||||
[] -> [];
|
|
||||||
Rules -> [{access_rules, [Access(Rule) || {_, Rule} <- Rules]}]
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
RateLimit = fun(undefined) ->
|
|
||||||
undefined;
|
|
||||||
(Val) ->
|
|
||||||
[L, D] = string:tokens(Val, ", "),
|
|
||||||
Limit = case cuttlefish_bytesize:parse(L) of
|
|
||||||
Sz when is_integer(Sz) -> Sz;
|
|
||||||
{error, Reason} -> error(Reason)
|
|
||||||
end,
|
|
||||||
Duration = case cuttlefish_duration:parse(D, s) of
|
|
||||||
Secs when is_integer(Secs) -> Secs;
|
|
||||||
{error, Reason1} -> error(Reason1)
|
|
||||||
end,
|
|
||||||
Rate = Limit / Duration,
|
|
||||||
{Rate, Limit}
|
|
||||||
end,
|
|
||||||
|
|
||||||
HandlerOpts = fun(Prefix) ->
|
|
||||||
Opts =
|
|
||||||
case http_uri:parse(cuttlefish:conf_get(Prefix ++ ".connection_handler_url", Conf)) of
|
|
||||||
{ok, {http, _, Host, Port, _, _}} ->
|
|
||||||
[{scheme, http}, {host, Host}, {port, Port}];
|
|
||||||
{ok, {https, _, Host, Port, _, _}} ->
|
|
||||||
[{scheme, https}, {host, Host}, {port, Port},
|
|
||||||
{ssl_options,
|
|
||||||
Filter([{certfile, cuttlefish:conf_get(Prefix ++ ".connection_handler_certfile", Conf)},
|
|
||||||
{keyfile, cuttlefish:conf_get(Prefix ++ ".connection_handler_keyfile", Conf)},
|
|
||||||
{cacertfile, cuttlefish:conf_get(Prefix ++ ".connection_handler_cacertfile", Conf)}
|
|
||||||
])}];
|
|
||||||
_ ->
|
|
||||||
error(invaild_connection_handler_url)
|
|
||||||
end,
|
|
||||||
[{handler, Opts}]
|
|
||||||
end,
|
|
||||||
|
|
||||||
ConnOpts = fun(Prefix) ->
|
|
||||||
Filter([{active_n, cuttlefish:conf_get(Prefix ++ ".active_n", Conf, undefined)},
|
|
||||||
{idle_timeout, cuttlefish:conf_get(Prefix ++ ".idle_timeout", Conf, undefined)}])
|
|
||||||
end,
|
|
||||||
|
|
||||||
LisOpts = fun(Prefix) ->
|
|
||||||
Filter([{acceptors, cuttlefish:conf_get(Prefix ++ ".acceptors", Conf)},
|
|
||||||
{max_connections, cuttlefish:conf_get(Prefix ++ ".max_connections", Conf)},
|
|
||||||
{max_conn_rate, cuttlefish:conf_get(Prefix ++ ".max_conn_rate", Conf, undefined)},
|
|
||||||
{tune_buffer, cuttlefish:conf_get(Prefix ++ ".tune_buffer", Conf, undefined)},
|
|
||||||
{proxy_protocol, cuttlefish:conf_get(Prefix ++ ".proxy_protocol", Conf, undefined)},
|
|
||||||
{proxy_protocol_timeout, cuttlefish:conf_get(Prefix ++ ".proxy_protocol_timeout", Conf, undefined)} | AccOpts(Prefix)])
|
|
||||||
end,
|
|
||||||
|
|
||||||
TcpOpts = fun(Prefix) ->
|
|
||||||
Filter([{backlog, cuttlefish:conf_get(Prefix ++ ".backlog", Conf, undefined)},
|
|
||||||
{send_timeout, cuttlefish:conf_get(Prefix ++ ".send_timeout", Conf, undefined)},
|
|
||||||
{send_timeout_close, cuttlefish:conf_get(Prefix ++ ".send_timeout_close", Conf, undefined)},
|
|
||||||
{recbuf, cuttlefish:conf_get(Prefix ++ ".recbuf", Conf, undefined)},
|
|
||||||
{sndbuf, cuttlefish:conf_get(Prefix ++ ".sndbuf", Conf, undefined)},
|
|
||||||
{buffer, cuttlefish:conf_get(Prefix ++ ".buffer", Conf, undefined)},
|
|
||||||
{nodelay, cuttlefish:conf_get(Prefix ++ ".nodelay", Conf, true)},
|
|
||||||
{reuseaddr, cuttlefish:conf_get(Prefix ++ ".reuseaddr", Conf, undefined)}])
|
|
||||||
end,
|
|
||||||
SplitFun = fun(undefined) -> undefined; (S) -> string:tokens(S, ",") end,
|
|
||||||
MapPSKCiphers = fun(PSKCiphers) ->
|
|
||||||
lists:map(
|
|
||||||
fun("PSK-AES128-CBC-SHA") -> {psk, aes_128_cbc, sha};
|
|
||||||
("PSK-AES256-CBC-SHA") -> {psk, aes_256_cbc, sha};
|
|
||||||
("PSK-3DES-EDE-CBC-SHA") -> {psk, '3des_ede_cbc', sha};
|
|
||||||
("PSK-RC4-SHA") -> {psk, rc4_128, sha}
|
|
||||||
end, PSKCiphers)
|
|
||||||
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,
|
|
||||||
TLSCiphers = cuttlefish:conf_get(Prefix++".ciphers", Conf, undefined),
|
|
||||||
PSKCiphers = cuttlefish:conf_get(Prefix++".psk_ciphers", Conf, undefined),
|
|
||||||
Ciphers =
|
|
||||||
case {TLSCiphers, PSKCiphers} of
|
|
||||||
{undefined, undefined} ->
|
|
||||||
cuttlefish:invalid(Prefix++".ciphers or "++Prefix++".psk_ciphers is absent");
|
|
||||||
{TLSCiphers, undefined} ->
|
|
||||||
SplitFun(TLSCiphers);
|
|
||||||
{undefined, PSKCiphers} ->
|
|
||||||
MapPSKCiphers(SplitFun(PSKCiphers));
|
|
||||||
{_TLSCiphers, _PSKCiphers} ->
|
|
||||||
cuttlefish:invalid(Prefix++".ciphers and "++Prefix++".psk_ciphers cannot be configured at the same time")
|
|
||||||
end,
|
|
||||||
UserLookupFun =
|
|
||||||
case PSKCiphers of
|
|
||||||
undefined -> undefined;
|
|
||||||
_ -> {fun emqx_psk:lookup/3, <<>>}
|
|
||||||
end,
|
|
||||||
Filter([{versions, Versions},
|
|
||||||
{ciphers, Ciphers},
|
|
||||||
{user_lookup_fun, UserLookupFun},
|
|
||||||
%{handshake_timeout, cuttlefish:conf_get(Prefix ++ ".handshake_timeout", 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,
|
|
||||||
|
|
||||||
UdpOpts = fun(Prefix) ->
|
|
||||||
Filter([{recbuf, cuttlefish:conf_get(Prefix ++ ".recbuf", Conf, undefined)},
|
|
||||||
{sndbuf, cuttlefish:conf_get(Prefix ++ ".sndbuf", Conf, undefined)},
|
|
||||||
{buffer, cuttlefish:conf_get(Prefix ++ ".buffer", Conf, undefined)},
|
|
||||||
{reuseaddr, cuttlefish:conf_get(Prefix ++ ".reuseaddr", Conf, undefined)}])
|
|
||||||
end,
|
|
||||||
|
|
||||||
ParseListenOn = fun(ListenOn) ->
|
|
||||||
case string:tokens(ListenOn, "://") of
|
|
||||||
[Port] -> {tcp, list_to_integer(Port)};
|
|
||||||
[T, Ip, Port]
|
|
||||||
when T =:= "tcp"; T =:= "ssl";
|
|
||||||
T =:= "udp"; T =:= "dtls" ->
|
|
||||||
{Atom(T), {Ip, list_to_integer(Port)}}
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
Listeners = fun(Proto) ->
|
|
||||||
Prefix = string:join(["exproto","listener", Proto], "."),
|
|
||||||
Opts = HandlerOpts(Prefix) ++ ConnOpts(Prefix) ++ LisOpts(Prefix),
|
|
||||||
case cuttlefish:conf_get(Prefix ++ ".endpoint", Conf, undefined) of
|
|
||||||
undefined -> [];
|
|
||||||
ListenOn0 ->
|
|
||||||
case ParseListenOn(ListenOn0) of
|
|
||||||
{tcp, ListenOn} ->
|
|
||||||
[{Proto, tcp, ListenOn, [{tcp_options, TcpOpts(Prefix)} | Opts]}];
|
|
||||||
{ssl, ListenOn} ->
|
|
||||||
[{Proto, ssl, ListenOn, [{tcp_options, TcpOpts(Prefix)},
|
|
||||||
{ssl_options, SslOpts(Prefix)} | Opts]}];
|
|
||||||
{udp, ListenOn} ->
|
|
||||||
[{Proto, udp, ListenOn, [{udp_options, UdpOpts(Prefix)} | Opts]}];
|
|
||||||
{dtls, ListenOn} ->
|
|
||||||
[{Proto, dtls, ListenOn, [{udp_options, UdpOpts(Prefix)},
|
|
||||||
{dtls_options, SslOpts(Prefix)} | Opts]}];
|
|
||||||
{_, _} ->
|
|
||||||
cuttlefish:invalid("Not supported listener type")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
lists:flatten([Listeners(Proto) || {[_, "listener", Proto, "endpoint"], ListenOn}
|
|
||||||
<- cuttlefish_variable:filter_by_prefix("exproto.listener", Conf)])
|
|
||||||
end}.
|
|
|
@ -1,259 +0,0 @@
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// 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.
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package emqx.exproto.v1;
|
|
||||||
|
|
||||||
// The Broker side serivce. It provides a set of APIs to
|
|
||||||
// handle a protcol access
|
|
||||||
service ConnectionAdapter {
|
|
||||||
|
|
||||||
// -- socket layer
|
|
||||||
|
|
||||||
rpc Send(SendBytesRequest) returns (CodeResponse) {};
|
|
||||||
|
|
||||||
rpc Close(CloseSocketRequest) returns (CodeResponse) {};
|
|
||||||
|
|
||||||
// -- protocol layer
|
|
||||||
|
|
||||||
rpc Authenticate(AuthenticateRequest) returns (CodeResponse) {};
|
|
||||||
|
|
||||||
rpc StartTimer(TimerRequest) returns (CodeResponse) {};
|
|
||||||
|
|
||||||
// -- pub/sub layer
|
|
||||||
|
|
||||||
rpc Publish(PublishRequest) returns (CodeResponse) {};
|
|
||||||
|
|
||||||
rpc Subscribe(SubscribeRequest) returns (CodeResponse) {};
|
|
||||||
|
|
||||||
rpc Unsubscribe(UnsubscribeRequest) returns (CodeResponse) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
service ConnectionHandler {
|
|
||||||
|
|
||||||
// -- socket layer
|
|
||||||
|
|
||||||
rpc OnSocketCreated(stream SocketCreatedRequest) returns (EmptySuccess) {};
|
|
||||||
|
|
||||||
rpc OnSocketClosed(stream SocketClosedRequest) returns (EmptySuccess) {};
|
|
||||||
|
|
||||||
rpc OnReceivedBytes(stream ReceivedBytesRequest) returns (EmptySuccess) {};
|
|
||||||
|
|
||||||
// -- pub/sub layer
|
|
||||||
|
|
||||||
rpc OnTimerTimeout(stream TimerTimeoutRequest) returns (EmptySuccess) {};
|
|
||||||
|
|
||||||
rpc OnReceivedMessages(stream ReceivedMessagesRequest) returns (EmptySuccess) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
message EmptySuccess { }
|
|
||||||
|
|
||||||
enum ResultCode {
|
|
||||||
|
|
||||||
// Operation successfully
|
|
||||||
SUCCESS = 0;
|
|
||||||
|
|
||||||
// Unknown Error
|
|
||||||
UNKNOWN = 1;
|
|
||||||
|
|
||||||
// Connection process is not alive
|
|
||||||
CONN_PROCESS_NOT_ALIVE = 2;
|
|
||||||
|
|
||||||
// Miss the required parameter
|
|
||||||
REQUIRED_PARAMS_MISSED = 3;
|
|
||||||
|
|
||||||
// Params type or values incorrect
|
|
||||||
PARAMS_TYPE_ERROR = 4;
|
|
||||||
|
|
||||||
// No permission or Pre-conditions not fulfilled
|
|
||||||
PERMISSION_DENY = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CodeResponse {
|
|
||||||
|
|
||||||
ResultCode code = 1;
|
|
||||||
|
|
||||||
// The reason message if result is false
|
|
||||||
string message = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SendBytesRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
bytes bytes = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CloseSocketRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AuthenticateRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
ClientInfo clientinfo = 2;
|
|
||||||
|
|
||||||
string password = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TimerRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
TimerType type = 2;
|
|
||||||
|
|
||||||
uint32 interval = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum TimerType {
|
|
||||||
|
|
||||||
KEEPALIVE = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PublishRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
string topic = 2;
|
|
||||||
|
|
||||||
uint32 qos = 3;
|
|
||||||
|
|
||||||
bytes payload = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SubscribeRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
string topic = 2;
|
|
||||||
|
|
||||||
uint32 qos = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UnsubscribeRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
string topic = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SocketCreatedRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
ConnInfo conninfo = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ReceivedBytesRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
bytes bytes = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TimerTimeoutRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
TimerType type = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SocketClosedRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
string reason = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ReceivedMessagesRequest {
|
|
||||||
|
|
||||||
string conn = 1;
|
|
||||||
|
|
||||||
repeated Message messages = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
|
||||||
// Basic data types
|
|
||||||
//--------------------------------------------------------------------
|
|
||||||
|
|
||||||
message ConnInfo {
|
|
||||||
|
|
||||||
SocketType socktype = 1;
|
|
||||||
|
|
||||||
Address peername = 2;
|
|
||||||
|
|
||||||
Address sockname = 3;
|
|
||||||
|
|
||||||
CertificateInfo peercert = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SocketType {
|
|
||||||
|
|
||||||
TCP = 0;
|
|
||||||
|
|
||||||
SSL = 1;
|
|
||||||
|
|
||||||
UDP = 2;
|
|
||||||
|
|
||||||
DTLS = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Address {
|
|
||||||
|
|
||||||
string host = 1;
|
|
||||||
|
|
||||||
uint32 port = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CertificateInfo {
|
|
||||||
|
|
||||||
string cn = 1;
|
|
||||||
|
|
||||||
string dn = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ClientInfo {
|
|
||||||
|
|
||||||
string proto_name = 1;
|
|
||||||
|
|
||||||
string proto_ver = 2;
|
|
||||||
|
|
||||||
string clientid = 3;
|
|
||||||
|
|
||||||
string username = 4;
|
|
||||||
|
|
||||||
string mountpoint = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Message {
|
|
||||||
|
|
||||||
string node = 1;
|
|
||||||
|
|
||||||
string id = 2;
|
|
||||||
|
|
||||||
uint32 qos = 3;
|
|
||||||
|
|
||||||
string from = 4;
|
|
||||||
|
|
||||||
string topic = 5;
|
|
||||||
|
|
||||||
bytes payload = 6;
|
|
||||||
|
|
||||||
uint64 timestamp = 7;
|
|
||||||
}
|
|
|
@ -82,7 +82,7 @@ create_gateway_by_default([{Type, Name, Confs}|More]) ->
|
||||||
create_gateway_by_default(More).
|
create_gateway_by_default(More).
|
||||||
|
|
||||||
zipped_confs() ->
|
zipped_confs() ->
|
||||||
All = maps:to_list(emqx_config:get([emqx_gateway])),
|
All = maps:to_list(emqx_config:get([gateway])),
|
||||||
lists:append(lists:foldr(
|
lists:append(lists:foldr(
|
||||||
fun({Type, Gws}, Acc) ->
|
fun({Type, Gws}, Acc) ->
|
||||||
{Names, Confs} = lists:unzip(maps:to_list(Gws)),
|
{Names, Confs} = lists:unzip(maps:to_list(Gws)),
|
||||||
|
|
|
@ -29,9 +29,9 @@
|
||||||
-export([structs/0 , fields/1]).
|
-export([structs/0 , fields/1]).
|
||||||
-export([t/1, t/3, t/4, ref/1]).
|
-export([t/1, t/3, t/4, ref/1]).
|
||||||
|
|
||||||
structs() -> ["emqx_gateway"].
|
structs() -> ["gateway"].
|
||||||
|
|
||||||
fields("emqx_gateway") ->
|
fields("gateway") ->
|
||||||
[{stomp, t(ref(stomp))},
|
[{stomp, t(ref(stomp))},
|
||||||
{mqttsn, t(ref(mqttsn))},
|
{mqttsn, t(ref(mqttsn))},
|
||||||
{exproto, t(ref(exproto))}
|
{exproto, t(ref(exproto))}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
, {"emqx_bridge_mqtt", emqx_bridge_mqtt_schema}
|
, {"emqx_bridge_mqtt", emqx_bridge_mqtt_schema}
|
||||||
, {"emqx_management", emqx_management_schema}
|
, {"emqx_management", emqx_management_schema}
|
||||||
, {"emqx_dashboard", emqx_dashboard_schema}
|
, {"emqx_dashboard", emqx_dashboard_schema}
|
||||||
, {"emqx_gateway", emqx_gateway_schema}
|
, {"gateway", emqx_gateway_schema}
|
||||||
, {"prometheus", emqx_prometheus_schema}
|
, {"prometheus", emqx_prometheus_schema}
|
||||||
, {"statsd", emqx_statsd_schema}
|
, {"statsd", emqx_statsd_schema}
|
||||||
, {"delayed", emqx_modules_schema}
|
, {"delayed", emqx_modules_schema}
|
||||||
|
|
Loading…
Reference in New Issue