MQTT基本信息

M2M大规模沟通需要不同的模式:之前的请求\/应答(Request\/Response)模式不再适合,取而代之的是发布\/订阅(Publish\/Subscribe)模式。

  • MQTT是基于二进制消息的发布\/订阅编程模式的消息协议,最早由IBM提出的,如今已经成为OASIS规范。与HTTP、CoAP、XMPP等协议相比,MQTT协议有以下的优势:

    • MQTT基于TCP,在反控设备的时候比CoAP等基于UDP的协议更可靠,比如使用3G通讯的时候需要专门实现CoAP over TCP,否则反控很不稳定。
    • MQTT异步Pub\/Sub实现,好比发个短信,无需等待对方确认便可以继续,而不像HTTP、CoAP那样必须等待对方应答才能返回的同步模式。
    • MQTT为物联网提供了许多体贴的设计,比如QoS,比如“遗言”等设计。
    • MQTT是二进制格式,比XMPP更轻量级。
  • MQTT非常适合需要低功耗和网络带宽有限的IoT场景:

    • 遥感数据
    • 汽车
    • 智能家居
    • 智慧城市
    • 医疗医护
  • 由于物联网的环境是非常特别的,所以MQTT遵循以下设计原则:

    1. 精简,不添加可有可无的功能。
    2. 发布\/订阅(Pub\/Sub)模式,方便消息在传感器之间传递。

    3. 允许用户动态创建主题,零运维成本。

    4. 把传输量降到最低以提高传输效率。

    5. 把低带宽、高延迟、不稳定的网络等因素考虑在内。

    6. 支持连续的会话控制。

    7. 理解客户端计算能力可能很低。

    8. 提供服务质量管理。

    9. 假设数据不可知,不强求传输数据的类型与格式,保持灵活性。

  • 业务场景如下:

    主题

    MQTT是通过主题对消息进行分类的,本质上就是一个UTF-8的字符串,不过可以通过反斜杠表示多个层级关系。主题并不需要创建,直接使用就是了。

    主题还可以通过通配符进行过滤。其中,+可以过滤一个层级,而#只能出现在主题最后表示过滤任意级别的层级。

    举个例子:

    • building-b\/floor-5:代表B楼5层的设备。

    • +\/floor-5:代表任何一个楼的5层的设备。

    • building-b\/#:代表B楼所有的设备。

注意,MQTT允许使用通配符订阅主题,但是并不允许使用通配符广播。

服务质量

为了满足不同的场景,MQTT支持三种不同级别的服务质量(Quality of Service,QoS)为不同场景提供消息可靠性:

  • 级别0:尽力而为。消息发送者会想尽办法发送消息,但是遇到意外并不会重试。
  • 级别1:至少一次。消息接收者如果没有知会或者知会本身丢失,消息发送者会再次发送以保证消息接收者至少会收到一次,当然可能造成重复消息。
  • 级别2:恰好一次。保证这种语义肯定会减少并发或者增加延时,不过丢失或者重复消息是不可接受的时候,级别2是最合适的。

服务质量是个老话题了。级别2所提供的不重不丢很多情况下是最理想的,不过往返多次的确认一定对并发和延迟带来影响。级别1提供的至少一次语义在日志处理这种场景下是完全OK的,所以像Kafka这类的系统利用这一特点减少确认从而大大提高了并发。级别0适合鸡肋数据场景,食之无味弃之可惜,就这么着吧。

消息类型

MQTT拥有14种不同的消息类型:

  1. CONNECT:客户端连接到MQTT代理
  2. CONNACK:连接确认
  3. PUBLISH:新发布消息
  4. PUBACK:新发布消息确认,是QoS 1给PUBLISH消息的回复
  5. PUBREC:QoS 2消息流的第一部分,表示消息发布已记录
  6. PUBREL:QoS 2消息流的第二部分,表示消息发布已释放
  7. PUBCOMP:QoS 2消息流的第三部分,表示消息发布完成
  8. SUBSCRIBE:客户端订阅某个主题
  9. SUBACK:对于SUBSCRIBE消息的确认
  10. UNSUBSCRIBE:客户端终止订阅的消息
  11. UNSUBACK:对于UNSUBSCRIBE消息的确认
  12. PINGREQ:心跳
  13. PINGRESP:确认心跳
  14. DISCONNECT:客户端终止连接前优雅地通知MQTT代理

