如何在 Ubuntu 16.04 上安装和保护 Mosquitto MQTT 消息代理
介绍
MQTT 是一种机器对机器消息传递协议,旨在为“物联网”设备提供轻量级发布/订阅通信。它通常用于车辆的地理跟踪车队、家庭自动化、环境传感器网络和公用事业-规模数据收集。
Mosquitto 是一种流行的 MQTT 服务器(或 broker,用 MQTT 的说法),具有强大的社区支持并且易于安装和配置。
在本教程中,我们将安装 Mosquitto,从 Let's Encrypt 检索 SSL 证书,并设置我们的代理以使用 SSL 来保护我们受密码保护的 MQTT 通信。
先决条件
在开始本教程之前,您需要:
- 一台 Ubuntu 16.04 服务器,具有非根用户、启用 sudo 的用户和基本防火墙设置,详见此 Ubuntu 16.04 服务器设置教程。
- 根据如何使用 DigitalOcean 设置主机名,指向您的服务器的域名。本教程将始终使用
mqtt.example.com
。
第 1 步 — 安装 Mosquitto
Ubuntu 16.04 在其默认软件存储库中有一个相当新的 Mosquitto 版本。使用您的非 root 用户登录并使用 apt-get
安装 Mosquitto。
- sudo apt-get install mosquitto mosquitto-clients
默认情况下,Ubuntu 会在安装后启动 Mosquitto 服务。让我们测试默认配置。我们将使用我们刚刚安装的 Mosquitto 客户端之一来订阅我们代理上的主题。
主题 是您发布和订阅消息的标签。它们按层次结构排列,因此您可以有 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
最后,安装名为 certbot
的官方 Let's Encrypt 客户端。
- 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
,其中包含一些帮助文本。将以下行粘贴到文件末尾,然后保存并关闭它。
. . .
15 3 * * * certbot renew --noninteractive --post-hook "systemctl restart mosquitto"
此行的 15 3 * * *
部分表示“每天凌晨 3:15 运行以下命令”。Certbot 的 renew
命令将检查所有证书安装在系统上并更新任何设置为在三十天内过期的。--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
这应该打开一个空文件。粘贴以下内容:
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
将以下内容粘贴到文件末尾,保留我们已经添加的两行:
. . .
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 仅将此端口绑定到本地主机接口,因此无法从外部访问。无论如何,外部请求都会被我们的防火墙阻止,但最好是明确的。
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
的标准安全端口。
如果测试一切顺利,我们将再次看到 hello 出现在另一个 mosquitto_sub
终端中。这意味着您的服务器已完全设置!如果您想扩展 MQTT 协议以使用 websockets,您可以执行最后一步。
第 7 步 — 通过 Websockets 配置 MQTT(可选)
为了在 Web 浏览器中使用 JavaScript 来表达 MQTT,该协议被改编为在标准的 websockets 上工作。如果您不需要此功能,则可以跳过此步骤。
我们需要在我们的 Mosqiutto 配置中再添加一个 listener
块:
- sudo nano /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 客户端。那里有一些,但是在浏览器中打开 Paho 客户端。您会看到以下内容:

填写连接信息如下:
- 主机应该是您的 Mosquitto 服务器的域,
mqtt.example.com
。 - 端口应为
8083
。 - ClientId 可以保留为默认值 js-utility-DI1m6。
- 路径可以保留默认值,/ws。
- 用户名应该是您的 Mosquitto 用户名;在这里,我们使用了 sammy。
- 密码应该是您选择的密码。
其余字段可以保留为默认值。
按“连接”后,基于 Paho 浏览器的客户端将连接到您的 Mosquitto 服务器。
要发布消息,请导航至 Publish Message 窗格,填写 Topic as test,然后在 Message 部分输入任何消息。接下来,按发布。该消息将显示在您的 mosquitto_sub
终端中。
结论
我们现在已经设置了一个安全的、受密码保护的 MQTT 服务器,具有来自 Let's Encrypt 服务的自动续订 SSL 证书。这将作为一个强大而安全的消息传递平台,适用于您梦想的任何项目。一些与 MQTT 协议配合良好的流行软件和硬件包括:
- OwnTracks,一款可以安装在手机上的开源地理跟踪应用程序。 OwnTracks 会定期向您的 MQTT 服务器报告位置信息,然后您可以将这些信息存储并显示在地图上,或者创建警报并根据您的位置激活物联网硬件。
- Node-RED 是一个基于浏览器的图形界面,用于将物联网“连接”在一起。您将一个节点的输出拖到另一个节点的输入,并且可以通过过滤器、在各种协议之间、将信息路由到数据库等。 Node-RED 很好地支持 MQTT。
- ESP8266 是一款经济实惠的 wifi 微控制器,具有 MQTT 功能。您可以连接一个以将温度数据发布到一个主题,或者订阅一个气压主题并在暴风雨来临时发出蜂鸣声!
这些只是 MQTT 生态系统中的几个流行示例。有更多的硬件和软件可以使用该协议。如果您已经有了最喜欢的硬件平台或软件语言,它可能具有 MQTT 功能。让你的“东西”互相交谈,玩得开心!