diff --git a/apps/emqx/etc/emqx.conf.rendered b/apps/emqx/etc/emqx.conf.rendered new file mode 100644 index 000000000..afa640621 --- /dev/null +++ b/apps/emqx/etc/emqx.conf.rendered @@ -0,0 +1,1671 @@ +##================================================================== +## Listeners +##================================================================== +## MQTT/TCP - TCP Listeners for MQTT Protocol +## syntax: listeners.tcp. +## example: listeners.tcp.my_tcp_listener +listeners.tcp.default { + ## The IP address and port that the listener will bind. + ## + ## @doc listeners.tcp..bind + ## ValueType: IPAddress | Port | IPAddrPort + ## Required: true + ## Examples: 1883, 127.0.0.1:1883, ::1:1883 + bind = "0.0.0.0:1883" + + ## The configuration zone this listener is using. + ## If not set, the global configs are used for this listener. + ## + ## See `zones.` for more details. + ## + ## @doc listeners.tcp..zone + ## ValueType: String + ## Required: false + #zone = default + + ## The size of the acceptor pool for this listener. + ## + ## @doc listeners.tcp..acceptors + ## ValueType: Number + ## Default: 16 + acceptors = 16 + + ## Maximum number of concurrent connections. + ## + ## @doc listeners.tcp..max_connections + ## ValueType: Number | infinity + ## Default: infinity + max_connections = 1024000 + + ## The access control rules for this listener. + ## + ## See: https://github.com/emqtt/esockd#allowdeny + ## + ## @doc listeners.tcp..access_rules + ## ValueType: Array + ## Default: [] + ## Examples: + ## access_rules: [ + ## "deny 192.168.0.0/24", + ## "all all" + ## ] + access_rules = [ + "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/ + ## + ## @doc listeners.tcp..proxy_protocol + ## ValueType: Boolean + ## Default: false + proxy_protocol = false + + ## Sets the timeout for proxy protocol. EMQ X will close the TCP connection + ## if no proxy protocol packet received within the timeout. + ## + ## @doc listeners.tcp..proxy_protocol_timeout + ## ValueType: Duration + ## Default: 3s + proxy_protocol_timeout = 3s + + ## When publishing or subscribing, prefix all topics with a mountpoint string. + ## The prefixed string will be removed from the topic name when the message + ## is delivered to the subscriber. The mountpoint is a way that users can use + ## to implement isolation of message routing between different listeners. + ## + ## For example if a clientA subscribes to "t" with `listeners.tcp..mountpoint` + ## set to "some_tenant", then the client accually subscribes to the topic + ## "some_tenant/t". Similarly if another clientB (connected to the same listener + ## with the clientA) send a message to topic "t", the message is accually route + ## to all the clients subscribed "some_tenant/t", so clientA will receive the + ## message, with topic name "t". + ## + ## Set to "" to disable the feature. + ## + ## Variables in mountpoint string: + ## - ${clientid}: clientid + ## - ${username}: username + ## + ## @doc listeners.tcp..mountpoint + ## ValueType: String + ## Default: "" + mountpoint = "" + + ## TCP options + ## See ${example_common_tcp_options} for more information + tcp.backlog = 1024 + tcp.buffer = 4KB +} + +## MQTT/SSL - SSL Listeners for MQTT Protocol +## syntax: listeners.ssl. +## example: listeners.ssl.my_ssl_listener +listeners.ssl.default { + ## The IP address and port that the listener will bind. + ## + ## @doc listeners.ssl..bind + ## ValueType: IPAddress | Port | IPAddrPort + ## Required: true + ## Examples: 8883, 127.0.0.1:8883, ::1:8883 + bind = "0.0.0.0:8883" + + ## The configuration zone this listener is using. + ## If not set, the global configs are used for this listener. + ## + ## See `zones.` for more details. + ## + ## @doc listeners.ssl..zone + ## ValueType: String + ## Required: false + #zone = default + + ## The size of the acceptor pool for this listener. + ## + ## @doc listeners.ssl..acceptors + ## ValueType: Number + ## Default: 16 + acceptors = 16 + + ## Maximum number of concurrent connections. + ## + ## @doc listeners.ssl..max_connections + ## ValueType: Number | infinity + ## Default: infinity + max_connections = 512000 + + ## The access control rules for this listener. + ## + ## See: https://github.com/emqtt/esockd#allowdeny + ## + ## @doc listeners.ssl..access_rules + ## ValueType: Array + ## Default: [] + ## Examples: + ## access_rules: [ + ## "deny 192.168.0.0/24", + ## "all all" + ## ] + access_rules = [ + "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/ + ## + ## @doc listeners.ssl..proxy_protocol + ## ValueType: Boolean + ## Default: true + proxy_protocol = false + + ## Sets the timeout for proxy protocol. EMQ X will close the TCP connection + ## if no proxy protocol packet received within the timeout. + ## + ## @doc listeners.ssl..proxy_protocol_timeout + ## ValueType: Duration + ## Default: 3s + proxy_protocol_timeout = 3s + + ## When publishing or subscribing, prefix all topics with a mountpoint string. + ## The prefixed string will be removed from the topic name when the message + ## is delivered to the subscriber. The mountpoint is a way that users can use + ## to implement isolation of message routing between different listeners. + ## + ## For example if a clientA subscribes to "t" with `listeners.ssl..mountpoint` + ## set to "some_tenant", then the client accually subscribes to the topic + ## "some_tenant/t". Similarly if another clientB (connected to the same listener + ## with the clientA) send a message to topic "t", the message is accually route + ## to all the clients subscribed "some_tenant/t", so clientA will receive the + ## message, with topic name "t". + ## + ## Set to "" to disable the feature. + ## + ## Variables in mountpoint string: + ## - ${clientid}: clientid + ## - ${username}: username + ## + ## @doc listeners.ssl..mountpoint + ## ValueType: String + ## Default: "" + mountpoint = "" + + ## SSL options + ssl.keyfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/key.pem" + ssl.certfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/cert.pem" + ssl.cacertfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/cacert.pem" + + # ssl.versions = ["tlsv1.3", "tlsv1.2", "tlsv1.1", "tlsv1"] + # TLS 1.3: "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" + # TLS 1-1.2 "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" + # PSK: "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA" + # NOTE: If PSK cipher-suites are intended, tlsv1.3 should not be enabled in 'versions' config + # ssl.ciphers = "" + + ## TCP options + ## See ${example_common_tcp_options} for more information + tcp.backlog = 1024 + tcp.buffer = 4KB +} + +## MQTT/QUIC - QUIC Listeners for MQTT Protocol +## syntax: listeners.quic. +## example: listeners.quic.my_quic_listener +listeners.quic.default { + ## The IP address and port that the listener will bind. + ## + ## @doc listeners.quic..bind + ## ValueType: IPAddress | Port | IPAddrPort + ## Required: true + ## Examples: 14567, 127.0.0.1:14567, ::1:14567 + bind = "0.0.0.0:14567" + + ## The configuration zone this listener is using. + ## If not set, the global configs are used for this listener. + ## + ## See `zones.` for more details. + ## NOTE: This is a cluster-wide configuration. + ## It requires all nodes to be stopped before changing it. + ## + ## @doc listeners.quic..zone + ## ValueType: String + ## Required: false + #zone = default + + ## The size of the acceptor pool for this listener. + ## + ## @doc listeners.quic..acceptors + ## ValueType: Number + ## Default: 16 + acceptors = 16 + + ## Maximum number of concurrent connections. + ## + ## @doc listeners.quic..max_connections + ## ValueType: Number | infinity + ## Default: infinity + max_connections = 1024000 + + ## Path to the file containing the user's private PEM-encoded key. + ## + ## @doc listeners.quic..keyfile + ## ValueType: String + ## Default: "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/key.pem" + keyfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/key.pem" + + ## Path to a file containing the user certificate. + ## + ## @doc listeners.quic..certfile + ## ValueType: String + ## Default: "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/cert.pem" + certfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/cert.pem" + + ## When publishing or subscribing, prefix all topics with a mountpoint string. + ## The prefixed string will be removed from the topic name when the message + ## is delivered to the subscriber. The mountpoint is a way that users can use + ## to implement isolation of message routing between different listeners. + ## + ## For example if a clientA subscribes to "t" with `listeners.quic..mountpoint` + ## set to "some_tenant", then the client accually subscribes to the topic + ## "some_tenant/t". Similarly if another clientB (connected to the same listener + ## with the clientA) send a message to topic "t", the message is accually route + ## to all the clients subscribed "some_tenant/t", so clientA will receive the + ## message, with topic name "t". + ## + ## Set to "" to disable the feature. + ## + ## Variables in mountpoint string: + ## - ${clientid}: clientid + ## - ${username}: username + ## + ## @doc listeners.quic..mountpoint + ## ValueType: String + ## Default: "" + mountpoint = "" +} + +## MQTT/WS - Websocket Listeners for MQTT Protocol +## syntax: listeners.ws. +## example: listeners.ws.my_ws_listener +listeners.ws.default { + ## The IP address and port that the listener will bind. + ## + ## @doc listeners.ws..bind + ## ValueType: IPAddress | Port | IPAddrPort + ## Required: true + ## Examples: 8083, 127.0.0.1:8083, ::1:8083 + bind = "0.0.0.0:8083" + + ## The configuration zone this listener is using. + ## If not set, the global configs are used for this listener. + ## + ## See `zones.` for more details. + ## + ## @doc listeners.ws..zone + ## ValueType: String + ## Required: false + #zone = default + + ## The size of the acceptor pool for this listener. + ## + ## @doc listeners.ws..acceptors + ## ValueType: Number + ## Default: 16 + acceptors = 16 + + ## Maximum number of concurrent connections. + ## + ## @doc listeners.ws..max_connections + ## ValueType: Number | infinity + ## Default: infinity + max_connections = 1024000 + + ## The access control rules for this listener. + ## + ## See: https://github.com/emqtt/esockd#allowdeny + ## + ## @doc listeners.ws..access_rules + ## ValueType: Array + ## Default: [] + ## Examples: + ## access_rules: [ + ## "deny 192.168.0.0/24", + ## "all all" + ## ] + access_rules = [ + "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/ + ## + ## @doc listeners.ws..proxy_protocol + ## ValueType: Boolean + ## Default: true + proxy_protocol = false + + ## Sets the timeout for proxy protocol. EMQ X will close the TCP connection + ## if no proxy protocol packet received within the timeout. + ## + ## @doc listeners.ws..proxy_protocol_timeout + ## ValueType: Duration + ## Default: 3s + proxy_protocol_timeout = 3s + + ## When publishing or subscribing, prefix all topics with a mountpoint string. + ## The prefixed string will be removed from the topic name when the message + ## is delivered to the subscriber. The mountpoint is a way that users can use + ## to implement isolation of message routing between different listeners. + ## + ## For example if a clientA subscribes to "t" with `listeners.ws..mountpoint` + ## set to "some_tenant", then the client accually subscribes to the topic + ## "some_tenant/t". Similarly if another clientB (connected to the same listener + ## with the clientA) send a message to topic "t", the message is accually route + ## to all the clients subscribed "some_tenant/t", so clientA will receive the + ## message, with topic name "t". + ## + ## Set to "" to disable the feature. + ## + ## Variables in mountpoint string: + ## - ${clientid}: clientid + ## - ${username}: username + ## + ## @doc listeners.ws..mountpoint + ## ValueType: String + ## Default: "" + mountpoint = "" + + ## TCP options + ## See ${example_common_tcp_options} for more information + tcp.backlog = 1024 + tcp.buffer = 4KB + + ## Websocket options + ## See ${example_common_websocket_options} for more information + websocket.idle_timeout = 86400s +} + +## MQTT/WSS - WebSocket Secure Listeners for MQTT Protocol +## syntax: listeners.wss. +## example: listeners.wss.my_wss_listener +listeners.wss.default { + ## The IP address and port that the listener will bind. + ## + ## @doc listeners.wss..bind + ## ValueType: IPAddress | Port | IPAddrPort + ## Required: true + ## Examples: 8084, 127.0.0.1:8084, ::1:8084 + bind = "0.0.0.0:8084" + + ## The configuration zone this listener is using. + ## If not set, the global configs are used for this listener. + ## + ## See `zones.` for more details. + ## + ## @doc listeners.wss..zone + ## ValueType: String + ## Required: false + #zone = default + + ## The size of the acceptor pool for this listener. + ## + ## @doc listeners.wss..acceptors + ## ValueType: Number + ## Default: 16 + acceptors = 16 + + ## Maximum number of concurrent connections. + ## + ## @doc listeners.wss..max_connections + ## ValueType: Number | infinity + ## Default: infinity + max_connections = 512000 + + ## The access control rules for this listener. + ## + ## See: https://github.com/emqtt/esockd#allowdeny + ## + ## @doc listeners.wss..access_rules + ## ValueType: Array + ## Default: [] + ## Examples: + ## access_rules: [ + ## "deny 192.168.0.0/24", + ## "all all" + ## ] + access_rules = [ + "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/ + ## + ## @doc listeners.wss..proxy_protocol + ## ValueType: Boolean + ## Default: true + proxy_protocol = false + + ## Sets the timeout for proxy protocol. EMQ X will close the TCP connection + ## if no proxy protocol packet received within the timeout. + ## + ## @doc listeners.wss..proxy_protocol_timeout + ## ValueType: Duration + ## Default: 3s + proxy_protocol_timeout = 3s + + ## When publishing or subscribing, prefix all topics with a mountpoint string. + ## The prefixed string will be removed from the topic name when the message + ## is delivered to the subscriber. The mountpoint is a way that users can use + ## to implement isolation of message routing between different listeners. + ## + ## For example if a clientA subscribes to "t" with `listeners.wss..mountpoint` + ## set to "some_tenant", then the client accually subscribes to the topic + ## "some_tenant/t". Similarly if another clientB (connected to the same listener + ## with the clientA) send a message to topic "t", the message is accually route + ## to all the clients subscribed "some_tenant/t", so clientA will receive the + ## message, with topic name "t". + ## + ## Set to "" to disable the feature. + ## + ## Variables in mountpoint string: + ## - ${clientid}: clientid + ## - ${username}: username + ## + ## @doc listeners.wss..mountpoint + ## ValueType: String + ## Default: "" + mountpoint = "" + + ## SSL options + ## See ${example_common_ssl_options} for more information + ssl.keyfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/key.pem" + ssl.certfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/cert.pem" + ssl.cacertfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/cacert.pem" + + ## TCP options + ## See ${example_common_tcp_options} for more information + tcp.backlog = 1024 + tcp.buffer = 4KB + + ## Websocket options + ## See ${example_common_websocket_options} for more information + websocket.idle_timeout = 86400s + +} + +## Enable per connection statistics. +## +## @doc stats.enable +## ValueType: Boolean +## Default: true +stats.enable = true + +authorization { + ## Behaviour after not matching a rule. + ## + ## @doc authorization.no_match + ## ValueType: allow | deny + ## Default: allow + no_match: allow + + ## The action when authorization check reject current operation + ## + ## @doc authorization.deny_action + ## ValueType: ignore | disconnect + ## Default: ignore + deny_action: ignore + + ## Whether to enable Authorization cache. + ## + ## If enabled, Authorization roles for each client will be cached in the memory + ## + ## @doc authorization.cache.enable + ## ValueType: Boolean + ## Default: true + cache.enable: true + + ## The maximum count of Authorization entries can be cached for a client. + ## + ## @doc authorization.cache.max_size + ## ValueType: Integer + ## Range: [0, 1048576] + ## Default: 32 + cache.max_size: 32 + + ## The time after which an Authorization cache entry will be deleted + ## + ## @doc authorization.cache.ttl + ## ValueType: Duration + ## Default: 1m + cache.ttl: 1m +} + +mqtt { + ## How long time the MQTT connection will be disconnected if the + ## TCP connection is established but MQTT CONNECT has not been + ## received. + ## + ## @doc mqtt.idle_timeout + ## ValueType: Duration + ## Default: 15s + idle_timeout = 15s + + ## Maximum MQTT packet size allowed. + ## + ## @doc mqtt.max_packet_size + ## ValueType: Bytes + ## Default: 1MB + max_packet_size = 1MB + + ## Maximum length of MQTT clientId allowed. + ## + ## @doc mqtt.max_clientid_len + ## ValueType: Integer + ## Range: [23, 65535] + ## Default: 65535 + max_clientid_len = 65535 + + ## Maximum topic levels allowed. + ## + ## @doc mqtt.max_topic_levels + ## ValueType: Integer + ## Range: [1, 65535] + ## Default: 128 + ## Depth so big may lead to subscribing performance issues + max_topic_levels = 128 + + ## Maximum QoS allowed. + ## + ## @doc mqtt.max_qos_allowed + ## ValueType: 0 | 1 | 2 + ## Default: 2 + max_qos_allowed = 2 + + ## Maximum Topic Alias, 0 means no topic alias supported. + ## + ## @doc mqtt.max_topic_alias + ## ValueType: Integer + ## Range: [0, 65535] + ## Default: 65535 + max_topic_alias = 65535 + + ## Whether the Server supports MQTT retained messages. + ## + ## @doc mqtt.retain_available + ## ValueType: Boolean + ## Default: true + retain_available = true + + ## Whether the Server supports MQTT Wildcard Subscriptions + ## + ## @doc mqtt.wildcard_subscription + ## ValueType: Boolean + ## Default: true + wildcard_subscription = true + + ## Whether the Server supports MQTT Shared Subscriptions. + ## + ## @doc mqtt.shared_subscription + ## ValueType: Boolean + ## Default: true + shared_subscription = true + + ## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) + ## + ## @doc mqtt.ignore_loop_deliver + ## ValueType: Boolean + ## Default: false + ignore_loop_deliver = false + + ## Whether to parse the MQTT frame in strict mode + ## + ## @doc mqtt.strict_mode + ## ValueType: Boolean + ## Default: false + strict_mode = false + + ## Specify the response information returned to the client + ## + ## This feature is disabled if is set to "" + ## + ## @doc mqtt.response_information + ## ValueType: String + ## Default: "" + response_information = "" + + ## Server Keep Alive of MQTT 5.0 + ## + ## @doc mqtt.server_keepalive + ## ValueType: Number | disabled + ## Default: disabled + server_keepalive = disabled + + ## The backoff for MQTT keepalive timeout. The broker will kick a connection out + ## until 'Keepalive * backoff * 2' timeout. + ## + ## @doc mqtt.keepalive_backoff + ## ValueType: Float + ## Range: (0.5, 1] + ## Default: 0.75 + keepalive_backoff = 0.75 + + ## Maximum number of subscriptions allowed. + ## + ## @doc mqtt.max_subscriptions + ## ValueType: Integer | infinity + ## Range: [1, infinity) + ## Default: infinity + max_subscriptions = infinity + + ## Force to upgrade QoS according to subscription. + ## + ## @doc mqtt.upgrade_qos + ## ValueType: Boolean + ## Default: false + upgrade_qos = false + + ## Maximum size of the Inflight Window storing QoS1/2 messages delivered but unacked. + ## + ## @doc mqtt.max_inflight + ## ValueType: Integer + ## Range: [1, 65535] + ## Default: 32 + max_inflight = 32 + + ## Retry interval for QoS1/2 message delivering. + ## + ## @doc mqtt.retry_interval + ## ValueType: Duration + ## Default: 30s + retry_interval = 30s + + ## Maximum QoS2 packets (Client -> Broker) awaiting PUBREL. + ## + ## @doc mqtt.max_awaiting_rel + ## ValueType: Integer | infinity + ## Range: [1, infinity) + ## Default: 100 + max_awaiting_rel = 100 + + ## The QoS2 messages (Client -> Broker) will be dropped if awaiting PUBREL timeout. + ## + ## @doc mqtt.await_rel_timeout + ## ValueType: Duration + ## Default: 300s + await_rel_timeout = 300s + + ## Default session expiry interval for MQTT V3.1.1 connections. + ## + ## @doc mqtt.session_expiry_interval + ## ValueType: Duration + ## Default: 2h + session_expiry_interval = 2h + + ## Maximum queue length. Enqueued messages when persistent client disconnected, + ## or inflight window is full. + ## + ## @doc mqtt.max_mqueue_len + ## ValueType: Integer | infinity + ## Range: [0, infinity) + ## Default: 1000 + max_mqueue_len = 1000 + + ## Topic priorities. + ## + ## There's no priority table by default, hence all messages + ## are treated equal. + ## + ## Priority number [1-255] + ## + ## NOTE: comma and equal signs are not allowed for priority topic names + ## NOTE: Messages for topics not in the priority table are treated as + ## either highest or lowest priority depending on the configured + ## value for mqtt.mqueue_default_priority + ## + ## @doc mqtt.mqueue_priorities + ## ValueType: Map | disabled + ## Examples: + ## To configure "topic/1" > "topic/2": + ## mqueue_priorities: {"topic/1": 10, "topic/2": 8} + ## Default: disabled + mqueue_priorities = disabled + + ## Default to highest priority for topics not matching priority table + ## + ## @doc mqtt.mqueue_default_priority + ## ValueType: highest | lowest + ## Default: lowest + mqueue_default_priority = lowest + + ## Whether to enqueue QoS0 messages. + ## + ## @doc mqtt.mqueue_store_qos0 + ## ValueType: Boolean + ## Default: true + mqueue_store_qos0 = true + + ## Whether use username replace client id + ## + ## @doc mqtt.use_username_as_clientid + ## ValueType: Boolean + ## Default: false + use_username_as_clientid = false + + ## Use the CN, DN or CRT field from the client certificate as a username. + ## Only works for SSL connection. + ## + ## @doc mqtt.peer_cert_as_username + ## ValueType: cn | dn | crt | disabled + ## Default: disabled + peer_cert_as_username = disabled + + ## Use the CN, DN or CRT field from the client certificate as a clientid. + ## Only works for SSL connection. + ## + ## @doc mqtt.peer_cert_as_clientid + ## ValueType: cn | dn | crt | disabled + ## Default: disabled + peer_cert_as_clientid = disabled +} + +flapping_detect { + ## Enable Flapping Detection. + ## + ## This config controls the allowed maximum number of CONNECT received + ## from the same clientid in a time frame defined by `window_time`. + ## After the limit is reached, successive CONNECT requests are forbidden + ## (banned) until the end of the time period defined by `ban_time`. + ## + ## @doc flapping_detect.enable + ## ValueType: Boolean + ## Default: true + enable = false + + ## The max disconnect allowed of a MQTT Client in `window_time` + ## + ## @doc flapping_detect.max_count + ## ValueType: Integer + ## Default: 15 + max_count = 15 + + ## The time window for flapping detect + ## + ## @doc flapping_detect.window_time + ## ValueType: Duration + ## Default: 1m + window_time = 1m + + ## How long the clientid will be banned + ## + ## @doc flapping_detect.ban_time + ## ValueType: Duration + ## Default: 5m + ban_time = 5m + +} + +force_shutdown { + ## Enable force_shutdown + ## + ## @doc force_shutdown.enable + ## ValueType: Boolean + ## Default: true + enable = true + + ## Max message queue length + ## @doc force_shutdown.max_message_queue_len + ## ValueType: Integer + ## Range: (0, infinity) + ## Default: 1000 + max_message_queue_len = 1000 + + ## Total heap size + ## + ## @doc force_shutdown.max_heap_size + ## ValueType: Size + ## Default: 32MB + max_heap_size = 32MB +} + +overload_protection { + ## React on system overload or not + ## @doc overload_protection.enable + ## ValueType: Boolean + ## Default: false + enable = false + + ## Backoff delay in ms + ## @doc overload_protection.backoff_delay + ## ValueType: Integer + ## Range: (0, infinity) + ## Default: 1 + backoff_delay = 1 + + ## Backoff GC enabled + ## @doc overload_protection.backoff_gc + ## ValueType: Boolean + ## Default: false + backoff_gc = false + + ## Backoff hibernation enabled + ## @doc overload_protection.backoff_hibernation + ## ValueType: Boolean + ## Default: true + backoff_hibernation = true + + ## Backoff hibernation enabled + ## @doc overload_protection.backoff_hibernation + ## ValueType: Boolean + ## Default: true + backoff_new_conn = true +} + +force_gc { + ## Force the MQTT connection process GC after this number of + ## messages or bytes passed through. + ## + ## @doc force_gc.enable + ## ValueType: Boolean + ## Default: true + enable = true + + ## GC the process after how many messages received + ## @doc force_gc.max_message_queue_len + ## ValueType: Integer + ## Range: (0, infinity) + ## Default: 16000 + count = 16000 + + ## GC the process after how much bytes passed through + ## + ## @doc force_gc.bytes + ## ValueType: Size + ## Default: 16MB + bytes = 16MB +} + +conn_congestion { + ## Whether to alarm the congested connections. + ## + ## Sometimes the mqtt connection (usually an MQTT subscriber) may + ## get "congested" because there're too many packets to sent. + ## The socket trys to buffer the packets until the buffer is + ## full. If more packets comes after that, the packets will be + ## "pending" in a queue and we consider the connection is + ## "congested". + ## + ## Enable this to send an alarm when there's any bytes pending in + ## the queue. You could set the `sndbuf` to a larger value if the + ## alarm is triggered too often. + ## + ## The name of the alarm is of format "conn_congestion//". + ## Where the is the client-id of the congested MQTT connection. + ## And the is the username or "unknown_user" of not provided by the client. + ## + ## @doc conn_congestion.enable_alarm + ## ValueType: Boolean + ## Default: true + enable_alarm = true + + ## Won't clear the congested alarm in how long time. + ## The alarm is cleared only when there're no pending bytes in + ## the queue, and also it has been `min_alarm_sustain_duration` + ## time since the last time we considered the connection is "congested". + ## + ## This is to avoid clearing and sending the alarm again too often. + ## + ## @doc conn_congestion.min_alarm_sustain_duration + ## ValueType: Duration + ## Default: 1m + min_alarm_sustain_duration = 1m +} + +rate_limit { + ## Maximum connections per second. + ## + ## @doc zones..max_conn_rate + ## ValueType: Number | infinity + ## Default: 1000 + ## Examples: + ## max_conn_rate: 1000 + max_conn_rate = 1000 + + ## Message limit for the a external MQTT connection. + ## + ## @doc rate_limit.conn_messages_in + ## ValueType: String | infinity + ## Default: infinity + ## Examples: 100 messages per 10 seconds. + ## conn_messages_in: "100,10s" + conn_messages_in = "100,10s" + + ## Limit the rate of receiving packets for a MQTT connection. + ## The rate is counted by bytes of packets per second. + ## + ## The connection won't accept more messages if the messages come + ## faster than the limit. + ## + ## @doc rate_limit.conn_bytes_in + ## ValueType: String | infinity + ## Default: infinity + ## Examples: 100KB incoming per 10 seconds. + ## conn_bytes_in: "100KB,10s" + ## + conn_bytes_in = "100KB,10s" +} + +quota { + ## Messages quota for the each of external MQTT connection. + ## This value consumed by the number of recipient on a message. + ## + ## @doc quota.conn_messages_routing + ## ValueType: String | infinity + ## Default: infinity + ## Examples: 100 messaegs per 1s: + ## quota.conn_messages_routing: "100,1s" + conn_messages_routing = "100,1s" + + ## Messages quota for the all of external MQTT connections. + ## This value consumed by the number of recipient on a message. + ## + ## @doc quota.overall_messages_routing + ## ValueType: String | infinity + ## Default: infinity + ## Examples: 200000 messages per 1s: + ## quota.overall_messages_routing: "200000,1s" + ## + overall_messages_routing = "200000,1s" +} + +##================================================================== +## Zones +##================================================================== +## A zone contains a set of configurations for listeners. +## +## A zone can be used by a listener via `listener...zone`. +## +## The configs defined in zones will override the global configs with the same key. +## +## For example given the following config: +## +## ``` +## a { +## b: 1, c: 1 +## } +## +## zone.my_zone { +## a { +## b:2 +## } +## } +## ``` +## +## The global config "a" is overridden by the configs "a" inside the zone "my_zone". +## If there is a listener uses the zone "my_zone", the value of config "a" will be: +## `{b:2, c: 1}`. +## Note that although the default value of `a.c` is `0`, the global value is used. +## i.e. configs in the zone have no default values. To overridde `a.c` we must configure +## it explicitly in the zone. +## +## All the global configs that can be overridden in zones are: +## - `stats.*` +## - `mqtt.*` +## - `authorization.*` +## - `flapping_detect.*` +## - `force_shutdown.*` +## - `conn_congestion.*` +## - `rate_limit.*` +## - `quota.*` +## - `force_gc.*` +## +## syntax: zones. +## example: zones.my_zone +zones.default { + +} + +##================================================================== +## Broker +##================================================================== +broker { + ## System interval of publishing $SYS messages. + ## + ## @doc broker.sys_msg_interval + ## ValueType: Duration | disabled + ## Default: 1m + sys_msg_interval = 1m + + ## System heartbeat interval of publishing following heart beat message: + ## - "$SYS/brokers//uptime" + ## - "$SYS/brokers//datetime" + ## + ## @doc broker.sys_heartbeat_interval + ## ValueType: Duration + ## Default: 30s | disabled + sys_heartbeat_interval = 30s + + ## Session locking strategy in a cluster. + ## + ## @doc broker.session_locking_strategy + ## ValueType: local | one | quorum | all + ## - local: only lock the session locally on the current node + ## - one: select only one remove node to lock the session + ## - quorum: select some nodes to lock the session + ## - all: lock the session on all of the nodes in the cluster + ## Default: quorum + session_locking_strategy = quorum + + ## Dispatch strategy for shared subscription + ## + ## @doc broker.shared_subscription_strategy + ## ValueType: random | round_robin | sticky | hash + ## - random: dispatch the message to a random selected subscriber + ## - round_robin: select the subscribers in a round-robin manner + ## - sticky: always use the last selected subscriber to dispatch, + ## until the susbcriber disconnected. + ## - hash: select the subscribers by the hash of clientIds + ## Default: round_robin + shared_subscription_strategy = round_robin + + ## Enable/disable shared dispatch acknowledgement for QoS1 and QoS2 messages + ## This should allow messages to be dispatched to a different subscriber in + ## the group in case the picked (based on shared_subscription_strategy) one # is offline + ## + ## @doc broker.shared_dispatch_ack_enabled + ## ValueType: Boolean + ## Default: false + shared_dispatch_ack_enabled = false + + ## Enable batch clean for deleted routes. + ## + ## @doc broker.route_batch_clean + ## ValueType: Boolean + ## Default: true + route_batch_clean = true + + ## Performance toggle for subscribe/unsubscribe wildcard topic. + ## Change this toggle only when there are many wildcard topics. + ## + ## NOTE: when changing from/to 'global' lock, it requires all + ## nodes in the cluster to be stopped before the change. + ## + ## @doc broker.perf.route_lock_type + ## ValueType: key | tab | global + ## - key: mnesia translational updates with per-key locks. recommended for single node setup. + ## - tab: mnesia translational updates with table lock. recommended for multi-nodes setup. + ## - global: global lock protected updates. recommended for larger cluster. + ## Default: key + perf.route_lock_type = key + + ## Enable trie path compaction. + ## Enabling it significantly improves wildcard topic subscribe + ## rate, if wildcard topics have unique prefixes like: + ## 'sensor//+/', where ID is unique per subscriber. + ## + ## Topic match performance (when publishing) may degrade if messages + ## are mostly published to topics with large number of levels. + ## + ## NOTE: This is a cluster-wide configuration. + ## It requires all nodes to be stopped before changing it. + ## + ## @doc broker.perf.trie_compaction + ## ValueType: Boolean + ## Default: true + perf.trie_compaction = true +} + +##================================================================== +## System Monitor +##================================================================== +sysmon { + ## The time interval for the periodic process limit check + ## + ## @doc sysmon.vm.process_check_interval + ## ValueType: Duration + ## Default: 30s + vm.process_check_interval = 30s + + ## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is set. + ## + ## @doc sysmon.vm.process_high_watermark + ## ValueType: Percentage + ## Default: 80% + vm.process_high_watermark = 80% + + ## The threshold, as percentage of processes, for how many processes can simultaneously exist at the local node before the corresponding alarm is clear. + ## + ## @doc sysmon.vm.process_low_watermark + ## ValueType: Percentage + ## Default: 60% + vm.process_low_watermark = 60% + + ## Enable Long GC monitoring. + ## Notice: don't enable the monitor in production for: + ## https://github.com/erlang/otp/blob/feb45017da36be78d4c5784d758ede619fa7bfd3/erts/emulator/beam/erl_gc.c#L421 + ## + ## @doc sysmon.vm.long_gc + ## ValueType: Duration | disabled + ## Default: disabled + vm.long_gc = disabled + + ## Enable Long Schedule(ms) monitoring. + ## + ## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 + ## + ## @doc sysmon.vm.long_schedule + ## ValueType: Duration | disabled + ## Default: disabled + vm.long_schedule = 240ms + + ## Enable Large Heap monitoring. + ## + ## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 + ## + ## @doc sysmon.vm.large_heap + ## ValueType: Size | disabled + ## Default: 32MB + vm.large_heap = 32MB + + ## Enable Busy Port monitoring. + ## + ## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 + ## + ## @doc sysmon.vm.busy_port + ## ValueType: Boolean + ## Default: true + vm.busy_port = true + + ## Enable Busy Dist Port monitoring. + ## + ## See: http://erlang.org/doc/man/erlang.html#system_monitor-2 + ## + ## @doc sysmon.vm.busy_dist_port + ## ValueType: Boolean + ## Default: true + vm.busy_dist_port = true + + ## The time interval for the periodic cpu check + ## + ## @doc sysmon.os.cpu_check_interval + ## ValueType: Duration + ## Default: 60s + os.cpu_check_interval = 60s + + ## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is set. + ## + ## @doc sysmon.os.cpu_high_watermark + ## ValueType: Percentage + ## Default: 80% + os.cpu_high_watermark = 80% + + ## The threshold, as percentage of system cpu, for how much system cpu can be used before the corresponding alarm is clear. + ## + ## @doc sysmon.os.cpu_low_watermark + ## ValueType: Percentage + ## Default: 60% + os.cpu_low_watermark = 60% + + ## The time interval for the periodic memory check + ## + ## @doc sysmon.os.mem_check_interval + ## ValueType: Duration | disabled + ## Default: 60s + os.mem_check_interval = 60s + + ## The threshold, as percentage of system memory, for how much system memory can be allocated before the corresponding alarm is set. + ## + ## @doc sysmon.os.sysmem_high_watermark + ## ValueType: Percentage + ## Default: 70% + os.sysmem_high_watermark = 70% + + ## The threshold, as percentage of system memory, for how much system memory can be allocated by one Erlang process before the corresponding alarm is set. + ## + ## @doc sysmon.os.procmem_high_watermark + ## ValueType: Percentage + ## Default: 5% + os.procmem_high_watermark = 5% +} + +##================================================================== +## Alarm +##================================================================== +alarm { + ## Specifies the actions to take when an alarm is activated + ## + ## @doc alarm.actions + ## ValueType: Array + ## Default: [log, publish] + actions = [log, publish] + + ## The maximum number of deactivated alarms + ## + ## @doc alarm.size_limit + ## ValueType: Integer + ## Default: 1000 + size_limit = 1000 + + ## Validity Period of deactivated alarms + ## + ## @doc alarm.validity_period + ## ValueType: Duration + ## Default: 24h + validity_period = 24h +} + +## Config references for listeners + +## Socket options for TCP connections +## See: http://erlang.org/doc/man/inet.html +example_common_tcp_options { + ## Specify the {active, N} option for this Socket. + ## + ## See: https://erlang.org/doc/man/inet.html#setopts-2 + ## + ## @doc listeners..tcp.active_n + ## ValueType: Number + ## Default: 100 + tcp.active_n = 100 + + ## TCP backlog defines the maximum length that the queue of + ## pending connections can grow to. + ## + ## @doc listeners..tcp.backlog + ## ValueType: Number + ## Range: [0, 1048576] + ## Default: 1024 + tcp.backlog = 1024 + + ## The TCP send timeout for the connections. + ## + ## @doc listeners..tcp.send_timeout + ## ValueType: Duration + ## Default: 15s + tcp.send_timeout = 15s + + ## Close the connection if send timeout. + ## + ## @doc listeners..tcp.send_timeout_close + ## ValueType: Boolean + ## Default: true + tcp.send_timeout_close = true + + ## The TCP receive buffer(os kernel) for the connections. + ## + ## @doc listeners..tcp.recbuf + ## ValueType: Size + ## Default: notset + #tcp.recbuf: 2KB + + ## The TCP send buffer(os kernel) for the connections. + ## + ## @doc listeners..tcp.sndbuf + ## ValueType: Size + ## Default: notset + #tcp.sndbuf: 4KB + + ## The size of the user-level software buffer used by the driver. + ## + ## @doc listeners..tcp.buffer + ## ValueType: Size + ## Default: notset + #tcp.buffer: 4KB + + ## The socket is set to a busy state when the amount of data queued internally + ## by the ERTS socket implementation reaches this limit. + ## + ## @doc listeners..tcp.high_watermark + ## ValueType: Size + ## Default: 1MB + tcp.high_watermark = 1MB + + ## The TCP_NODELAY flag for the connections. + ## + ## @doc listeners..tcp.nodelay + ## ValueType: Boolean + ## Default: false + tcp.nodelay = false + + ## The SO_REUSEADDR flag for the connections. + ## + ## @doc listeners..tcp.reuseaddr + ## ValueType: Boolean + ## Default: true + tcp.reuseaddr = true +} + +## Socket options for SSL connections +## See: http://erlang.org/doc/man/ssl.html +example_common_ssl_options { + + ## A performance optimization setting, it allows clients to reuse + ## pre-existing sessions, instead of initializing new ones. + ## Read more about it here. + ## + ## @doc listeners..ssl.reuse_sessions + ## ValueType: Boolean + ## Default: true + ssl.reuse_sessions = true + + ## 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. + ## + ## @doc listeners..ssl.secure_renegotiate + ## ValueType: Boolean + ## Default: true + ssl.secure_renegotiate = true + + ## In protocols that support client-initiated renegotiation, + ## the cost of resources of such an operation is higher for the server than the client. + ## This can act as a vector for denial of service attacks. + ## The SSL application already takes measures to counter-act such attempts, + ## but client-initiated renegotiation can be strictly disabled by setting this option to false. + ## The default value is true. Note that disabling renegotiation can result in + ## long-lived connections becoming unusable due to limits on + ## the number of messages the underlying cipher suite can encipher. + ssl.client_renegotiation = true + + ## 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. + ## + ## @doc listeners..ssl.honor_cipher_order + ## ValueType: Boolean + ## Default: true + ssl.honor_cipher_order = true + + # ssl.versions = ["tlsv1.3", "tlsv1.2", "tlsv1.1", "tlsv1"] + # TLS 1.3: "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" + # TLS 1-1.2 "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" + # PSK: "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA" + # NOTE: If PSK cipher-suites are intended, tlsv1.3 should not be enabled in 'versions' config + # NOTE: by default, ALL ciphers are enabled + # ssl.ciphers = "" + + ## TLS Handshake timeout. + ## + ## @doc listeners..ssl.handshake_timeout + ## ValueType: Duration + ## Default: 15s + ssl.handshake_timeout = 15s + + ## Maximum number of non-self-issued intermediate certificates that + ## can follow the peer certificate in a valid certification path. + ## + ## @doc listeners..ssl.depth + ## ValueType: Integer + ## Default: 10 + ssl.depth = 10 + + ## Path to the file containing the user's private PEM-encoded key. + ## + ## @doc listeners..ssl.keyfile + ## ValueType: File + ## Default: "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/key.pem" + ssl.keyfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/key.pem" + + ## Path to a file containing the user certificate. + ## + ## @doc listeners..ssl.certfile + ## ValueType: File + ## Default: "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/cert.pem" + ssl.certfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/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. + ## + ## @doc listeners..ssl.cacertfile + ## ValueType: File + ## Default: "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/cacert.pem" + ssl.cacertfile = "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/cacert.pem" + + ## Maximum number of non-self-issued intermediate certificates that + ## can follow the peer certificate in a valid certification path. + ## + ## @doc listeners..ssl.depth + ## ValueType: Number + ## Default: 10 + ssl.depth = 10 + + ## String containing the user's password. Only used if the private keyfile + ## is password-protected. + ## + ## See: listener.ssl.$name.key_password + ## + ## @doc listeners..ssl.depth + ## ValueType: String + ## Default: "" + #ssl.key_password: "" + + ## 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 + ## + ## @doc listeners..ssl.dhfile + ## ValueType: File + ## Default: "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/certs/dh-params.pem" + #ssl.dhfile: "/Users/zhongwen/github/emqx/emqx-v5/apps/emqx/etc/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 + ## + ## @doc listeners..ssl.verify + ## ValueType: verify_peer | verify_none + ## Default: verify_none + ssl.verify = verify_none + + ## 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. + ## + ## @doc listeners..ssl.fail_if_no_peer_cert + ## ValueType: Boolean + ## Default: true + ssl.fail_if_no_peer_cert = false + +} + +## Socket options for websocket connections +example_common_websocket_options { + ## The path of WebSocket MQTT endpoint + ## + ## @doc listeners..websocket.mqtt_path + ## ValueType: Path + ## Default: "/mqtt" + websocket.mqtt_path = "/mqtt" + + ## Whether a WebSocket message is allowed to contain multiple MQTT packets + ## + ## @doc listeners..websocket.mqtt_piggyback + ## ValueType: single | multiple + ## Default: multiple + websocket.mqtt_piggyback = multiple + + ## The compress flag for external WebSocket connections. + ## + ## If this Value is set true,the websocket message would be compressed + ## + ## @doc listeners..websocket.compress + ## ValueType: Boolean + ## Default: false + websocket.compress = false + + ## The idle timeout for external WebSocket connections. + ## + ## @doc listeners..websocket.idle_timeout + ## ValueType: Duration | infinity + ## Default: infinity + websocket.idle_timeout = infinity + + ## The max frame size for external WebSocket connections. + ## + ## @doc listeners..websocket.max_frame_size + ## ValueType: Size + ## Default: infinity + websocket.max_frame_size = infinity + + ## If set to true, the server fails if the client does not + ## have a Sec-WebSocket-Protocol to send. + ## Set to false for WeChat MiniApp. + ## + ## @doc listeners..websocket.fail_if_no_subprotocol + ## ValueType: Boolean + ## Default: true + websocket.fail_if_no_subprotocol = true + + ## Supported subprotocols + ## + ## @doc listeners..websocket.supported_subprotocols + ## ValueType: String + ## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 + websocket.supported_subprotocols = "mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5" + + ## Enable origin check in header for websocket connection + ## + ## @doc listeners..websocket.check_origin_enable + ## ValueType: Boolean + ## Default: false + websocket.check_origin_enable = false + + ## Allow origin to be absent in header in websocket connection + ## when check_origin_enable is true + ## + ## @doc listeners..websocket.allow_origin_absence + ## ValueType: Boolean + ## Default: true + websocket.allow_origin_absence = true + + ## Comma separated list of allowed origin in header for websocket connection + ## + ## @doc listeners..websocket.check_origins + ## ValueType: String + ## Examples: + ## local http dashboard url + ## check_origins: "http://localhost:18083, http://127.0.0.1:18083" + ## Default: "" + websocket.check_origins = "http://localhost:18083, http://127.0.0.1:18083" + + ## Specify which HTTP header for real source IP if the EMQ X cluster is + ## deployed behind NGINX or HAProxy. + ## + ## @doc listeners..websocket.proxy_address_header + ## ValueType: String + ## Default: X-Forwarded-For + websocket.proxy_address_header = X-Forwarded-For + + ## Specify which HTTP header for real source port if the EMQ X cluster is + ## deployed behind NGINX or HAProxy. + ## + ## @doc listeners..websocket.proxy_port_header + ## ValueType: String + ## Default: X-Forwarded-Port + websocket.proxy_port_header = X-Forwarded-Port + + websocket.deflate_opts { + ## The level of deflate options for external WebSocket connections. + ## + ## @doc listeners..websocket.deflate_opts.level + ## ValueType: none | default | best_compression | best_speed + ## Default: default + level = default + + ## The mem_level of deflate options for external WebSocket connections. + ## + ## @doc listeners..websocket.deflate_opts.mem_level + ## ValueType: Integer + ## Range: [1,9] + ## Default: 8 + mem_level = 8 + + ## The strategy of deflate options for external WebSocket connections. + ## + ## @doc listeners..websocket.deflate_opts.strategy + ## ValueType: default | filtered | huffman_only | rle + ## Default: default + strategy = default + + ## The deflate option for external WebSocket connections. + ## + ## @doc listeners..websocket.deflate_opts.server_context_takeover + ## ValueType: takeover | no_takeover + ## Default: takeover + server_context_takeover = takeover + + ## The deflate option for external WebSocket connections. + ## + ## @doc listeners..websocket.deflate_opts.client_context_takeover + ## ValueType: takeover | no_takeover + ## Default: takeover + client_context_takeover = takeover + + ## The deflate options for external WebSocket connections. + ## + ## + ## @doc listeners..websocket.deflate_opts.server_max_window_bits + ## ValueType: Integer + ## Range: [8,15] + ## Default: 15 + server_max_window_bits = 15 + + ## The deflate options for external WebSocket connections. + ## + ## @doc listeners..websocket.deflate_opts.client_max_window_bits + ## ValueType: Integer + ## Range: [8,15] + ## Default: 15 + client_max_window_bits = 15 + } +} + +persistent_session_store { + ## Enable/disable internal persistent session store. + ## + ## @doc persistent_session_store.enabled + ## ValueType: Boolean + ## Default: false + enabled = false + + ## How long are undelivered messages retained in the store + ## + ## @doc persistent_session_store.max_retain_undelivered + ## ValueType: Duration + ## Default: 1h + max_retain_undelivered = 1h + + ## The time interval in which to try to run garbage collection of persistent session messages + ## + ## @doc persistent_session_store.message_gc_interval + ## ValueType: Duration + ## Default: 1h + message_gc_interval = 1h + + ## The time interval in which to try to run garbage collection of persistent session transient data + ## + ## @doc persistent_session_store.session_message_gc_interval + ## ValueType: Duration + ## Default: 1m + session_message_gc_interval = 1m +} diff --git a/apps/emqx/src/emqx_trace/emqx_trace.erl b/apps/emqx/src/emqx_trace/emqx_trace.erl index 7bca3ca3c..67542a2bf 100644 --- a/apps/emqx/src/emqx_trace/emqx_trace.erl +++ b/apps/emqx/src/emqx_trace/emqx_trace.erl @@ -112,7 +112,6 @@ log_filter([{Id, FilterFun, Filter, Name} | Rest], Log0) -> case logger_config:get(ets:whereis(logger), Id) of {ok, #{module := Module} = HandlerConfig0} -> HandlerConfig = maps:without(?OWN_KEYS, HandlerConfig0), - io:format("~p~n", [{Module, Log, HandlerConfig}]), try Module:log(Log, HandlerConfig) catch C:R:S -> case logger:remove_handler(Id) of diff --git a/apps/emqx/test/emqx_trace_handler_SUITE.erl b/apps/emqx/test/emqx_trace_handler_SUITE.erl index 01d587028..f3ab7b5b8 100644 --- a/apps/emqx/test/emqx_trace_handler_SUITE.erl +++ b/apps/emqx/test/emqx_trace_handler_SUITE.erl @@ -50,15 +50,15 @@ end_per_testcase(_Case, _Config) -> t_trace_clientid(_Config) -> %% Start tracing %% add list clientid - ok = emqx_trace_handler:install(clientid, "client", debug, "tmp/client.log"), - ok = emqx_trace_handler:install(clientid, <<"client2">>, all, "tmp/client2.log"), - ok = emqx_trace_handler:install(clientid, <<"client3">>, all, "tmp/client3.log"), + ok = emqx_trace_handler:install("CLI-client1", clientid, "client", debug, "tmp/client.log"), + ok = emqx_trace_handler:install("CLI-client2", clientid, <<"client2">>, all, "tmp/client2.log"), + ok = emqx_trace_handler:install("CLI-client3", clientid, <<"client3">>, all, "tmp/client3.log"), {error, {handler_not_added, {file_error, ".", eisdir}}} = emqx_trace_handler:install(clientid, <<"client5">>, debug, "."), emqx_trace:check(), - ok = filesync(<<"client">>, clientid), - ok = filesync(<<"client2">>, clientid), - ok = filesync(<<"client3">>, clientid), + ok = filesync(<<"CLI-client1">>, clientid), + ok = filesync(<<"CLI-client2">>, clientid), + ok = filesync(<<"CLI-client3">>, clientid), %% Verify the tracing file exits ?assert(filelib:is_regular("tmp/client.log")), @@ -66,11 +66,11 @@ t_trace_clientid(_Config) -> ?assert(filelib:is_regular("tmp/client3.log")), %% Get current traces - ?assertMatch([#{type := clientid, filter := "client", name := <<"client">>, + ?assertMatch([#{type := clientid, filter := "client", name := <<"CLI-client1">>, level := debug, dst := "tmp/client.log"}, - #{type := clientid, filter := "client2", name := <<"client2">> + #{type := clientid, filter := "client2", name := <<"CLI-client2">> , level := debug, dst := "tmp/client2.log"}, - #{type := clientid, filter := "client3", name := <<"client3">>, + #{type := clientid, filter := "client3", name := <<"CLI-client3">>, level := debug, dst := "tmp/client3.log"} ], emqx_trace_handler:running()), @@ -79,9 +79,9 @@ t_trace_clientid(_Config) -> emqtt:connect(T), emqtt:publish(T, <<"a/b/c">>, <<"hi">>), emqtt:ping(T), - ok = filesync(<<"client">>, clientid), - ok = filesync(<<"client2">>, clientid), - ok = filesync(<<"client3">>, clientid), + ok = filesync(<<"CLI-client1">>, clientid), + ok = filesync(<<"CLI-client2">>, clientid), + ok = filesync(<<"CLI-client3">>, clientid), %% Verify messages are logged to "tmp/client.log" but not "tmp/client2.log". {ok, Bin} = file:read_file("tmp/client.log"), @@ -92,24 +92,24 @@ t_trace_clientid(_Config) -> ?assert(filelib:file_size("tmp/client2.log") == 0), %% Stop tracing - ok = emqx_trace_handler:uninstall(clientid, <<"client">>), - ok = emqx_trace_handler:uninstall(clientid, <<"client2">>), - ok = emqx_trace_handler:uninstall(clientid, <<"client3">>), + ok = emqx_trace_handler:uninstall(clientid, <<"CLI-client1">>), + ok = emqx_trace_handler:uninstall(clientid, <<"CLI-client2">>), + ok = emqx_trace_handler:uninstall(clientid, <<"CLI-client3">>), emqtt:disconnect(T), ?assertEqual([], emqx_trace_handler:running()). t_trace_clientid_utf8(_) -> Utf8Id = <<"client 漢字編碼"/utf8>>, - ok = emqx_trace_handler:install(clientid, Utf8Id, debug, "tmp/client-utf8.log"), + ok = emqx_trace_handler:install("CLI-UTF8", clientid, Utf8Id, debug, "tmp/client-utf8.log"), emqx_trace:check(), {ok, T} = emqtt:start_link([{clientid, Utf8Id}]), emqtt:connect(T), [begin emqtt:publish(T, <<"a/b/c">>, <<"hi">>) end|| _ <- lists:seq(1, 10)], emqtt:ping(T), - ok = filesync(Utf8Id, clientid), - ok = emqx_trace_handler:uninstall(clientid, Utf8Id), + ok = filesync("CLI-UTF8", clientid), + ok = emqx_trace_handler:uninstall(clientid, "CLI-UTF8"), emqtt:disconnect(T), ?assertEqual([], emqx_trace_handler:running()), ok. @@ -119,11 +119,11 @@ t_trace_topic(_Config) -> emqtt:connect(T), %% Start tracing - ok = emqx_trace_handler:install(topic, <<"x/#">>, all, "tmp/topic_trace_x.log"), - ok = emqx_trace_handler:install(topic, <<"y/#">>, all, "tmp/topic_trace_y.log"), + ok = emqx_trace_handler:install("CLI-TOPIC-1", topic, <<"x/#">>, all, "tmp/topic_trace_x.log"), + ok = emqx_trace_handler:install("CLI-TOPIC-2", topic, <<"y/#">>, all, "tmp/topic_trace_y.log"), emqx_trace:check(), - ok = filesync(<<"x/#">>, topic), - ok = filesync(<<"y/#">>, topic), + ok = filesync("CLI-TOPIC-1", topic), + ok = filesync("CLI-TOPIC-2", topic), %% Verify the tracing file exits ?assert(filelib:is_regular("tmp/topic_trace_x.log")), @@ -131,9 +131,9 @@ t_trace_topic(_Config) -> %% Get current traces ?assertMatch([#{type := topic, filter := <<"x/#">>, - level := debug, dst := "tmp/topic_trace_x.log", name := <<"x/#">>}, + level := debug, dst := "tmp/topic_trace_x.log", name := <<"CLI-TOPIC-1">>}, #{type := topic, filter := <<"y/#">>, - name := <<"y/#">>, level := debug, dst := "tmp/topic_trace_y.log"} + name := <<"CLI-TOPIC-2">>, level := debug, dst := "tmp/topic_trace_y.log"} ], emqx_trace_handler:running()), @@ -142,8 +142,8 @@ t_trace_topic(_Config) -> emqtt:publish(T, <<"x/y/z">>, <<"hi2">>), emqtt:subscribe(T, <<"x/y/z">>), emqtt:unsubscribe(T, <<"x/y/z">>), - ok = filesync(<<"x/#">>, topic), - ok = filesync(<<"y/#">>, topic), + ok = filesync("CLI-TOPIC-1", topic), + ok = filesync("CLI-TOPIC-2", topic), {ok, Bin} = file:read_file("tmp/topic_trace_x.log"), ?assertNotEqual(nomatch, binary:match(Bin, [<<"hi1">>])), @@ -154,8 +154,8 @@ t_trace_topic(_Config) -> ?assert(filelib:file_size("tmp/topic_trace_y.log") =:= 0), %% Stop tracing - ok = emqx_trace_handler:uninstall(topic, <<"x/#">>), - ok = emqx_trace_handler:uninstall(topic, <<"y/#">>), + ok = emqx_trace_handler:uninstall(topic, <<"CLI-TOPIC-1">>), + ok = emqx_trace_handler:uninstall(topic, <<"CLI-TOPIC-2">>), {error, _Reason} = emqx_trace_handler:uninstall(topic, <<"z/#">>), ?assertEqual([], emqx_trace_handler:running()), emqtt:disconnect(T). @@ -165,11 +165,12 @@ t_trace_ip_address(_Config) -> emqtt:connect(T), %% Start tracing - ok = emqx_trace_handler:install(ip_address, "127.0.0.1", all, "tmp/ip_trace_x.log"), - ok = emqx_trace_handler:install(ip_address, "192.168.1.1", all, "tmp/ip_trace_y.log"), + ok = emqx_trace_handler:install("CLI-IP-1", ip_address, "127.0.0.1", all, "tmp/ip_trace_x.log"), + ok = emqx_trace_handler:install("CLI-IP-2", ip_address, + "192.168.1.1", all, "tmp/ip_trace_y.log"), emqx_trace:check(), - ok = filesync(<<"127.0.0.1">>, ip_address), - ok = filesync(<<"192.168.1.1">>, ip_address), + ok = filesync(<<"CLI-IP-1">>, ip_address), + ok = filesync(<<"CLI-IP-2">>, ip_address), %% Verify the tracing file exits ?assert(filelib:is_regular("tmp/ip_trace_x.log")), @@ -177,10 +178,10 @@ t_trace_ip_address(_Config) -> %% Get current traces ?assertMatch([#{type := ip_address, filter := "127.0.0.1", - name := <<"127.0.0.1">>, + name := <<"CLI-IP-1">>, level := debug, dst := "tmp/ip_trace_x.log"}, #{type := ip_address, filter := "192.168.1.1", - name := <<"192.168.1.1">>, + name := <<"CLI-IP-2">>, level := debug, dst := "tmp/ip_trace_y.log"} ], emqx_trace_handler:running()), @@ -190,8 +191,8 @@ t_trace_ip_address(_Config) -> emqtt:publish(T, <<"x/y/z">>, <<"hi2">>), emqtt:subscribe(T, <<"x/y/z">>), emqtt:unsubscribe(T, <<"x/y/z">>), - ok = filesync(<<"127.0.0.1">>, ip_address), - ok = filesync(<<"192.168.1.1">>, ip_address), + ok = filesync(<<"CLI-IP-1">>, ip_address), + ok = filesync(<<"CLI-IP-2">>, ip_address), {ok, Bin} = file:read_file("tmp/ip_trace_x.log"), ?assertNotEqual(nomatch, binary:match(Bin, [<<"hi1">>])), @@ -202,8 +203,8 @@ t_trace_ip_address(_Config) -> ?assert(filelib:file_size("tmp/ip_trace_y.log") =:= 0), %% Stop tracing - ok = emqx_trace_handler:uninstall(ip_address, <<"127.0.0.1">>), - ok = emqx_trace_handler:uninstall(ip_address, <<"192.168.1.1">>), + ok = emqx_trace_handler:uninstall(ip_address, <<"CLI-IP-1">>), + ok = emqx_trace_handler:uninstall(ip_address, <<"CLI-IP-2">>), {error, _Reason} = emqx_trace_handler:uninstall(ip_address, <<"127.0.0.2">>), emqtt:disconnect(T), ?assertEqual([], emqx_trace_handler:running()). @@ -215,7 +216,12 @@ filesync(Name, Type) -> %% sometime the handler process is not started yet. filesync(_Name, _Type, 0) -> ok; -filesync(Name, Type, Retry) -> +filesync(Name0, Type, Retry) -> + Name = + case is_binary(Name0) of + true -> Name0; + false -> list_to_binary(Name0) + end, try Handler = binary_to_atom(<<"trace_", (atom_to_binary(Type))/binary, "_", Name/binary>>), diff --git a/apps/emqx_management/src/emqx_mgmt_api_trace.erl b/apps/emqx_management/src/emqx_mgmt_api_trace.erl index 5669e5653..d6902d123 100644 --- a/apps/emqx_management/src/emqx_mgmt_api_trace.erl +++ b/apps/emqx_management/src/emqx_mgmt_api_trace.erl @@ -142,11 +142,11 @@ fields(trace) -> #{desc => """Filter type""", nullable => false, example => <<"clientid">>})}, - {topic, hoconsc:mk(emqx_schema:unicode_binary(), + {topic, hoconsc:mk(binary(), #{desc => """support mqtt wildcard topic.""", nullable => true, example => <<"/dev/#">>})}, - {clientid, hoconsc:mk(emqx_schema:unicode_binary(), + {clientid, hoconsc:mk(binary(), #{desc => """mqtt clientid.""", nullable => true, example => <<"dev-001">>})}, diff --git a/apps/emqx_modules/etc/emqx_modules.conf.rendered b/apps/emqx_modules/etc/emqx_modules.conf.rendered new file mode 100644 index 000000000..be34479e8 --- /dev/null +++ b/apps/emqx_modules/etc/emqx_modules.conf.rendered @@ -0,0 +1,50 @@ + +delayed { + enable = true + ## 0 is no limit + max_delayed_messages = 0 +} + +observer_cli { + enable = true +} + +telemetry { + enable = true +} + +event_message { + "$event/client_connected" = true + "$event/client_disconnected" = true + # "$event/client_subscribed": false + # "$event/client_unsubscribed": false + # "$event/message_delivered": false + # "$event/message_acked": false + # "$event/message_dropped": false +} + +topic_metrics: [ + #{topic: "test/1"} +] + +rewrite: [ + # { + # action = publish + # source_topic = "x/#" + # re = "^x/y/(.+)$" + # dest_topic = "z/y/$1" + # }, + # { + # action = subscribe + # source_topic = "x1/#" + # re = "^x1/y/(.+)$" + # dest_topic = "z1/y/$1" + # }, + # { + # action = all + # source_topic = "x2/#" + # re = "^x2/y/(.+)$" + # dest_topic = "z2/y/$1" + # } +] +