EMQX物联网MQTT消息服务器集群搭建

莱昂纳多迪卡普利奥 2024-08-14 12:37:02 阅读 82

EMQX官方介绍

1 环境准备

两台Ubuntu 20.04 作为EMQX节点

一台Ubuntu 20.04安装MYSQL数据库

2 搭建EMQX集群

2.1 下载EMQX开源版安装包

1访问https://www.emqx.com/zh/try?product=broker,选择对应的EMQX版本

2 点击免费下载,选择安装方式和CPU架构

3 下载EMQX安装包并解压

执行下面的命令下载EMQX安装包

<code>​

wget https://www.emqx.com/zh/downloads/broker/4.4.5/emqx-4.4.5-otp24.1.5-3-ubuntu20.04-amd64.zip

解压安装包

2.2 修改EMQX配置文件

安装包解压后进入emqx文件夹

修改emqx.conf文件

<code>vim etc/emqx.conf

(1)修改节点名称

节点1 node.name=emqx1@IP

节点2 node.name=emqx2@IP

(2)修改创建集群方式

方式一:使用 static模式

根据节点列表自动创建集群

修改cluster.discovery = static

修改cluster.static.seeds = emqx1@IP,emqx2@IP(多个节点以逗号分割)

修改完成后保存

方式二:手动(manual) 方式管理集群

# 配置集群名称

cluster.name = emqxcl

# 配置集群发现方式

cluster.discovery = manual

# 配置节点名称,节点名格式为 Name@Host, Host 必须是 IP 地址或 FQDN (主机名。域名)

node.name = emqx1@IP

2.3 防火墙配置

使用下面的命令在防火墙开放端口号

sudo ufw allow 端口号

防火墙开放以下端口:

(1)Dashboard访问端口18083

(2)ekka 模式(4.0 版本之后的默认模式):

在ekka 模式下,集群发现端口的映射关系是约定好的,而不是动态的。 node.dist_listen_min 和 node.dist_listen_max 两个配置在ekka 模式下不起作用。

如果集群节点间存在防火墙,防火墙需要放开这个约定的端口。约定端口的规则如下:

ListeningPort = BasePort + Offset

其中 BasePort 为 4370 (不可配置), Offset 为节点名的数字后缀. 如果节点名没有数字后缀的话, Offsset 为 0。

举例来说, 如果 emqx.conf 里配置了节点名:node.name = emqx@192.168.0.12,那么监听端口为 4370, 但对于 emqx1 (或者 emqx-1) 端口就是 4371,以此类推。

(3)The Cluster RPC Port

每个节点还需要监听一个 RPC 端口,也需要被防火墙也放开。跟上面说的ekka 模式下的集群发现端口一样,这个 RPC 端口也是约定式的。

RPC 端口的规则跟ekka 模式下的集群发现端口类似,只不过 BasePort = 5370。就是说,如果 emqx.conf 里配置了节点名:node.name = emqx@192.168.0.12,那么监听端口为 5370, 但对于 emqx1 (或者 emqx-1) 端口就是 5371,以此类推。

2.4 启动EMQX集群

(1)分别启动EMQX集群的每个节点

进入emqx文件夹执行下面的命令启动EMQX Broker

./bin/emqx start

使用 static模式创建集群时,当节点全部启动后,会自动创建集群

使用手动(manual) 方式管理集群:

在节点1加入节点2形成集群

# 在节点emqx1@IP上执行

./bin/emqx_ctl cluster join emqx2@IP

# 或者在节点在节点emqx2@IP执行

./bin/emqx_ctl cluster join emqx1@IP

节点退出集群:

# 让本节点退出集群

./bin/emqx_ctl cluster leave

# 从集群删除其他节点

./bin/emqx_ctl cluster force-leave emqx1@IP

(2)查看节点状态:

./bin/emqx_ctl status

(3)查看emqx集群状态:

./bin/emqx_ctl cluster status

2.5 访问EMQX Dashboard

访问 http://ip:18083 来查看Dashboard,默认用户名是 admin,密码是 public

3 EMQX认证

EMQX 的认证支持包括两个层面:

MQTT 协议本身在 CONNECT 报文中指定用户名和密码,EMQX 以插件形式支持基于 Username、ClientID、HTTP、JWT、LDAP 及各类数据库如 MongoDB、MySQL、PostgreSQL、Redis 等多种形式的认证。

