MQTT Rate Limits Draft
This commit is contained in:
		
							parent
							
								
									db6c1075eb
								
							
						
					
					
						commit
						d8097d2b76
					
				@ -619,6 +619,13 @@ transport:
 | 
			
		||||
  log:
 | 
			
		||||
    enabled: "${TB_TRANSPORT_LOG_ENABLED:true}"
 | 
			
		||||
    max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}"
 | 
			
		||||
  rate_limits:
 | 
			
		||||
    # Maximum number of simultaneous connections from a single ip address
 | 
			
		||||
    max_connections_per_ip: "${TB_TRANSPORT_MAX_CONNECTIONS_PER_IP:50}"
 | 
			
		||||
    # Maximum number of connect attempts with invalid credentials
 | 
			
		||||
    max_wrong_credentials_per_ip: "${TB_TRANSPORT_MAX_WRONG_CREDENTIALS_PER_IP:10}"
 | 
			
		||||
    # Timeout to expire block IP addresses
 | 
			
		||||
    ip_block_timeout: "${TB_TRANSPORT_IP_BLOCK_TIMEOUT:10000}"
 | 
			
		||||
  # Local HTTP transport parameters
 | 
			
		||||
  http:
 | 
			
		||||
    enabled: "${HTTP_ENABLED:true}"
 | 
			
		||||
@ -630,6 +637,9 @@ transport:
 | 
			
		||||
    enabled: "${MQTT_ENABLED:true}"
 | 
			
		||||
    bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
 | 
			
		||||
    bind_port: "${MQTT_BIND_PORT:1883}"
 | 
			
		||||
    # Enable proxy protocol support. Disabled by default. If enabled, supports both v1 and v2.
 | 
			
		||||
    # Useful to get the real IP address of the client in the logs and for rate limits.
 | 
			
		||||
    proxy_enabled: "${MQTT_PROXY_PROTOCOL_ENABLED:false}"
 | 
			
		||||
    timeout: "${MQTT_TIMEOUT:10000}"
 | 
			
		||||
    msg_queue_size_per_device_limit: "${MQTT_MSG_QUEUE_SIZE_PER_DEVICE_LIMIT:100}" # messages await in the queue before device connected state. This limit works on low level before TenantProfileLimits mechanism
 | 
			
		||||
    netty:
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.PreDestroy;
 | 
			
		||||
import java.net.InetSocketAddress;
 | 
			
		||||
import java.util.concurrent.ExecutorService;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
 | 
			
		||||
