使用NGINX Plus优化企业环境中的MQTT部署

NGINX | 2023年6月05日

在发布NGINX Plus R29版本时,我们简要介绍了该版本新增的原生解析MQTT消息支持。本文将在此基础上,探讨如何配置NGINX Plus,以优化企业环境中的MQTT部署。

什么是MQTT?

MQTT是消息队列遥测传输协议的缩写。它是一种广受欢迎的轻量级发布-订阅消息传递协议,非常适合通过互联网连接物联网(IoT)或机器对机器(M2M)设备和应用。MQTT旨在低带宽或低功耗环境中高效运行,因此非常适合拥有大量远程客户端的应用。它广泛应用于消费电子、汽车、交通运输、制造和医疗保健等多个行业。

NGINX Plus MQTT消息处理

NGINX Plus R29支持MQTT 3.1.1和MQTT 5.0。它充当客户端与代理服务器之间的代理,将任务从核心系统卸载,从而简化扩展并降低计算成本。具体而言,NGINX Plus可解析和重写MQTT CONNECT消息的部分内容,从而支持以下功能:

  • MQTT代理服务器负载均衡
  • 会话持久性(将重新连接的客户端连接到同一代理)
  • SSL/TLS终止
  • 客户端证书身份验证

MQTT消息处理指令必须在NGINX配置文件的stream上下文中定义,并由ngx_stream_mqtt_preread_module
和ngx_stream_mqtt_filter_module提供。

The preread模块会在NGINX内部代理之前处理MQTT数据,以便根据解析后的消息数据做出负载均衡和上游路由决策。

The filter模块支持重写收到的CONNECT消息中的clientid、username和password字段。将这些字段设置为变量和复杂值的功能可显著扩展配置选项,使NGINX Plus能够屏蔽敏感设备信息,或插入TLS证书可分辨名称等数据。

MQTT指令和变量

现已提供多项新指令和嵌入式变量,可用于调整NGINX配置,以优化MQTT部署并满足您的特定需求。

Preread 模块指令和嵌入式变量

  • mqtt_preread,启用MQTT解析,从客户端设备发送的CONNECT消息中提取clientid和username字段。这些值可通过嵌入式变量使用,并有助于对会话进行哈希处理,以将其分配到负载均衡的上游服务器(示例如下)。
  • $mqtt_preread_clientid,表示设备发送的MQTT客户端标识符。
  • $mqtt_preread_username,表示客户端为进行身份验证而发送的用户名。

过滤器模块指令

  • mqtt,定义是否启用MQTT重写。
  • mqtt_buffers:覆盖每个连接可分配的MQTT处理缓冲区数量上限及每个缓冲区的大小。默认情况下,NGINX将每个连接的缓冲区数量限制为100个,每个缓冲区的大小为1 KB。通常,这种设置有利于优化性能,但在特殊情况下可能需要调整。例如,较长的MQTT消息需要更大的缓冲区。对于在短时间内通过给定连接处理大量MQTT消息的系统,增加缓冲区数量可能有助于提升性能。大多数情况下,调整缓冲区参数对底层系统性能影响不大,因为NGINX使用内部内存池构建缓冲区。
  • mqtt_rewrite_buffer_size,指定用于构建MQTT消息的缓冲区大小。此指令已弃用,并自NGINX Plus R30起不再使用。
  • mqtt_set_connect,重写客户端发送的CONNECT消息中的参数。支持的参数包括:clientid、username和password。

MQTT示例

下面详细介绍使用 NGINX Plus 处理 MQTT 消息的优势及相关最佳实践。请注意,以下示例使用端口 1883 和 8883。端口 1883 是默认的非安全 MQTT 端口,而端口 8883 是默认的 SSL/TLS 加密端口。

MQTT代理服务器负载均衡

MQTT设备的临时性可能导致客户端IP意外变化,从而难以将设备连接路由到正确的上游代理。随后,设备连接从一个上游代理迁移到另一个上游代理,可能引发代理之间成本高昂的同步操作,增加延迟和成本。

通过解析MQTT CONNECT消息中的clientid字段,NGINX可以与上游服务代理建立粘性会话。具体方法是将clientid用作哈希键,以维持与后端代理服务的连接。

在此示例中,我们代理MQTT设备数据,并使用clientid作为令牌,与3个上游代理建立粘性会话。我们使用consistent参数,以便在某台上游服务器发生故障时,将其承担的流量均匀分配到其余服务器,同时不影响这些服务器上已建立的会话。

stream {      mqtt_preread on; 
     
      upstream backend {
          zone tcp_mem 64k;
          hash $mqtt_preread_clientid consistent;
    
          server 10.0.0.7:1883; # upstream mqtt broker 1
          server 10.0.0.8:1883; # upstream mqtt broker 2
          server 10.0.0.9:1883; # upstream mqtt broker 3 
      }
    
      server {
          listen 1883;
          proxy_pass backend;
          proxy_connect_timeout 1s;
      }
  }