在传输层上,TLS 可以保证使用客户端证书的客户端到服务器的身份验证,并确保服务器向客户端验证服务器证书。也支持基于 PSK 的 TLS/DTLS 认证。

本章节介绍了 EMQX 的MySQL认证插件的配置方法以及TLS认证配置。

3.1 开启认证模式

EMQ X 默认配置中启用了匿名认证,任何客户端都能接入 EMQX,因此,EMQX开启认证首先要关闭匿名认证。

vim etc/emqx.conf

将allow_anonymous设为false,关闭匿名访问,关闭后EMQX将不允许匿名用户连接,需要通过用户名密码连接。

3.2 MySQL认证

MySQL 认证使用外部 MySQL 数据库作为认证数据源,可以存储大量数据,同时方便与外部设备管理系统集成。

3.2.1 安装MySQL数据库

MySQL数据库配置

3.2.2 MySQL认证配置

1 在MySQL中创建emqx所需的数据库和用户认证表

(1)创建用户名emqx密码emqx

<code>CREATE USER emqx@'%' IDENTIFIED BY 'emqx';

(2)创建emqx数据库

create database emqx character set utf8;

(3)将数据库emqx下的所有表授权给emqx用户

grant all privileges on emqx.* to 'emqx'@'%';

(4)创建认证用户表

