diff --git a/apps/emqx/etc/emqx.conf b/apps/emqx/etc/emqx.conf index f6627bf1c..6be808264 100644 --- a/apps/emqx/etc/emqx.conf +++ b/apps/emqx/etc/emqx.conf @@ -2155,6 +2155,312 @@ listener.wss.external.allow_origin_absence = true ## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084 listener.wss.external.check_origins = "https://localhost:8084, https://127.0.0.1:8084" + +##-------------------------------------------------------------------- +## External QUIC listener for MQTT Protocol + +## listener.quic.$name is the IP address and port that the MQTT/QUIC +## listener will bind. +## +## Value: IP:Port | Port +## +## Examples: 8084, 127.0.0.1:8084, ::1:8084 +listener.quic.external = 4567 + +## The path of WebSocket MQTT endpoint +## +## Value: URL Path +listener.quic.external.mqtt_path = /mqtt + +## The acceptor pool for external MQTT/QUIC listener. +## +## Value: Number +listener.quic.external.acceptors = 4 + +## Maximum number of concurrent MQTT/Webwocket/SSL connections. +## +## Value: Number +listener.quic.external.max_connections = 16 + +## Maximum MQTT/QUIC connections per second. +## +## See: listener.tcp.$name.max_conn_rate +## +## Value: Number +listener.quic.external.max_conn_rate = 1000 + +## Simulate the {active, N} option for the MQTT/QUIC connections. +## +## Value: Number +listener.quic.external.active_n = 100 + +## Zone of the external MQTT/QUIC listener belonged to. +## +## Value: String +listener.quic.external.zone = external + +## The access control rules for the MQTT/QUIC listener. +## +## See: listener.tcp.$name.access. +## +## Value: ACL Rule +listener.quic.external.access.1 = allow all + +## 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. +## +## Value: true | false +## listener.quic.external.fail_if_no_subprotocol = true + +## Supported subprotocols +## +## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 +## listener.quic.external.supported_subprotocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 + +## Enable the Proxy Protocol V1/2 support. +## +## See: listener.tcp.$name.proxy_protocol +## +## Value: on | off +## listener.quic.external.proxy_protocol = on + +## Sets the timeout for proxy protocol. +## +## See: listener.tcp.$name.proxy_protocol_timeout +## +## Value: Duration +## listener.quic.external.proxy_protocol_timeout = 3s + +## TLS versions only to protect from POODLE attack. +## +## See: listener.ssl.$name.tls_versions +## +## Value: String, seperated by ',' +## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier +## listener.quic.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 + +## Path to the file containing the user's private PEM-encoded key. +## +## See: listener.ssl.$name.keyfile +## +## Value: File +listener.quic.external.keyfile = {{ platform_etc_dir }}/certs/key.pem + +## Path to a file containing the user certificate. +## +## See: listener.ssl.$name.certfile +## +## Value: File +listener.quic.external.certfile = {{ platform_etc_dir }}/certs/cert.pem + +## Path to the file containing PEM-encoded CA certificates. +## +## See: listener.ssl.$name.cacert +## +## Value: File +## listener.quic.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + +## Maximum number of non-self-issued intermediate certificates that +## can follow the peer certificate in a valid certification path. +## +## See: listener.ssl.external.depth +## +## Value: Number +## listener.quic.external.depth = 10 + +## String containing the user's password. Only used if the private keyfile +## is password-protected. +## +## See: listener.ssl.$name.key_password +## +## Value: String +## listener.quic.external.key_password = yourpass + +## See: listener.ssl.$name.dhfile +## +## Value: File +## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem + +## See: listener.ssl.$name.verify +## +## Value: verify_peer | verify_none +## listener.quic.external.verify = verify_peer + +## See: listener.ssl.$name.fail_if_no_peer_cert +## +## Value: false | true +## listener.quic.external.fail_if_no_peer_cert = true + +## See: listener.ssl.$name.ciphers +## +## Value: Ciphers +listener.quic.external.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.quic.external.ciphers' and 'listener.quic.external.psk_ciphers' cannot +## be configured at the same time. +## See 'https://tools.ietf.org/html/rfc4279#section-2'. +## listener.quic.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA + +## See: listener.ssl.$name.secure_renegotiate +## +## Value: on | off +## listener.quic.external.secure_renegotiate = off + +## See: listener.ssl.$name.reuse_sessions +## +## Value: on | off +## listener.quic.external.reuse_sessions = on + +## See: listener.ssl.$name.honor_cipher_order +## +## Value: on | off +## listener.quic.external.honor_cipher_order = on + +## See: listener.ssl.$name.peer_cert_as_username +## +## Value: cn | dn | crt | pem | md5 +## listener.quic.external.peer_cert_as_username = cn + +## See: listener.ssl.$name.peer_cert_as_clientid +## +## Value: cn | dn | crt | pem | md5 +## listener.quic.external.peer_cert_as_clientid = cn + +## TCP backlog for the QUIC connection. +## +## See: listener.tcp.$name.backlog +## +## Value: Number >= 0 +listener.quic.external.backlog = 1024 + +## The TCP send timeout for the QUIC connection. +## +## See: listener.tcp.$name.send_timeout +## +## Value: Duration +listener.quic.external.send_timeout = 15s + +## Close the QUIC connection if send timeout. +## +## See: listener.tcp.$name.send_timeout_close +## +## Value: on | off +listener.quic.external.send_timeout_close = on + +## The TCP receive buffer(os kernel) for the QUIC connections. +## +## See: listener.tcp.$name.recbuf +## +## Value: Bytes +## listener.quic.external.recbuf = 4KB + +## The TCP send buffer(os kernel) for the QUIC connections. +## +## See: listener.tcp.$name.sndbuf +## +## Value: Bytes +## listener.quic.external.sndbuf = 4KB + +## The size of the user-level software buffer used by the driver. +## +## See: listener.tcp.$name.buffer +## +## Value: Bytes +## listener.quic.external.buffer = 4KB + +## The TCP_NODELAY flag for QUIC connections. +## +## See: listener.tcp.$name.nodelay +## +## Value: true | false +## listener.quic.external.nodelay = true + +## The compress flag for external QUIC connections. +## +## If this Value is set true,the websocket message would be compressed +## +## Value: true | false +## listener.quic.external.compress = true + +## The level of deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.level +## +## Value: none | default | best_compression | best_speed +## listener.quic.external.deflate_opts.level = default + +## The mem_level of deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.mem_level +## +## Valid range is 1-9 +## listener.quic.external.deflate_opts.mem_level = 8 + +## The strategy of deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.strategy +## +## Value: default | filtered | huffman_only | rle +## listener.quic.external.deflate_opts.strategy = default + +## The deflate option for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.server_context_takeover +## +## Value: takeover | no_takeover +## listener.quic.external.deflate_opts.server_context_takeover = takeover + +## The deflate option for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.client_context_takeover +## +## Value: takeover | no_takeover +## listener.quic.external.deflate_opts.client_context_takeover = takeover + +## The deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.server_max_window_bits +## +## Valid range is 8-15 +## listener.quic.external.deflate_opts.server_max_window_bits = 15 + +## The deflate options for external QUIC connections. +## +## See: listener.quic.$name.deflate_opts.client_max_window_bits +## +## Valid range is 8-15 +## listener.quic.external.deflate_opts.client_max_window_bits = 15 + +## The idle timeout for external QUIC connections. +## +## See: listener.quic.$name.idle_timeout +## +## Value: Duration +## listener.quic.external.idle_timeout = 60s + +## The max frame size for external QUIC connections. +## +## Value: Number +## listener.quic.external.max_frame_size = 0 + +## Whether a WebSocket message is allowed to contain multiple MQTT packets +## +## Value: single | multiple +listener.quic.external.mqtt_piggyback = multiple +## Enable origin check in header for secure websocket connection +## +## Value: true | false (default false) +listener.quic.external.check_origin_enable = false +## Allow origin to be absent in header in secure websocket connection when check_origin_enable is true +## +## Value: true | false (default true) +listener.quic.external.allow_origin_absence = true +## Comma separated list of allowed origin in header for secure websocket connection +## +## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084 +listener.quic.external.check_origins = https://localhost:8084, https://127.0.0.1:8084 + ## CONFIG_SECTION_END=listeners ================================================ ## CONFIG_SECTION_BGN=modules ================================================== diff --git a/apps/emqx/src/emqx_listeners.erl b/apps/emqx/src/emqx_listeners.erl index 1f3d1776b..d97fe32ed 100644 --- a/apps/emqx/src/emqx_listeners.erl +++ b/apps/emqx/src/emqx_listeners.erl @@ -139,7 +139,22 @@ start_listener(Proto, ListenOn, Options) when Proto == http; Proto == ws -> %% Start MQTT/WSS listener start_listener(Proto, ListenOn, Options) when Proto == https; Proto == wss -> start_http_listener(fun cowboy:start_tls/3, 'mqtt:wss', ListenOn, - ranch_opts(Options), ws_opts(Options)). + ranch_opts(Options), ws_opts(Options)); + +%% MQTT over QUIC +start_listener(quic, ListenOn, Options) -> + SSLOpts = proplists:get_value(ssl_options, Options), + ListenOpts = [ {cert, proplists:get_value(certfile, SSLOpts)} + , {key, proplists:get_value(keyfile, SSLOpts)} + , {alpn, ["mqtt"]} + , {conn_acceptors, 32} + ], + ConnectionOpts = [ {conn_callback, quicer_server_conn_callback} + , {idle_timeout_ms, 5000} + , {peer_unidi_stream_count, 1} + , {peer_bidi_stream_count, 10}], + StreamOpts = [{stream_callback, quicer_echo_server_stream_callback}], + quicer:start_listener('mqtt:quic', ListenOn, {ListenOpts, ConnectionOpts, StreamOpts}). replace(Opts, Key, Value) -> [{Key, Value} | proplists:delete(Key, Opts)]. diff --git a/rebar.config b/rebar.config index 109b680c5..eaa0ea6cf 100644 --- a/rebar.config +++ b/rebar.config @@ -54,6 +54,7 @@ , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.5.1"}}} , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.2.1"}}} + , {quicer, {git, "https://github.com/qzhuyan/quic.git", {branch, "quicer_application"}}} ]}. {xref_ignores, diff --git a/rebar.config.erl b/rebar.config.erl index 0248e6dab..a05b09686 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -241,6 +241,7 @@ relx_apps(ReleaseType) -> , compiler , runtime_tools , cuttlefish + , quicer , emqx , {mnesia, load} , {ekka, load}