NGINX Plus还可以解析MQTT CONNECT消息中的用户名字段。有关详细信息,请参阅ngx_stream_mqtt_preread_module规范。

SSL/TLS终止

对设备通信进行加密,是确保数据机密性和防范中间人攻击的关键。然而,TLS握手、加密和解密可能会给MQTT代理带来资源负担。为解决这一问题,NGINX Plus可以从代理(或代理集群)卸载数据加密任务,从而简化安全规则,让代理专注于处理设备消息。

本示例展示如何使用NGINX将来自设备且经过TLS加密的MQTT流量代理到后端代理服务器。ssl_session_cache指令定义了一个5 MB的缓存,足以存储约20,000个SSL会话。根据proxy_connect_timeout指令的定义,NGINX会尝试连接被代理的代理服务器,并在5秒后超时。

stream {      server {
          listen 8883 ssl;
          ssl_certificate /etc/nginx/certs/tls-cert.crt;
          ssl_certificate_key /etc/nginx/certs/tls-key.key;
          ssl_session_cache shared:SSL:5m;
          proxy_pass 10.0.0.8:1883;
          proxy_connect_timeout 5s;
      }
  } 

客户端ID替换

出于安全考虑,可以选择不在MQTT代理的数据库中存储可识别客户端身份的信息。例如,设备可能会在MQTT CONNECT消息中发送序列号或其他敏感数据。通过使用从客户端接收的其他已知静态值替换设备标识符,可以为每台尝试访问由NGINX Plus代理的MQTT代理的设备创建一个替代唯一键。

在此示例中,我们从设备的客户端SSL证书中提取唯一标识符,并用它来掩盖设备的MQTT客户端ID。客户端证书身份验证(双向TLS)由ssl_verify_client指令控制。当该指令设置为on参数时,NGINX会确保客户端证书由受信任的证书颁发机构(CA)签名。受信任的CA证书列表由ssl_client_certificate指令定义。

stream {      mqtt on; 
    
      server {
          listen 8883 ssl;
          ssl_certificate /etc/nginx/certs/tls-cert.crt;
          ssl_certificate_key /etc/nginx/certs/tls-key.key;
          ssl_client_certificate /etc/nginx/certs/client-ca.crt;
          ssl_session_cache shared:SSL:10m;
          ssl_verify_client on;
          proxy_pass 10.0.0.8:1883;
          proxy_connect_timeout 1s;
          
          mqtt_set_connect clientid $ssl_client_serial;
      }
  }

将客户端证书用作身份验证凭据

对MQTT客户端进行身份验证的一种常见方法是,将客户端证书中存储的数据用作用户名。NGINX Plus可以解析客户端证书并重写MQTT用户名字段,从而让后端代理无需执行此任务。在以下示例中,我们提取客户端证书的使用者可分辨名称(Subject DN),并将其复制到MQTT CONNECT消息的用户名部分。

stream {      mqtt on; 
     
      server {
          listen 8883 ssl;
          ssl_certificate /etc/nginx/certs/tls-cert.crt;
          ssl_certificate_key /etc/nginx/certs/tls-key.key;
          ssl_client_certificate /etc/nginx/certs/client-ca.crt;
          ssl_session_cache shared:SSL:10m;
          ssl_verify_client on;
          proxy_pass 10.0.0.8:1883;
          proxy_connect_timeout 1s;
          
          mqtt_set_connect username $ssl_client_s_dn;
      }
  } 

有关NGINX Plus MQTT CONNECT消息重写的完整规范,请参阅ngx_stream_mqtt_filter_module规范。

立即开始

NGINX Plus未来对MQTT的改进可能包括解析其他MQTT消息类型,以及对CONNECT消息进行更深入的解析,从而支持以下功能:

  • 其他身份验证和访问控制机制
  • 通过对“频繁通信”的客户端实施速率限制来保护代理
  • 消息遥测和连接指标

如果您是首次使用NGINX Plus,请注册30天免费试用,开始使用MQTT。我们也非常期待您就最关注的功能提供反馈。欢迎在评论区分享您的看法。


分享
标签: F5 NGINX, Tech

相关博客文章

NGINX Plus R26 正式发布
NGINX | 2022/02/15

NGINX Plus R26 正式发布

NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
NGINX | 2022/02/10

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击

gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
NGINX | 2021/09/13

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划

了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。

NGINX Plus R19 正式发布
NGINX | 2019/09/03

NGINX Plus R19 正式发布

NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。

NGINX现已正式加入F5
NGINX | 2019/05/09

NGINX现已正式加入F5

NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。

使用NGINX和NGINX Plus实现高性能缓存
NGINX | 2016/08/24

使用NGINX和NGINX Plus实现高性能缓存

我将介绍NGINX在内容缓存和性能方面的工作原理,并分享一些控制NGINX内容缓存方式的技巧。