CREATE TABLE `mqtt_user` (

  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,

  `username` varchar(100) DEFAULT NULL,

  `password` varchar(100) DEFAULT NULL,

  `salt` varchar(35) DEFAULT NULL,

  `is_superuser` tinyint(1) DEFAULT 0,

  `created` datetime DEFAULT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `mqtt_username` (`username`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

创建好数据库相关用户和表结构后在每个节点的配置文件etc/plugins/emqx_auth_mysql.conf 中配置以下内容:

2 修改MySQL认证配置文件

创建好数据库相关用户和表结构后在每个节点的配置文件etc/plugins/emqx_auth_mysql.conf 中配置以下内容:

(1)配置 MySQL 服务相关的连接地址,用户名密码和数据库

(2) 配置认证SQL

进行身份认证时,EMQX 将使用当前客户端信息填充并执行用户配置的认证 SQL,查询出该客户端在数据库中的认证数据。

你可以在认证 SQL 中使用以下占位符,执行时 EMQX 将自动填充为客户端信息:

%u:用户名

%c:Client ID

%C:TLS 证书公用名(证书的域名或子域名),仅当 TLS 连接时有效

%d:TLS 证书 subject,仅当 TLS 连接时有效

你可以根据业务需要调整认证 SQL,如添加多个查询条件、使用数据库预处理函数,以实现更多业务相关的功能。但是任何情况下认证 SQL 需要满足以下条件:

a.查询结果中必须包含 password 字段,EMQX 使用该字段与客户端密码比对

b.如果启用了加盐配置,查询结果中必须包含 salt 字段,EMQX 使用该字段作为 salt(盐)值

c.查询结果只能有一条,多条结果时只取第一条作为有效数据

(3) 配置加盐规则与哈希方法

配置密码加密方式,本文密码加密采用加盐+sha256方式

3.3 TLS认证双向认证

1 MQTT TLS 的默认端口是 8883:

2 配置证书和 CA

开启TLS双向认证时需要创建客户端和服务端证书,并在此处配置服务端证书的位置,在使用TLS双向认证连接时,需要在客户端配置客户端证书。

创建自签名证书方法参考附录6.1章节

3 开启双向认证

以下两个配置是 打开服务端 验证客户端证书 (默认关闭 即单向验证 打开即可实现双向认证)

作为一种性能优化设置,它允许客户端重用预先存在的会话,而不是初始化新的会话。

EMQX集群所有节点配置完成后,分别重启每个节点

3.4 认证验证

3.4.1 开启emqx_auth_mysql插件认证

开启所有节点的emqx_auth_mysql插件认证,打开emqx Dashboard界面,点击插件模块,启动emqx_auth_mysql插件。

3.4.2 创建emqx用户名和密码

在emqx数据库的mqtt_user表中新增一个用户

<code>INSERT INTO `mqtt_user` ( `username`, `password`, `salt`) VALUES

(‘test’, ‘62a11257d24e7b577b87936eaf7931a0f15bc7b4e52f2c37507b7792c38b6d0c’, ‘secret’);

注意:

密码是经过加密后的密文,如果采用了加盐,需要添加加盐字段,可以在网上搜索加密工具,本文使用的加密工具地址:md5加密,sha1加密--md5在线解密

选择正确的加密方式生成的密码hash值(本文是采用salt+sha256)

3.4.3 验证
3.4.3.1 使用WebSocket工具

使用EMQX Dashboard Websocket工具可以进行简单的客户端连接服务端测试,主题订阅和发布消息。

开启MYSQL认证后,需要通过已创建的用户名和密码连接,否则会显示连接失败。

使用WebSocket工具的SSL连接需要安装客户端证书,否则无法连接,本文推荐使用MQTTX客户端工具进行SSL认证连接测试。

3.4.3.2 MQTTX客户端连接工具

1 安装MQTTX客户端

访问MQTTX:全功能 MQTT 客户端工具下载安装MQTTX客户端

2 打开客户端新建连接

(1)新建一个连接,填写服务端地址、端口号、用户名和密码,开启SSL/TLS双向认证,选择自定义证书,开启SSL Secure。

(2)选择本地客户端证书的位置

点击connect进行连接

MQTTX其他功能可以参考官方文档,在次不做过多介绍

MQTTX:全功能 MQTT 客户端工具

4 发布订阅 ACL

发布订阅 ACL 指对 发布 (PUBLISH)/订阅 (SUBSCRIBE) 操作的 权限控制。例如拒绝用户名为 Anna 向 open/elsa/door 发布消息。

EMQX 支持通过客户端发布订阅 ACL 进行客户端权限的管理,本章节介绍了 EMQX 发布订阅 ACL 以及对应MYSQL插件的配置方法。

4.1 MySQL插件配置

发布订阅 ACL功能包含在emqx_auth_mysql 插件中,在3.2MySQL认证小节中已经开启了此插件,并进行了数据库配置,在此基础上还需进行如下配置。

4.1.1 创建ACL规则表

在emqx数据库中创建acl规则表mqtt_acl

<code>CREATE TABLE `mqtt_acl` (

  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,

  `allow` int(1) DEFAULT 1 COMMENT '0: deny, 1: allow',

  `ipaddr` varchar(60) DEFAULT NULL COMMENT 'IpAddress',

  `username` varchar(100) DEFAULT NULL COMMENT 'Username',

  `clientid` varchar(100) DEFAULT NULL COMMENT 'ClientId',

  `access` int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub',

  `topic` varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter',

  PRIMARY KEY (`id`),

  INDEX (ipaddr),

  INDEX (username),

  INDEX (clientid)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

规则表字段说明:

allow:禁止(0),允许(1)

ipaddr:设置 IP 地址

username:连接客户端的用户名,此处的值如果设置为 $all 表示该规则适用于所有的用户

clientid:连接客户端的 Client ID

access:允许的操作:订阅(1),发布(2),订阅发布都可以(3)

topic:控制的主题,可以使用通配符,并且可以在主题中加入占位符来匹配客户端信息,例如 t/%c 则在匹配时主题将会替换为当前客户端的 Client ID

%u:用户名

%c:Client ID

4.1.2 超级用户 SQL(super_query)

进行 ACL 鉴权时,EMQX 将使用当前客户端信息填充并执行用户配置的超级用户 SQL,查询客户端是否为超级用户。客户端为超级用户时将跳过 ACL SQL。

在etc/plugins/emqx_auth_mysql.conf配置中做如下修改

你可以在 SQL 中使用以下占位符,执行时 EMQX 将自动填充为客户端信息:

%u:用户名

%c:Client ID

%C:TLS 证书公用名(证书的域名或子域名),仅当 TLS 连接时有效

%d:TLS 证书 subject,仅当 TLS 连接时有效

你可以根据业务需要调整超级用户 SQL,如添加多个查询条件、使用数据库预处理函数,以实现更多业务相关的功能。但是任何情况下超级用户 SQL 需要满足以下条件:

(1)查询结果中必须包含 is_superuser 字段,is_superuser 应该显式的为 true;

(2)查询结果只能有一条,多条结果时只取第一条作为有效数据。

提示

如果不需要超级用户功能,注释并禁用该选项能有效提高效率

4.1.3 ACL SQL(acl_query)

进行 ACL 鉴权时,EMQX 将使用当前客户端信息填充并执行用户配置的超级用户 SQL,如果没有启用超级用户 SQL 或客户端不是超级用户,则使用 ACL SQL 查询出该客户端在数据库中的 ACL 规则。

在etc/plugins/emqx_auth_mysql.conf配置中做如下修改

你可以在 ACL SQL 中使用以下占位符,执行时 EMQX 将自动填充为客户端信息:

%u:用户名

%c:Client ID

%C:TLS 证书公用名(证书的域名或子域名),仅当 TLS 连接时有效

%d:TLS 证书 subject,仅当 TLS 连接时有效

你可以根据业务需要调整 ACL SQL,如添加多个查询条件、使用数据库预处理函数,以实现更多业务相关的功能。但是任何情况下 ACL SQL 需要满足以下条件:

查询结果中必须包含 allow、access、topic、clientid、username、ipaddr 字段,如果字段不想参与比对则使用 $all 字符串或者数据库 NULL 值

查询结果可以有多条,多条结果时按照从上到下的顺序进行匹配

提示

可以在 SQL 中调整查询条件、指定排序方式实现更高效率的查询。

4.2 ACL全局配置

修改etc/emqx.conf 中的 ACL 配置

(1)acl_nomatch

 ACL 未匹配时默认授权,deny拒绝授权

(2)acl_file

配置默认 ACL 文件,使用文件定义默认 ACL 规则

(3)acl_deny_action

配置 ACL 授权结果为禁止的响应动作,为 disconnect 时将断开设备

(4)enable_acl_cache

ACL 缓存开关

(5)acl_cache_max_size

单个客户端最大缓存规则数量

(6)acl_cache_ttl

缓存失效时间,超时后缓存将被清除

ACL 缓存允许客户端在命中某条 ACL 规则后,便将其缓存至内存中,以便下次直接使用,客户端发布、订阅频率较高的情况下开启 ACL 缓存可以提高 ACL 检查性能。

4.3 内置ACL配置

通过etc/acl.conf文件配置ACL内置规则

内置 ACL 优先级最低,可以被 ACL 插件覆盖,如需禁用全部注释即可。规则文件更改后需重启 EMQX 以应用生效。

内置 ACL 是优先级最低规则表,在所有的 ACL 检查完成后,如果仍然未命中则检查默认的 ACL 规则。

本文在此配置几条ACL默认规则

(1)允许 "dashboard" 用户 订阅 "$SYS/#" 主题

{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.

(2) 允许 IP 地址为 "127.0.0.1" 的用户 发布/订阅 "$SYS/#","#" 主题

{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}.

(3)拒绝 "所有用户" 订阅 "$SYS/#" "#" 主题

{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}.

acl.conf具体编写规则请参考官方文档

内置 ACL | EMQX 4.4 文档

将每个节点上述配置修改完毕后重启后生效

重启EMQX节点命令

./bin/emqx restart

4.4 ACL功能验证

使用EMQX Dashboard Websocket工具可以进行简单验证,开启topic发布订阅ACL后,必须在mqtt_acl表中为用户配置topic权限后,用户才可以发布或订阅该topic。

如下图,为test用户赋予test-topic主题的发布订阅权限

使用Websocket工具进行验证:

(1)首先使用test用户连接服务端

(2)连接服务端后尝试订阅其他未授权主题会显示订阅失败,只能订阅test-topic,订阅成功后可以在订阅模块显示订阅信息

 (3)向已授权的指定topic发送信息

(4)向未授权的topic发送消息

向未授权的topic发送消息时在websocket工具界面仍会显示消息已发出,但可以在emqx日志查看到不能向未授权的topic发送消息。

5 EMQX系统优化

5.1 Linux 操作系统参数

1 系统全局允许分配的最大文件句柄数:

<code># 2 millions system-wide

sysctl -w fs.file-max=2097152

sysctl -w fs.nr_open=2097152

echo 2097152 > /proc/sys/fs/nr_open

2 允许当前会话 / 进程打开文件句柄数:

ulimit -n 1048576

3 /etc/sysctl.conf

持久化 'fs.file-max' 设置到 /etc/sysctl.conf 文件:

fs.file-max = 1048576

4 /etc/systemd/system.conf 设置服务最大文件句柄数

DefaultLimitNOFILE=1048576

5 /etc/security/limits.conf

/etc/security/limits.conf 持久化设置允许用户 / 进程打开文件句柄数:

*      soft   nofile      1048576

*      hard   nofile      1048576

5.2 TCP 协议栈网络参数

1 并发连接 backlog 设置:

sysctl -w net.core.somaxconn=32768

sysctl -w net.ipv4.tcp_max_syn_backlog=16384

sysctl -w net.core.netdev_max_backlog=16384

2 可用知名端口范围:

sysctl -w net.ipv4.ip_local_port_range='1000 65535'code>

3 TCP Socket 读写 Buffer 设置:

sysctl -w net.core.rmem_default=262144

sysctl -w net.core.wmem_default=262144

sysctl -w net.core.rmem_max=16777216

sysctl -w net.core.wmem_max=16777216

sysctl -w net.core.optmem_max=16777216

sysctl -w net.ipv4.tcp_rmem='1024 4096 16777216'code>

sysctl -w net.ipv4.tcp_wmem='1024 4096 16777216'code>

4 TCP 连接追踪设置:

sysctl -w net.netfilter.nf_conntrack_max=1000000

sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30

5 TIME-WAIT Socket 最大数量、回收与重用设置:

sysctl -w net.ipv4.tcp_max_tw_buckets=1048576

6 FIN-WAIT-2 Socket 超时设置:

sysctl -w net.ipv4.tcp_fin_timeout=15

5.3 Erlang 虚拟机参数

优化设置 Erlang 虚拟机启动参数,配置文件 emqx/etc/emqx.conf:

## Erlang Process Limit

node.process_limit = 2097152

## Sets the maximum number of simultaneously existing ports for this system

node.max_ports = 1048576

5.4 EMQX 消息服务器参数

ceptor 池大小,最大允许连接数。 emqx/etc/emqx.conf

## TCP Listener

listener.tcp.external = 0.0.0.0:1883

listener.tcp.external.acceptors = 64

listener.tcp.external.max_connections = 1024000

注意:EMQX节点参数的配置需要重启节点后生效

6 附录

6.1 openssl生成证书

6.1.1 生成自签名 CA 证书

首先,我们需要一个自签名的 CA 证书。生成这个证书需要有一个私钥为它签名,可以执行以下命令来生成私钥:

openssl genrsa -out ca.key 2048

这个命令将生成一个密钥长度为 2048 的密钥并保存在 ca.key 中。有了这个密钥,就可以用它来生成 EMQX 的根证书了:

openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.pem

根证书是整个信任链的起点,如果一个证书的每一级签发者向上一直到根证书都是可信的,那个我们就可以认为这个证书也是可信的。有了这个根证书,我们就可以用它来给其他实体签发实体证书了。

6.1.2 生成服务端证书

实体(在这里指的是 EMQX)也需要一个自己的私钥对来保证它对自己证书的控制权。生成这个密钥的过程和上面类似:

openssl genrsa -out emqx.key 2048

新建 openssl.cnf 文件,

req_distinguished_name :根据情况进行修改,

alt_names: BROKER_ADDRESS 修改为 EMQX 服务器实际的 IP 或 DNS 地址,例如:IP.1 = 127.0.0.1,或 DNS.1 = broker.xxx.com

注意:IP 和 DNS 二者保留其一即可,如果已购买域名,只需保留 DNS 并修改为你所使用的域名地址。

[req]

default_bits  = 2048

distinguished_name = req_distinguished_name

req_extensions = req_ext

x509_extensions = v3_req

prompt = no

[req_distinguished_name]

countryName = CN

stateOrProvinceName = Zhejiang

localityName = Hangzhou

organizationName = EMQX

commonName = CA

[req_ext]

subjectAltName = @alt_names

[v3_req]

subjectAltName = @alt_names

[alt_names]

IP.1 = BROKER_ADDRESS

DNS.1 = BROKER_ADDRESS

然后以这个密钥和配置签发一个证书请求:

openssl req -new -key ./emqx.key -config openssl.cnf -out emqx.csr

然后以根证书来签发 EMQX 的实体证书:

openssl x509 -req -in ./emqx.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out emqx.pem -days 3650 -sha256 -extensions v3_req -extfile openssl.cnf

6.1.3 生成客户端证书

双向连接认证还需要创建客户端证书,首先需要创建客户端密钥:

openssl genrsa -out client.key 2048

使用生成的客户端密钥来创建一个客户端请求文件:

openssl req -new -key client.key -out client.csr -subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=EMQX/CN=client"

最后使用先前生成好的服务端 CA 证书来给客户端签名,生成一个客户端证书:

openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem

准备好服务端和客户端证书后,我们就可以在 EMQX 中启用 TLS/SSL 双向认证功能。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。