MQTT代理

apt-get install mosquitto
apt-get install mosquitto-clients
##new version
apt-add-repository ppa:mosquitto-dev/mosquitto-ppa
apt-get update
apt-get install mosquitto
apt-get install mosquitto-clients

配置Mosquitto 的websocket协议访问

支持websocket协议的mosquitto版本要大于1.4.5

打开websocket协议的方法: 只需要在\/etc\/mosquitto\/mosquitto.conf中添加:

listener 8001
protocol websockets

MQTT安全相关

安全对于几乎所有的项目都是一个挑战,对于物联网项目更是如此:

  • 设备安全性与设备可用性之间往往是零和博弈。
  • 加密算法需要更多的计算能力,而物联网设备的性能往往非常有限。
  • 物联网的网络条件常常要比家庭或者办公室的网络条件差许多。

对于以上挑战,MQTT提供了多个层次的安全特性:

  1. 网络层:有条件可以通过拉专线或者使用VPN来连接设备与MQTT代理,以提高网络传输的安全性。
  2. 传输层:传输层使用TLS加密是确保安全的一个好手段,可以防止中间人攻击(Man-In-The-Middle Attack)。客户端证书不但可以作为设备的身份凭证,还可以用来验证设备。
  3. 应用层:MQTT还提供客户标识(Client Identifier)以及用户名密码,在应用层验证设备。

虽然MQTT提供了多重安全设计,不过世界上并没有银弹能够保障数据的绝对安全,所以应该在设计的时候就把安全放在设计目标之中并拥有相当的优先级,否则上文提到的智能洗衣机就是一个活生生的教训。

网络层可以使用专线或者VPN超出了本文的范围,下面我们结合Mosquitto仔细了解一下传输层和应用层的MQTT安全特性。

加密

MQTT是基于TCP的,默认情况通讯并不加密。如果你需要传输敏感信息或者对设备进行反控,使用TLS几乎是必须的。

TLS是非常成熟的安全协议,在握手的时候便可以创建安全连接,使得黑客无法窃听或者篡改内容了。使用TLS的时候有以下注意点:

  • 尽可能使用高版本的TLS。
  • 验证X509证书链防止中间人攻击。
  • 尽量使用有CA发布的证书。

当然,TLS会增加连接时开销,对低运算能力的设备而言是额外的负担,不过如果设备是长连接的话就会避免反复连接的开销。

Mosquitto原生支持了TLS加密,生成证书后再配置一下MQTT代理即可。

首先我们需要生成证书权威(Certificate Authority,CA)的认证和密钥,生成过程中Common Name一定要填写Fully Qualified Domain Name(测试起见用IP地址也凑合):

openssl req -new -x509 -days 365 -extensions v3_ca -keyout ca.key -out ca.crt

接下来生成MQTT代理使用的密钥:

openssl genrsa -des3 -out server.key 2048

并去除密码:

openssl genrsa -out server.key 2048

然后为MQTT代理准备一个认证注册请求(Certificate Signing Request,CSR),这里的Common Name也要写对:

openssl req -out server.csr -key server.key -new

最后通过CA签署这个CSR生成MQTT代理证书:

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365

现在配置\/etc\/mosquitto\/mosquitto.conf,确保8883端口的设置如下:

listener 8883
cafile /etc/mosquitto/tls/ca.crt 
certfile /etc/mosquitto/tls/server.crt 
keyfile /etc/mosquitto/tls/server.key

重启Mosquitto服务就可以用以下命令订阅和发布消息了,当然所有消息都由TLS加密,可以无忧无虑地传递私密信息:

mosquitto_sub -h host -p 8883 -t 'topic' --cafile ca.crt
mosquitto_pub -h host -p 8883 -t 'topic' -m '15' --cafile ca.crt

其中,host需要与前面指定的Common Name一致,否则TLS连接会报错。

认证

MQTT支持两种层次的认证:

传输层:传输层使用TLS不但可以加密通讯,还可以使用X509证书来认证设备。 应用层:MQTT支持客户标识、用户名密码以及X509证书,在应用层验证设备。 通过传输层和应用层来解释认证并不直观,下面我们直接从客户标识、用户名密码以及X509证书的角度来了解认证。

客户标识

