在云服务器上安装emqx+wss并使用ESP32连接
本文介绍如何在云服务器上,使用docker安装emqx
,搭建基于websockets
的mqtt服务并使用ESP32进行连接。
获取并制作证书
获取SSL证书
若要为emqx启用加密的mqtts
与wss
协议,需要SSL证书。通常情况下,有两种渠道取得SSL证书:自签证书与向CA申请证书。使用自签证书,不需要向CA申请证书,但是需要为每个客户端安装CA根证书,而CA的根证书预先安装在了设备上,因此使用CA签发的证书不需要在客户端上安装证书(对于例如ESP32
的嵌入式设备仍需要)。
由于我的域名在阿里云上,并且阿里云提供免费的SSL证书,因此在此我选择使用CA签发的证书。后文的证书配置流程也以阿里云的免费证书以及域名iot.yuanze.wang
为例,具体的申请过程在此不再赘述。
在申请到证书后,前往阿里云SSL证书控制台下载证书,下载格式请选择Apache
。由于emqx的CA证书需要完整的证书链,因此还需额外下载根证书。对于阿里云免费SSL证书来说,需要下载DigiCert OV和DV根证书。
下载完成后,将解压后的网站证书与根证书放到同一个文件夹中,可以发现文件夹中共有4个文件,它们分别是:
iot.yuanze.wang.key
:网站证书的私钥文件iot.yuanze.wang_public.crt
:网站证书的公钥文件iot.yuanze.wang_chain.crt
:中间CA机构Encryption Everywhere
的证书Digicert-OV-DV-root.cer
:顶级CADigicert
的根证书
制作emqx要求的证书
上文中提到,emqx需要CA的完整证书链。对于我的免费SSL证书来说,其证书链为Digicert
->Encryption Everywhere
->iot.yuanze.wang
,CA证书链即为去掉iot.yuanze.wang
后的前两个。
emqx需要三个证书文件,cacert.pem
cert.pem
与key.pem
,它们分别为CA证书链、网站证书的公钥与私钥。因此,直接将iot.yuanze.wang.key
重命名为key.pem
(若从其他途径申请证书,需要注意私钥格式为RSA,可以打开私钥文件后,确认其第一行是否为BEGIN RSA PRIVATE KEY
),将iot.yuanze.wang_public.crt
重命名为cert.pem
。接下来,分别用文本编辑器打开Digicert-OV-DV-root.cer
与iot.yuanze.wang_chain.crt
,将其中内容合并成一个文件,命名为cacert.pem
。
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEqjCCA5KgAwIBAgIQAnmsRYvBskWr+YBTzSybsTANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xNzExMjcxMjQ2MTBaFw0yNzExMjcxMjQ2MTBaMG4xCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH
MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPeP6wkab41dyQh6mKc
oHqt3jRIxW5MDvf9QyiOR7VfFwK656es0UFiIb74N9pRntzF1UgYzDGu3ppZVMdo
lbxhm6dWS9OK/lFehKNT0OYI9aqk6F+U7cA6jxSC+iDBPXwdF4rs3KRyp3aQn6pj
pp1yr7IB6Y4zv72Ee/PlZ/6rK6InC6WpK0nPVOYR7n9iDuPe1E4IxUMBH/T33+3h
yuH3dvfgiWUOUkjdpMbyxX+XNle5uEIiyBsi4IvbcTCh8ruifCIi5mDXkZrnMT8n
wfYCV6v6kDdXkbgGRLKsR4pucbJtbKqIkUGxuZI2t7pfewKRc5nWecvDBZf3+p1M
pA8CAwEAAaOCAU8wggFLMB0GA1UdDgQWBBRVdE+yck/1YLpQ0dfmUVyaAYca1zAf
BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTAOBgNVHQ8BAf8EBAMCAYYw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C
AQAwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
Y2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQu
Y29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG
/WwBAjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
MAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAQEAK3Gp6/aGq7aBZsxf/oQ+TD/B
SwW3AU4ETK+GQf2kFzYZkby5SFrHdPomunx2HBzViUchGoofGgg7gHW0W3MlQAXW
M0r5LUvStcr82QDWYNPaUy4taCQmyaJ+VB+6wxHstSigOlSNF2a6vg4rgexixeiV
4YSB03Yqp2t3TeZHM9ESfkus74nQyW7pRGezj+TC44xCagCQQOzzNmzEAP2SnCrJ
sNE2DpRVMnL8J6xBRdjmOsC3N6cQuKuRXbzByVBjCqAA8t1L0I+9wXJerLPyErjy
rMKWaBFLmfK/AHNF4ZihwPGOc7w6UHczBZXH5RFzJNnww+WnKuTPI0HfnVH8lg==
-----END CERTIFICATE-----
然后,将上述3个证书文件上传到云服务器上。本文中我将证书上传到/home/docker/emqx-certs
目录下。
wangyz@aliyun-host:/home/docker/emqx-certs$ tree .
.
├── cacert.pem
├── cert.pem
└── key.pem
配置emqx
用下面的命令启动emqx
容器。
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 -v /home/docker/emqx-certs:/opt/emqx/etc/certs --restart always emqx/emqx:4.3.5
emqx
默认将1883
端口用于基于TCP的mqtt协议,8883
端口用于基于TCP及SSL加密的mqtts协议;8083
与8084
端口则用于websocket与启用了TLS的websockets协议。除此之外,18083
端口用于web管理页面,因此在启动容器时需要开启上述端口。如果不需要上述协议中的某个协议,也可以不映射对应的端口。同时,上述命令还将存放上传证书的目录挂载到了容器对应的目录中。
开启认证插件
由于后文将要禁止匿名登录,因此需要首先选择一个认证插件。emqx提供了多种认证插件,在这里仅配置最简单的mnesia
认证。它无需数据库,可以通过预设定的账户与密码来实现身份认证。但是该插件默认是禁用状态,要想实现认证功能,必须将其启用。emqx集成了一个web管理页面,通过web管理界面,可以快速的启用该插件。
访问iot.yuanze.wang:18083
,打开web管理页面,初始账户为admin
,初始密码为public
。进入后,点击右侧插件选项卡,找到emqx_auth_mnesia
插件,点击右侧的启用按钮,即可启用mnesia
认证功能。同时,还可以在通用-用户选项卡中,点击admin
右侧的编辑按钮对管理员默认密码进行修改。
修改emqx的配置文件
为了对emqx进行配置,需要先进入docker容器。
docker exec -it emqx bash
进入容器后,默认目录为/opt/emqx
。此目录下的etc
目录存放了配置文件,emqx的主配置文件为etc/emqx.conf
文件,使用编辑器打开该文件。
vi etc/emqx.conf
功能 | 设置项 |
---|---|
关闭匿名登录 | allow_anonymous = false |
启用mqtts的CA证书链 | listener.ssl.external.cacertfile = etc/certs/cacert.pem |
启用wss的CA证书链 | listener.wss.external.cacertfile = etc/certs/cacert.pem |
修改完上述配置后,保存退出。
接下来为mnesia
认证插件配置一个默认密码。尽管官方不建议直接将明文密码写在配置文件中,但对于个人调试用途的服务器来说已经足够使用了。插件的配置文件为etc/plugins/emqx_auth_mnesia.conf
,使用编辑器打开该文件,取消auth.user.1.username
与auth.user.1.password
的注释,并在后面填写用户名与密码即可。
执行完上述配置后,按Ctrl + D退出容器。然后,重启容器。
docker restart emqx
测试服务器
配置完成后,便可以尝试使用mqtt客户端对配置好的服务器进行测试,例如MQTTX软件。
对服务器参数进行配置后,点击连接按钮,若配置正确,将可以正确连接至服务器。发布并订阅同一个topic后,可以收到信息,证明服务器工作正常。
使用ESP32连接服务器
乐鑫在esp-idf
中已经提供了基于wss的连接例程,在esp-idf/examples/protocols/mqtt/wss
下,本文下面的实验也基于该例程。
首先,在idf.py menuconfig
中的Example Connection Configuration
设置中,配置好WiFi的SSID与密码。然后,打开源码目录的main/app_main.c
文件,找到其中对mqtt连接信息的声明部分。
const esp_mqtt_client_config_t mqtt_cfg = {
.uri = CONFIG_BROKER_URI,
.cert_pem = (const char *)mqtt_eclipseprojects_io_pem_start,
};
将其改为实际的服务器信息。在源码中更改CONFIG_BROKER_URI
后,可以不用在menuconfig
中更改服务器的地址。
const esp_mqtt_client_config_t mqtt_cfg = {
.uri = "wss://iot.yuanze.wang:8084/mqtt",
.username = "xxxxx",
.password = "xxxxx",
.cert_pem = (const char *)mqtt_eclipseprojects_io_pem_start,
};
由于嵌入式系统中并没有根证书,还需要将SSL根证书加入到程序之中。找到main/qtt_eclipseprojects_io.pem
文件,将前述的Digicert-OV-DV-root.cer
根证书中的所有内容替换到该文件中,然后编译并下载。
I (2078) esp_netif_handlers: example_connect: sta ip: 192.168.2.215, mask: 255.255.255.0, gw: 192.168.2.1
I (2078) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.2.215
I (2088) example_connect: Connected to example_connect: sta
I (2088) example_connect: - IPv4 address: 192.168.2.215
I (2098) MQTTWSS_EXAMPLE: [APP] Free memory: 246972 bytes
I (2108) MQTTWSS_EXAMPLE: Other event id:7
W (2118) wifi:<ba-add>idx:1 (ifx:0, 88:c3:97:50:05:6a), tid:0, ssn:4, winSize:64
I (3908) MQTTWSS_EXAMPLE: MQTT_EVENT_CONNECTED
I (3918) MQTTWSS_EXAMPLE: sent subscribe successful, msg_id=31044
I (3918) MQTTWSS_EXAMPLE: sent subscribe successful, msg_id=6202
I (3918) MQTTWSS_EXAMPLE: sent unsubscribe successful, msg_id=2784
I (3988) MQTTWSS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=31044
I (3988) MQTTWSS_EXAMPLE: sent publish successful, msg_id=0
I (3998) MQTTWSS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=6202
I (3998) MQTTWSS_EXAMPLE: sent publish successful, msg_id=0
I (4008) MQTTWSS_EXAMPLE: MQTT_EVENT_UNSUBSCRIBED, msg_id=2784
I (4068) MQTTWSS_EXAMPLE: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=data
I (4068) MQTTWSS_EXAMPLE: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=data
从日志中可以看到,ESP32已经成功连接上WiFi,并连接上了mqtt服务器,向/topic/qos0
发布了内容为data
的消息,之后订阅了/topic/qos0
并收到了data
。此时,通过MQTTX软件同样可以向ESP32发布消息。