介绍
MQTT 是一种机器对机器的消息传递协议,旨在为“物联网”设备提供轻量级的发布/订阅通信。 它通常用于地理跟踪车队、家庭自动化、环境传感器网络和公用事业规模的数据收集。
Mosquitto 是一种流行的 MQTT 服务器(或 broker,用 MQTT 的说法),具有强大的社区支持并且易于安装和配置。
在本教程中,我们将安装 Mosquitto,从 Let's Encrypt 检索 SSL 证书,并设置我们的代理以使用 SSL 来保护我们受密码保护的 MQTT 通信。
先决条件
在开始本教程之前,您需要:
- 一个 Ubuntu 16.04 服务器,具有非 root、启用 sudo 的用户和基本防火墙设置,如 this Ubuntu 16.04 server setup tutorial 中所述。
- 根据 如何使用 DigitalOcean 设置主机名,指向您的服务器的域名。 本教程将自始至终使用
mqtt.example.com
。
第 1 步 — 安装 Mosquitto
Ubuntu 16.04 在其默认软件存储库中有一个相当新的 Mosquitto 版本。 使用非 root 用户登录并使用 apt-get
安装 Mosquitto。
sudo apt-get install mosquitto mosquitto-clients
默认情况下,Ubuntu 会在安装后启动 Mosquitto 服务。 让我们测试一下默认配置。 我们将使用我们刚刚安装的 Mosquitto 客户端之一来订阅我们代理上的主题。
Topics 是您发布和订阅消息的标签。 它们按层次结构排列,例如,您可以使用 sensors/outside/temp
和 sensors/outside/humidity
。 您如何安排主题取决于您和您的需求。 在本教程中,我们将使用一个简单的测试主题来测试我们的配置更改。
再次登录您的服务器,这样您就有了两个并排的终端。 在新终端中,使用 mosquitto_sub
订阅测试主题:
mosquitto_sub -h localhost -t test
-h
用于指定MQTT服务器的主机名,-t
为主题名。 点击 ENTER
后您将看不到任何输出,因为 mosquitto_sub
正在等待消息到达。 切换回您的另一个终端并发布一条消息:
mosquitto_pub -h localhost -t test -m "hello world"
mosquitto_pub
的选项与 mosquitto_sub
相同,但这次我们使用附加的 -m
选项来指定我们的消息。 点击 ENTER
,你应该会看到 hello world 在另一个终端弹出。 您已经发送了您的第一条 MQTT 消息!
在第二个终端输入CTRL+C
退出mosquitto_sub
,但保持与服务器的连接打开。 我们将在第 5 步中再次使用它进行另一个测试。
接下来,我们将使用新的 Let's Encrypt 客户端 Certbot 通过 SSL 保护我们的安装。
第 2 步 — 为 Let's Encrypt 证书安装 Certbot
Let's Encrypt 是一项通过自动化 API 提供免费 SSL 证书的新服务。 有许多客户端可以与 API 通信,Ubuntu 在其默认存储库中包含官方客户端,但它有点过时并且缺少我们需要的一项重要功能。
相反,我们将从 Ubuntu PPA 或 Personal Package Archive 安装官方客户端。 这些是打包更新或更晦涩软件的替代存储库。 首先,添加存储库。
sudo add-apt-repository ppa:certbot/certbot
您需要按 ENTER
接受。 之后,更新包列表以获取新存储库的包信息。
sudo apt-get update
最后,安装官方的 Let's Encrypt 客户端,名为 certbot
。
sudo apt-get install certbot
现在我们已经安装了 certbot
,让我们运行它来获取我们的证书。
第 3 步 — 运行 Certbot
certbot
需要回答 Let's Encrypt API 发出的密码挑战,以证明我们控制了我们的域。 它使用端口 80
(HTTP) 和/或 443
(HTTPS) 来完成此操作。 我们将只使用端口 80
,所以现在让我们允许该端口上的传入流量:
sudo ufw allow http
OutputRule added
我们现在可以运行 Certbot 来获取我们的证书。 我们将使用 --standalone
选项告诉 Certbot 自行处理 HTTP 质询请求,并且 --standalone-supported-challenges http-01
将通信限制为端口 80
。 -d
用于指定您想要证书的域,certonly
告诉 Certbot 只检索证书而不执行任何其他配置步骤。
sudo certbot certonly --standalone --standalone-supported-challenges http-01 -d mqtt.example.com
运行命令时,系统会提示您输入电子邮件地址并同意服务条款。 完成此操作后,您应该会看到一条消息,告诉您该过程成功以及您的证书存储在哪里。
我们有我们的证书。 现在我们需要确保 Certbot 在它们即将到期时自动更新它们。
第 4 步 — 设置 Certbot 自动续订
Let's Encrypt 的证书有效期只有九十天。 这是为了鼓励用户自动化他们的证书更新过程。 我们需要设置一个定期运行的命令来检查过期证书并自动更新它们。
为了每天运行更新检查,我们将使用 cron
,这是一种用于运行定期作业的标准系统服务。 我们通过打开和编辑一个名为 crontab
的文件来告诉 cron
要做什么。
sudo crontab -e
系统将提示您选择文本编辑器。 选择您最喜欢的,您将看到默认的 crontab
,其中包含一些帮助文本。 在文件末尾粘贴以下行,然后保存并关闭它。
crontab
. . . 15 3 * * * certbot renew --noninteractive --post-hook "systemctl restart mosquitto"
该行的 15 3 * * *
部分表示“每天凌晨 3:15 运行以下命令”。 Certbot 的 renew
命令将检查系统上安装的所有证书,并更新任何设置为在 30 天内到期的证书。 --noninteractive
告诉 Certbot 不要等待用户输入。
--post-hook "systemctl restart mosquitto"
将重新启动 Mosquitto 以获取新证书,但前提是证书已更新。 这个 post-hook
功能是旧版本的 Let's Encrypt 客户端所缺少的,也是我们从 PPA 而不是默认的 Ubuntu 存储库安装的原因。 如果没有它,我们将不得不每天重新启动 Mosquitto,即使实际上没有更新任何证书。 尽管您的 MQTT 客户端应配置为自动重新连接,但明智的做法是避免每天无缘无故地中断它们。
现在自动证书更新已设置完毕,我们将回到配置 Mosquitto 以提高安全性。
第 5 步 — 配置 MQTT 密码
让我们将 Mosquitto 配置为使用密码。 Mosquitto 包含一个实用程序,用于生成名为 mosquitto_passwd
的特殊密码文件。 此命令将提示您输入指定用户名的密码,并将结果放在 /etc/mosquitto/passwd
中。
sudo mosquitto_passwd -c /etc/mosquitto/passwd sammy
现在我们将为 Mosquitto 打开一个新的配置文件,并告诉它使用这个密码文件来要求所有连接的登录:
sudo nano /etc/mosquitto/conf.d/default.conf
这应该打开一个空文件。 粘贴以下内容:
/etc/mosquitto/conf.d/default.conf
allow_anonymous false password_file /etc/mosquitto/passwd
allow_anonymous false
将禁用所有未经身份验证的连接,并且 password_file
行告诉 Mosquitto 在哪里查找用户和密码信息。 保存并退出文件。
现在我们需要重新启动 Mosquitto 并测试我们的更改。
sudo systemctl restart mosquitto
尝试在没有密码的情况下发布消息:
mosquitto_pub -h localhost -t "test" -m "hello world"
该消息应被拒绝:
OutputConnection Refused: not authorised. Error: The connection was refused.
在我们再次使用密码尝试之前,再次切换到您的第二个终端窗口,并订阅“测试”主题,这次使用用户名和密码:
mosquitto_sub -h localhost -t test -u "sammy" -P "password"
它应该连接并坐下,等待消息。 您可以在本教程的其余部分保持此终端打开并保持连接,因为我们会定期向它发送测试消息。
现在使用您的另一个终端发布消息,再次使用用户名和密码:
mosquitto_pub -h localhost -t "test" -m "hello world" -u "sammy" -P "password"
消息应该像第 1 步一样通过。 我们已经成功地为 Mosquitto 添加了密码保护。 不幸的是,我们通过互联网发送未加密的密码。 接下来我们将通过向 Mosquitto 添加 SSL 加密来解决这个问题。
第 6 步 — 配置 MQTT SSL
要启用 SSL 加密,我们需要告诉 Mosquitto 我们的 Let's Encrypt 证书的存储位置。 打开我们之前启动的配置文件:
sudo nano /etc/mosquitto/conf.d/default.conf
在文件末尾粘贴以下内容,留下我们已经添加的两行:
/etc/mosquitto/conf.d/default.conf
. . . listener 1883 localhost listener 8883 certfile /etc/letsencrypt/live/mqtt.example.com/cert.pem cafile /etc/letsencrypt/live/mqtt.example.com/chain.pem keyfile /etc/letsencrypt/live/mqtt.example.com/privkey.pem
我们将两个单独的 listener
块添加到配置中。 第一个,listener 1883 localhost
,更新端口 1883
上的默认 MQTT 侦听器,这是我们迄今为止一直连接的。 1883
是标准的未加密 MQTT 端口。 该行的 localhost
部分指示 Mosquitto 仅将此端口绑定到 localhost 接口,因此无法从外部访问。 无论如何,外部请求都会被我们的防火墙阻止,但最好是明确的。
listener 8883
在端口 8883
上设置加密侦听器。 这是 MQTT + SSL 的标准端口,通常称为 MQTTS。 接下来的三行 certfile
、cafile
和 keyfile
都将 Mosquitto 指向相应的 Let's Encrypt 文件以设置加密连接。
保存并退出文件,然后重新启动 Mosquitto 以更新设置:
sudo systemctl restart mosquitto
更新防火墙以允许连接到端口 8883
。
sudo ufw allow 8883
OutputRule added
现在我们使用 mosquitto_pub
再次测试,SSL 有几个不同的选项:
mosquitto_pub -h mqtt.example.com -t test -m "hello again" -p 8883 --capath /etc/ssl/certs/ -u "sammy" -P "password"
请注意,我们使用的是完整主机名而不是 localhost
。 因为我们的 SSL 证书是为 mqtt.example.com
颁发的,所以如果我们尝试与 localhost
建立安全连接,我们会收到错误消息,指出主机名与证书主机名不匹配(即使它们都指向相同的 Mosquitto 服务器)。
--capath /etc/ssl/certs/
为 mosquitto_pub
启用 SSL,并告诉它在哪里查找根证书。 这些通常由您的操作系统安装,因此 Mac OS、Windows 等的路径不同。 mosquitto_pub
使用根证书来验证 Mosquitto 服务器的证书是否由 Let's Encrypt 证书颁发机构正确签名。 重要的是要注意 mosquitto_pub
和 mosquitto_sub
不会在没有此选项(或类似的 --cafile
选项)的情况下尝试 SSL 连接,即使您正在连接到标准安全8883
的端口。
如果测试一切顺利,我们将在另一个 mosquitto_sub
终端中再次看到 hello 。 这意味着您的服务器已完全设置好! 如果您想扩展 MQTT 协议以使用 websockets,您可以执行最后一步。
第 7 步 — 通过 Websockets 配置 MQTT(可选)
为了在 Web 浏览器中使用 JavaScript 使用 MQTT,该协议适用于标准 websockets。 如果您不需要此功能,则可以跳过此步骤。
我们需要在 Mosqiutto 配置中再添加一个 listener
块:
sudo nano /etc/mosquitto/conf.d/default.conf
在文件末尾,添加以下内容:
/etc/mosquitto/conf.d/default.conf
. . . listener 8083 protocol websockets certfile /etc/letsencrypt/live/mqtt.example.com/cert.pem cafile /etc/letsencrypt/live/mqtt.example.com/chain.pem keyfile /etc/letsencrypt/live/mqtt.example.com/privkey.pem
除了端口号和 protocol websockets
行之外,这与前一个块基本相同。 MQTT over websockets 没有官方标准化端口,但 8083
是最常见的。
保存并退出文件,然后重新启动 Mosquitto。
sudo systemctl restart mosquitto
现在,在防火墙中打开端口 8083
。
sudo ufw allow 8083
为了测试这个功能,我们将使用一个公共的、基于浏览器的 MQTT 客户端。 那里有一些,但是 Eclipse Paho JavaScript Client 使用起来简单直接。 在浏览器中打开Paho客户端。 您将看到以下内容:
填写连接信息如下:
- Host 应该是 Mosquitto 服务器的域
mqtt.example.com
。 - 端口应该是
8083
。 - ClientId 可以保留默认值,js-utility-DI1m6。
- Path可以保留默认值,/ws。
- Username 应该是你的 Mosquitto 用户名; 在这里,我们使用了 sammy。
- Password 应该是您选择的密码。
其余字段可以保留其默认值。
按下 Connect 后,基于 Paho 浏览器的客户端将连接到您的 Mosquitto 服务器。
要发布消息,请导航到 Publish Message 窗格,将 Topic 填写为 test,然后在 Message 部分输入任何消息. 接下来,按 发布 。 该消息将显示在您的 mosquitto_sub
终端中。
结论
我们现在已经设置了一个安全的、受密码保护的 MQTT 服务器,它可以自动更新来自 Let's Encrypt 服务的 SSL 证书。 这将为您梦想的任何项目提供强大且安全的消息传递平台。 一些适用于 MQTT 协议的流行软件和硬件包括:
- OwnTracks,一款可以安装在手机上的开源地理追踪应用。 OwnTracks 会定期向您的 MQTT 服务器报告位置信息,然后您可以将其存储并显示在地图上,或者根据您的位置创建警报并激活 IoT 硬件。
- Node-RED 是一个基于浏览器的图形界面,用于将物联网“连接”在一起。 您将一个节点的输出拖到另一个节点的输入,并且可以通过过滤器、在各种协议之间、将信息路由到数据库等。 Node-RED 很好地支持 MQTT。
- ESP8266 是具有 MQTT 功能的廉价 wifi 微控制器。 您可以连接一个以将温度数据发布到主题,或者订阅气压主题并在暴风雨来临时发出蜂鸣声!
这些只是 MQTT 生态系统中的几个流行示例。 有更多的硬件和软件可以使用该协议。 如果您已经拥有喜欢的硬件平台或软件语言,它可能具有 MQTT 功能。 让你的“东西”互相交谈,玩得开心!