用户可以使用最多65535个字符作为客户标识(Client Identifier),UUID或者MAC地址最为常见。

使用客户标识来认证并不可靠,不过在某些封闭的环境中或许已经足够。

用户名\/密码

MQTT协议支持通过CONNECT消息的username和password字段发送用户名和密码。

用户名密码的认证使用起来非常方便,不过再强调一下,由于用户名密码是以明文形式传输,在通过互联网时使用TSL加密是必须的。

Mosquitto支持用户名\/密码认证方式,只要确保\/etc\/mosquitto\/mosquitto.conf有如下设置:

password_file /etc/mosquitto/passwd
allow_anonymous false

X509证书

MQTT代理在TLS握手成功之后可以继续发送客户端的X509证书来认证设备,如果设备不合法便可以中断连接。

使用X509认证的好处,是在传输层就可以验证设备的合法性,在发送MQTT CONNECT之前便可以阻隔非法设备的连接,以节省后续不必要的资源浪费。

如果你可以控制设备的创建和设置,X509证书认证或许是个非常好的选择。不过代价也是有的:

需要设计证书创建流程。如果你对设备有着完全的控制,在设备出厂前就能烧录X509证书到设备中,那么这条路是非常合适的。但是,对于移动设备等无法实现烧录证书的场景,用户名\/密码认证或许是更好的选择。 需要管理证书的生命周期,最好通过PKI(Public-Key-Infrastructure)来管理。 如果证书泄露了,一定要立即使证书失效。一个选择是使用证书黑名单(Certificate Revocation Lists),另一个选择是提供在线证书状态协议(Online Certificate Status Protocol),方便MQTT代理及时了解证书的状态。 MQTT原生支持X509认证,生成客户证书后再配置一下MQTT代理便可。

首先生成设备密钥:

openssl genrsa -des3 -out client.key 2048

然后为准备一个设备认证注册请求:

openssl req -out client.csr -key client.key -new

最后通过CA签署这个CSR生成设备证书:

openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365

现在配置\/etc\/mosquitto\/mosquitto.conf,确保8883端口的设置如下:

listener 8883

cafile /etc/mosquitto/tls/ca.crt

certfile /etc/mosquitto/tls/server.crt

keyfile /etc/mosquitto/tls/server.key

require_certificate true

重启Mosquitto服务就可以用以下命令订阅和发布消息了,当然所有消息都由TLS加密,可以无忧无虑地传递私密信息啦:

mosquitto_sub -h host -p 8883 -t 'topic' --cafile ca.crt --cert client.crt --key client.key

mosquitto_pub -h host -p 8883 -t 'topic' -m '95' --cafile ca.crt --cert client.crt --key client.key

可以看到,X509同时提供了完善的加密和验证,只是证书的生命周期管理的代价要比用户名密码高一些。

授权

对MQTT而言意味着对主题的订阅和发布权限。Mosquitto内置了基本的授权,那就是基于Access Control List的授权。

由于ACL是基于特定用户的,所以需要使用用户名密码认证方式。然后,在\/etc\/mosquitto\/mosquitto.conf中指定ACL文件:

acl_file /etc/mosquitto/acl

在这个ACL文件便可以指定用户的读写权限,比如下面便可以授权用户tom读写指定主题的权限:

user tom

topic readwrite company/building/floor/#

Mosquitto只提供了基本的基于ACL的授权,更高级的基于RBAC的授权可能需要通过插件的形式自行开发了。

体系

在MQTT项目实施时,还可以考虑通过防火墙保护MQTT代理:

  • 仅允许相关的流量传递到MQTT代理,比如UDP、ICMP等流量可以直接屏蔽掉。
  • 仅允许相关端口的流量传递到MQTT代理,比如MQTT over TCP使用1883,而MQTT over TLS使用8883。
  • 仅允许某些IP地址段来访问MQTT代理,如果业务场景允许的话。

云平台托管的物联网服务

使用物联网服务提供如下好处:

  1. 从设备到云端以及从云端到设备可靠地进行大规模消息传输。
  2. 对设备认证与权限管理,并保证数据安全传输。
  3. 支持多种语言开发,兼容主流硬件设备。
  4. 与大数据服务无缝对接,以数据分析驱动业务进步。

MQTT协议中规定的其它安全举措:Securing MQTT

results matching ""

    No results matching ""