@ -73,6 +74,10 @@ public class MqttTransportContext extends TransportContext {
 | 
			
		||||
    @Value("${transport.mqtt.timeout:10000}")
 | 
			
		||||
    private long timeout;
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Value("${transport.mqtt.proxy_enabled:false}")
 | 
			
		||||
    private boolean proxyEnabled;
 | 
			
		||||
 | 
			
		||||
    private final AtomicInteger connectionsCounter = new AtomicInteger();
 | 
			
		||||
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
@ -88,4 +93,13 @@ public class MqttTransportContext extends TransportContext {
 | 
			
		||||
    public void channelUnregistered() {
 | 
			
		||||
        connectionsCounter.decrementAndGet();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean checkAddress(InetSocketAddress address){
 | 
			
		||||
        return rateLimitService.checkAddress(address);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onAuthFailed(InetSocketAddress address){
 | 
			
		||||
        rateLimitService.onAuthFailed(address);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ import com.google.gson.JsonParseException;
 | 
			
		||||
import io.netty.channel.ChannelFuture;
 | 
			
		||||
import io.netty.channel.ChannelHandlerContext;
 | 
			
		||||
import io.netty.channel.ChannelInboundHandlerAdapter;
 | 
			
		||||
import io.netty.handler.codec.haproxy.HAProxyMessage;
 | 
			
		||||
import io.netty.handler.codec.mqtt.MqttConnAckMessage;
 | 
			
		||||
import io.netty.handler.codec.mqtt.MqttConnAckVariableHeader;
 | 
			
		||||
import io.netty.handler.codec.mqtt.MqttConnectMessage;
 | 
			
		||||
@ -164,6 +165,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
 | 
			
		||||
    @Override
 | 
			
		||||
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
 | 
			
		||||
        log.trace("[{}] Processing msg: {}", sessionId, msg);
 | 
			
		||||
        if (address == null) {
 | 
			
		||||
            address = getAddress(ctx);
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            if (msg instanceof MqttMessage) {
 | 
			
		||||
                MqttMessage message = (MqttMessage) msg;
 | 
			
		||||
@ -182,8 +186,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    InetSocketAddress getAddress(ChannelHandlerContext ctx) {
 | 
			
		||||
        return ctx.channel().attr(MqttTransportService.ADDRESS).get();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void processMqttMsg(ChannelHandlerContext ctx, MqttMessage msg) {
 | 
			
		||||
        address = getAddress(ctx);
 | 
			
		||||
        if (msg.fixedHeader() == null) {
 | 
			
		||||
            log.info("[{}:{}] Invalid message received", address.getHostName(), address.getPort());
 | 
			
		||||
            ctx.close();
 | 
			
		||||
@ -199,10 +206,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    InetSocketAddress getAddress(ChannelHandlerContext ctx) {
 | 
			
		||||
        return (InetSocketAddress) ctx.channel().remoteAddress();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void processProvisionSessionMsg(ChannelHandlerContext ctx, MqttMessage msg) {
 | 
			
		||||
        switch (msg.fixedHeader().messageType()) {
 | 
			
		||||
            case PUBLISH:
 | 
			
		||||
@ -771,7 +774,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
 | 
			
		||||
 | 
			
		||||
    private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage connectMessage) {
 | 
			
		||||
        String userName = connectMessage.payload().userName();
 | 
			
		||||
        log.debug("[{}] Processing connect msg for client with user name: {}!", sessionId, userName);
 | 
			
		||||
        log.debug("[{}][{}] Processing connect msg for client with user name: {}!", address, sessionId, userName);
 | 
			
		||||
        TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder()
 | 
			
		||||
                .setClientId(connectMessage.payload().clientIdentifier());
 | 
			
		||||
        if (userName != null) {
 | 
			
		||||
@ -820,6 +823,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            context.onAuthFailed(address);
 | 
			
		||||
            ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED, connectMessage));
 | 
			
		||||
            log.trace("[{}] X509 auth failure: {}", sessionId, address, e);
 | 
			
		||||
            ctx.close();
 | 
			
		||||
@ -931,6 +935,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
 | 
			
		||||
 | 
			
		||||
    private void onValidateDeviceResponse(ValidateDeviceCredentialsResponse msg, ChannelHandlerContext ctx, MqttConnectMessage connectMessage) {
 | 
			
		||||
        if (!msg.hasDeviceInfo()) {
 | 
			
		||||
            context.onAuthFailed(address);
 | 
			
		||||
            ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED, connectMessage));
 | 
			
		||||
            ctx.close();
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
@ -18,9 +18,12 @@ package org.thingsboard.server.transport.mqtt;
 | 
			
		||||
import io.netty.channel.ChannelInitializer;
 | 
			
		||||
import io.netty.channel.ChannelPipeline;
 | 
			
		||||
import io.netty.channel.socket.SocketChannel;
 | 
			
		||||
import io.netty.handler.codec.haproxy.HAProxyMessageDecoder;
 | 
			
		||||
import io.netty.handler.codec.mqtt.MqttDecoder;
 | 
			
		||||
import io.netty.handler.codec.mqtt.MqttEncoder;
 | 
			
		||||
import io.netty.handler.ssl.SslHandler;
 | 
			
		||||
import org.thingsboard.server.transport.mqtt.limits.IpFilter;
 | 
			
		||||
import org.thingsboard.server.transport.mqtt.limits.ProxyIpFilter;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Andrew Shvayka
 | 
			
		||||
@ -39,6 +42,12 @@ public class MqttTransportServerInitializer extends ChannelInitializer<SocketCha
 | 
			
		||||
    public void initChannel(SocketChannel ch) {
 | 
			
		||||
        ChannelPipeline pipeline = ch.pipeline();
 | 
			
		||||
        SslHandler sslHandler = null;
 | 
			
		||||
        if (context.isProxyEnabled()) {
 | 
			
		||||
            pipeline.addLast("proxy", new HAProxyMessageDecoder());
 | 
			
		||||
            pipeline.addLast("ipFilter", new ProxyIpFilter(context));
 | 
			
		||||
        } else {
 | 
			
		||||
            pipeline.addLast("ipFilter", new IpFilter(context));
 | 
			
		||||
        }
 | 
			
		||||
        if (sslEnabled && context.getSslHandlerProvider() != null) {
 | 
			
		||||
            sslHandler = context.getSslHandlerProvider().getSslHandler();
 | 
			
		||||
            pipeline.addLast(sslHandler);
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,7 @@ import io.netty.channel.ChannelOption;
 | 
			
		||||
import io.netty.channel.EventLoopGroup;
 | 
			
		||||
import io.netty.channel.nio.NioEventLoopGroup;
 | 
			
		||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
 | 
			
		||||
import io.netty.util.AttributeKey;
 | 
			
		||||
import io.netty.util.ResourceLeakDetector;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
@ -33,6 +34,8 @@ import org.thingsboard.server.common.data.TbTransportService;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.annotation.PreDestroy;
 | 
			
		||||
import java.net.InetAddress;
 | 
			
		||||
import java.net.InetSocketAddress;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Andrew Shvayka
 | 
			
		||||
@ -42,6 +45,8 @@ import javax.annotation.PreDestroy;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class MqttTransportService implements TbTransportService {
 | 
			
		||||
 | 
			
		||||
    public static AttributeKey<InetSocketAddress> ADDRESS = AttributeKey.newInstance("SRC_ADDRESS");
 | 
			
		||||
 | 
			
		||||
    @Value("${transport.mqtt.bind_address}")
 | 
			
		||||
    private String host;
 | 
			
		||||
    @Value("${transport.mqtt.bind_port}")
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,46 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2021 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.transport.mqtt.limits;
 | 
			
		||||
 | 
			
		||||
import io.netty.channel.ChannelHandlerContext;
 | 
			
		||||
import io.netty.channel.ChannelInboundHandlerAdapter;
 | 
			
		||||
import io.netty.handler.codec.haproxy.HAProxyMessage;
 | 
			
		||||
import io.netty.handler.ipfilter.AbstractRemoteAddressFilter;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.server.transport.mqtt.MqttTransportContext;
 | 
			
		||||
import org.thingsboard.server.transport.mqtt.MqttTransportService;
 | 
			
		||||
 | 
			
		||||
import java.net.InetSocketAddress;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class IpFilter extends AbstractRemoteAddressFilter<InetSocketAddress> {
 | 
			
		||||
 | 
			
		||||
    private MqttTransportContext context;
 | 
			
		||||
 | 
			
		||||
    public IpFilter(MqttTransportContext context) {
 | 
			
		||||
        this.context = context;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected boolean accept(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) throws Exception {
 | 
			
		||||
        if(context.checkAddress(remoteAddress)){
 | 
			
		||||
            ctx.channel().attr(MqttTransportService.ADDRESS).set(remoteAddress);
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,60 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2021 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.transport.mqtt.limits;
 | 
			
		||||
 | 
			
		||||
import io.netty.channel.ChannelHandlerContext;
 | 
			
		||||
import io.netty.channel.ChannelInboundHandlerAdapter;
 | 
			
		||||
import io.netty.handler.codec.haproxy.HAProxyMessage;
 | 
			
		||||
import io.netty.util.AttributeKey;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.server.transport.mqtt.MqttTransportContext;
 | 
			
		||||
import org.thingsboard.server.transport.mqtt.MqttTransportService;
 | 
			
		||||
 | 
			
		||||
import java.net.InetAddress;
 | 
			
		||||
import java.net.InetSocketAddress;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class ProxyIpFilter extends ChannelInboundHandlerAdapter {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private MqttTransportContext context;
 | 
			
		||||
 | 
			
		||||
    public ProxyIpFilter(MqttTransportContext context) {
 | 
			
		||||
        this.context = context;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
 | 
			
		||||
        if(msg instanceof HAProxyMessage){
 | 
			
		||||
            HAProxyMessage proxyMsg = (HAProxyMessage) msg;
 | 
			
		||||
            if(proxyMsg.sourceAddress() != null && proxyMsg.sourcePort() > 0) {
 | 
			
		||||
                InetSocketAddress address = new InetSocketAddress(proxyMsg.sourceAddress(), proxyMsg.sourcePort());
 | 
			
		||||
                if(!context.checkAddress(address)){
 | 
			
		||||
                    ctx.close();
 | 
			
		||||
                } else {
 | 
			
		||||
                    ctx.channel().attr(MqttTransportService.ADDRESS).set(address);
 | 
			
		||||
                    // We no longer need this channel in the pipeline. Similar to HAProxyMessageDecoder
 | 
			
		||||
                    ctx.pipeline().remove(this);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                log.debug("Received local health-check connection message: {}", proxyMsg);
 | 
			
		||||
                ctx.close();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            super.channelRead(ctx, msg);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.thingsboard.common.util.ThingsBoardExecutors;
 | 
			
		||||
import org.thingsboard.server.cache.ota.OtaPackageDataCache;
 | 
			
		||||
import org.thingsboard.server.common.transport.limits.TransportRateLimitService;
 | 
			
		||||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
 | 
			
		||||
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
 | 
			
		||||
 | 
			
		||||
@ -57,6 +58,9 @@ public abstract class TransportContext {
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private TransportResourceCache transportResourceCache;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    protected TransportRateLimitService rateLimitService;
 | 
			
		||||
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void init() {
 | 
			
		||||
        executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass());
 | 
			
		||||
@ -73,4 +77,6 @@ public abstract class TransportContext {
 | 
			
		||||
        return serviceInfoProvider.getServiceId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,8 @@ import org.thingsboard.server.common.transport.TransportTenantProfileCache;
 | 
			
		||||
import org.thingsboard.server.common.transport.profile.TenantProfileUpdateResult;
 | 
			
		||||
import org.thingsboard.server.queue.util.TbTransportComponent;
 | 
			
		||||
 | 
			
		||||
import java.net.InetAddress;
 | 
			
		||||
import java.net.InetSocketAddress;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
@ -116,6 +118,18 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi
 | 
			
		||||
        tenantAllowed.put(tenantId, allowed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Set<InetAddress> blockedAddresses = new HashSet<>();
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean checkAddress(InetSocketAddress address) {
 | 
			
		||||
        return !blockedAddresses.contains(address.getAddress());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onAuthFailed(InetSocketAddress address) {
 | 
			
		||||
        blockedAddresses.add(address.getAddress());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private <T extends EntityId> void mergeLimits(T entityId, EntityTransportRateLimits newRateLimits,
 | 
			
		||||
                                                  Function<T, EntityTransportRateLimits> getFunction,
 | 
			
		||||
                                                  BiConsumer<T, EntityTransportRateLimits> putFunction) {
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,8 @@ import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.transport.profile.TenantProfileUpdateResult;
 | 
			
		||||
 | 
			
		||||
import java.net.InetSocketAddress;
 | 
			
		||||
 | 
			
		||||
public interface TransportRateLimitService {
 | 
			
		||||
 | 
			
		||||
    EntityType checkLimits(TenantId tenantId, DeviceId deviceId, int dataPoints);
 | 
			
		||||
@ -33,4 +35,8 @@ public interface TransportRateLimitService {
 | 
			
		||||
    void remove(DeviceId deviceId);
 | 
			
		||||
 | 
			
		||||
    void update(TenantId tenantId, boolean transportEnabled);
 | 
			
		||||
 | 
			
		||||
    boolean checkAddress(InetSocketAddress address);
 | 
			
		||||
 | 
			
		||||
    void onAuthFailed(InetSocketAddress address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -88,6 +88,9 @@ transport:
 | 
			
		||||
  mqtt:
 | 
			
		||||
    bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
 | 
			
		||||
    bind_port: "${MQTT_BIND_PORT:1883}"
 | 
			
		||||
    # Enable proxy protocol support. Disabled by default. If enabled, supports both v1 and v2.
 | 
			
		||||
    # Useful to get the real IP address of the client in the logs and for rate limits.
 | 
			
		||||
    proxy_enabled: "${MQTT_PROXY_PROTOCOL_ENABLED:false}"
 | 
			
		||||
    timeout: "${MQTT_TIMEOUT:10000}"
 | 
			
		||||
    msg_queue_size_per_device_limit: "${MQTT_MSG_QUEUE_SIZE_PER_DEVICE_LIMIT:100}" # messages await in the queue before device connected state. This limit works on low level before TenantProfileLimits mechanism
 | 
			
		||||
    netty:
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user