0%

[MQTT] Mosquitto Docker 架設與設定詳細過程

前言

[MQTT] CentOS 7 安裝 Mosquitto 並使用帳密驗證 一文已經介紹如何從 yum 開始一步步地架設 MQTT Broker。這次,我想使用 Docker 來架設 Mosquitto,同時運行 MQTT/MQTTS/WS/WSS 四種通訊協定,順便練習 Docker 的使用方法。

環境

  • Ubuntu 20.04 LTS
  • Docker 20.10.2

下載官方映像檔

搜尋官方建立的 mqtt Image 並下載

1
2
3
4
5
6
7
8
9
user@pc:~$ sudo docker search mqtt -f is-official=true  # 搜尋 Image
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
eclipse-mosquitto Eclipse Mosquitto is an open source message … 767 [OK]

user@pc:~$ sudo docker pull eclipse-mosquitto # 下載指定 Image

user@pc:~$ sudo docker images # 列出所有已下載的 Images
REPOSITORY TAG IMAGE ID CREATED SIZE
eclipse-mosquitto latest 7593d452cd8c 2 weeks ago 9.95MB
  • -f is-official=true:指定過濾出官方提供的 Image。

建立所需目錄與檔案

Container 關閉之後不會把關閉之前的數據與狀態存下來,恢復到與剛下載好的 Image 一樣乾淨 (其實根本就是一樣的東西啦),因此在使用 Container 之前,需要先創立一些目錄與檔案,來存放我們想永久儲存的資料與設定

建立目錄

1
2
3
user@pc:~$ sudo mkdir -p /mosquitto/config  # 存放設定檔的目錄
user@pc:~$ sudo mkdir -p /mosquitto/data # 存放數據檔的目錄
user@pc:~$ sudo mkdir -p /mosquitto/log # 存放日誌檔的目錄

建立檔案

/mosquitto/config/mosquitto.conf

1
2
3
4
5
6
7
8
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

allow_anonymous true

listener 1883
protocol mqtt
  • persistencetrue 為開啟資料存放在地端,讓通訊更穩定。可參考詳細說明 How to set up persistent storage for Mosquitto MQTT broker
  • persistence_location:指定資料存放於 /mosquitto/data/ 目錄下。
  • log_dest file:Log 會被寫到 /mosquitto/log/mosquitto.log 檔案內。
  • allow_anonymous:在 Mosquitto 2.0 之後預設不允許匿名使用者登入,因此要設定此選項為 true 才可匿名登入。
  • listener:掛載服務於 1883 Port。
  • protocol:1883 Port 的服務為 MQTT。

運行 MQTT Container

啟動 Container

啟動 eclipse-mosquitto 的 Image,並查看運行中的 Container 確定有正常運作 (STATUS 沒有異常訊息)

1
2
3
4
5
6
7
user@pc:~$ sudo docker run -d --name mqtt -p 1883:1883 \
-v /mosquitto:/mosquitto -v /mosquitto/data:/mosquitto/data \
-v /mosquitto/log:/mosquitto/log eclipse-mosquitto

user@pc:~$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
76612c251fa2 eclipse-mosquitto "/docker-entrypoint.…" 5 minutes ago Up 2 minutes 0.0.0.0:1883->1883/tcp mqtt
  • -d:啟動後於後台工作。
  • --name:幫此 Container 取名為 mqtt
  • p:Port 映射 host port:container port
  • v:Volumn 映射 host directory:contrainer directory

到目前為止,初步的設定已經完成,並已讓 Mosquitto Container 運行起來,且沒有出現錯誤訊息。

測試 MQTT Broker

若要測試 Broker 功能是否正常,要使用到 mosquitto_submosquitto_pub 兩個指令,因此需先安裝 mosquitto-clients

1
user@pc:~$ sudo apt-get install mosquitto-clients

開啟一個新的 Terminal (在此稱 tty1),輸入指令來訂閱主題

1
user@pc:~$ mosquitto_sub -h localhost -t Try/MQTT/Docker
  • mosquitto_sub:訂閱主題。
  • -h:指定 MQTT Broker 的 IP 或 FQDN,此為 localhost
  • -t:指定訂閱的主題名稱,此為 Try/MQTT/Docker

開啟另一個新的 Terminal (在此稱 tty2),輸入指令來發布主題

1
user@pc:~$ mosquitto_pub -h localhost -t Try/MQTT/Docker -m "Test Message"
  • mosquitto_pub:發布主題。
  • -h:指定 MQTT Broker 的 IP 或 FQDN,此為 localhost
  • -t:指定發布的主題,此為 Try/MQTT/Docker
  • -m:發布的訊息,此為 Test Message

這時在 tty1 應該就能看到 Test Message 的訊息,代表 Mosquitto Container 運行正常且 MQTT Broker 功能可以使用!

使用帳密檔

建立帳密檔

建立帳密檔需使用到 mosquitto_passwd 指令,因此需先安裝 mosquitto,安裝結束時可能會看到錯誤訊息,這錯誤是因為當 mosquitto 安裝好後預設會自動啟動服務且 Port 為 1883,與我們的 Docker 相衝突,因此不需理會。除此之外,這裡我們只是要拿包含在 mosquittomosquitto_passwd 指令來使用,所以必須將 mosquitto 的服務關閉,避免占用我們 Docker 要使用的 Port

1
2
3
4
5
6
7
8
user@pc:~$ sudo apt-get install mosquitto
...
...
Job for mosquitto.service failed because the control process exited with error code.
See "systemctl status mosquitto.service" and "journalctl -xe" for details. # 此錯誤可忽略

user@pc:~$ sudo systemctl stop mosquitto.service
user@pc:~$ sudo systemctl disable mosquitto.service

安裝好就可以創建帳密檔

1
2
3
4
5
6
user@pc:~$ sudo mosquitto_passwd -c /mosquitto/config/passwd_file joker # 要創建的使用者名稱為 joker
Password: # 1234
Reenter password: # 1234

user@pc:~$ cat /mosquitto/config/passwd_file # 查看內容
joker:$6$9XBw6q7Iq1alMR6w$576QauwU5YTieVS+PPjrDD3a5UDolccQ0JwArFvcohYQe1XeEIxJlw0vXQCV0sN30+EoipWi1ng39GNHXl/PtQ==
  • -c:將會新建一個帳密檔,會覆蓋現有的檔案。若只是想新增使用者到已經存在的帳密檔,不加此選項即可。
  • /mosquitto/config/passwd_file:帳密檔的絕對路徑。
  • joker:要創建的使用者名稱。

讀取帳密檔

建立好之後,需修改 /mosquitto/config/mosquitto.config,讓 Mosquitto 知道要去讀取帳密檔,並且取消匿名使用者的登入權限 (匿名登入權限依應用情境而定)

1
2
3
4
5
6
7
8
9
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

allow_anonymous false # 這裡要改,取消匿名使用者的登入權限
password_file /mosquitto/config/passwd_file # 這裡要加,讓 Mosquitto 知道要去讀取帳密檔

listener 1883
protocol mqtt

重新運行 Container,使 Mosquitto 讀取的設定檔為最新的內容,使用帳號才能發布 (mosquitto_pub) 或訂閱 (mosquitto_sub)

1
user@pc:~$ sudo docker restart mqtt

測試帳密訂閱與發布

開啟一個新的 Terminal (在此稱 tty1),使用指令來訂閱主題。但由於我們已經設定為不可匿名登入,可以預想到的是會無法正常運作,因為沒有授權 (也就是沒有指定帳號)

1
2
user@pc:~$ mosquitto_sub -h localhost -t Try/MQTT/Docker
Connection error: Connection Refused: not authorised.

tty1 中的指令更改如下

1
user@pc:~$ mosquitto_sub -h localhost -t Try/MQTT/Docker -u joker -P 1234
  • -u:指定使用者帳號。
  • -P:指定密碼,注意是大寫 P。

開啟另一個新的 Terminal (在此稱 tty2)

1
user@pc:~$ mosquitto_pub -h localhost -t Try/MQTT/Docker -m "Test Message" -u joker -P 1234 

這時在 tty1 應該就能看到 Test Message 的訊息,代表帳號密碼機制設定完成並且有正常運作!

使用 ACL 檔

建立 ACL 檔

ACL (Access Control List) 存取控制表是用來描述一個物件對一串列的存取權限,Mosquitto 同樣也提供這個功能。第一步與帳密檔同樣,創建一個檔案 /mosquitto/config/acl,內容如下

1
2
user joker
topic readwrite joker/#
  • 使用者 joker 只可以讀寫符合 pattern 為 joker/# 的 topic,其中 # 為多層級 WildCard 字元 (+ 為單一層級),因此 jokerjoker/Tryjoker/Try/MQTT 都是符合的 topic。

讀取 ACL 檔

修改 /mosquitto/config/mosquitto.config,讓 Mosquitto 知道要去讀取 ACL 檔

1
2
3
4
5
6
7
8
9
10
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

allow_anonymous false
password_file /mosquitto/config/passwd_file
acl_file /mosquitto/config/acl # 加上這行

listener 1883
protocol mqtt

重新運行 Container,使 Mosquitto 讀取的設定檔為最新的內容,知道 ACL 檔案的存在,這時 ACL 功能就正在運行囉

1
user@pc:~$ sudo docker restart mqtt

若嘗試以無權限的 topic (例如:john) 進行發布與訂閱,會發現雖然沒有出現錯誤訊息 (例如:權限不足),但真的發送不出去,也接收不到的哦!

WebSocket MQTT

啟用 WebSocket MQTT

WebSocket 是一種可在單個 TCP 連線上全雙工的通訊協定,只需要交握一次,便能建立永久的連線,進行雙向資料傳輸。對於需要低功耗的 IoT 應用來說,透過 WebSocket 使用 MQTT 的訂閱與發佈可以比傳統單純只使用 MQTT 來的節能,因此知道如何開啟 Mosquitto 的 WebSocket 通訊是必要的。

修改 /mosquitto/config/mosquitto.config

1
2
3
4
5
6
7
8
9
10
11
12
13
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

allow_anonymous false
password_file /mosquitto/config/passwd_file
acl_file /mosquitto/config/acl

listener 1883
protocol mqtt

listener 9001 # 在 Port 9001
protocol websockets # 提供 WebSocket 服務
  • 一般 MQTT 協定在 1883 Port 服務
  • WebSocket MQTT 在 9001 Port 服務

刪除正在運行的 MQTT Container,並建立一個比原先多綁定 Port 9001 的 Container

1
2
3
4
5
6
7
8
9
10
user@pc:~$ sudo docker rm -f mqtt

user@pc:~$ sudo docker run -d --name mqtt -p 1883:1883 -p 9001:9001 \
-v /mosquitto:/mosquitto -v /mosquitto/data:/mosquitto/data \
-v /mosquitto/log:/mosquitto/log eclipse-mosquitto

user@pc:~$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f955f94c7f6e eclipse-mosquitto "/docker-entrypoint.…" 4 minutes ago Up 2 minutes 0.0.0.0:1883->1883/tcp, 0.0.0.0:9001->9001/tcp mqtt

測試 WebSocket MQTT

WebSocket MQTT 不像 MQTT 可使用 mosquitto_pub/mosquitto_sub 進行測試,因為這兩個指令不支援 WebSocket 通訊協定。這裡要介紹一個 MQTT 開發的好朋友 MQTTBox,MQTTBox 有 Chrome 瀏覽器的擴充套件版本,因此只要有 Chrome 瀏覽器就可以安裝使用!安裝 MQTTBox 並將其開啟,新建一個 MQTT Client,只需填寫 MQTT Client NameProtocolHostUsernamePassword 五個欄位即可,其中因為我們要測試的是 WS MQTT 協定,因此 Protocol 要選擇 ws

WS MQTT 連線設定WS MQTT 連線設定

建立好並存檔,同時確認畫面上方 Connected 按鈕為綠色的,表示連線正常,這時就可以來發布/訂閱測試啦!在 Topic to publishTopic to subscribe 兩個欄位填寫相同的主題名稱,先訂閱再發布,就會再訂閱區塊中看到發布的訊息囉!

WS MQTT 訂閱與發佈測試WS MQTT 訂閱與發佈測試

確認 WS MQTT 可以正常運行!

MQTTS 與 WSS

Mosquitto 有提供 MQTTS 與 WSS 通訊協定,協定名稱中最後一個字母 S 皆代表 SSL,SSL 能使我們的通訊透過加密防止中間人或竊聽攻擊,要使用 SSL 必須先擁有憑證,然而申請一個正式憑證需要較多手續與耗費許多時日。因此在測試環境中我們可以先自簽憑證,測試加上 SSL 後系統是否能正常運作,若之後系統要上線,再替換為正式的憑證即可。

自簽憑證

詳細步驟與說明可以參考 [SSL] 自簽憑證過程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 創建目錄
user@pc:~$ mkdir root
user@pc:~$ mkdir server

# CA 產 CA 的自簽憑證
user@pc:~$ openssl genrsa -out root/ca.key 4096
user@pc:~$ openssl req -new -x509 -days 365 -subj "/C=TW/CN={這裡任意}" \
-key root/ca.key -out root/ca.crt

# 簽 Server 憑證
user@pc:~$ openssl genrsa -out server/server.key 2048
user@pc:~$ openssl req -new -key server/server.key -subj "/C=TW/CN={Server IP 或 FQDN}" \
-out server/server.csr
user@pc:~$ openssl x509 -req -CAcreateserial -days 30 -sha256 -CA root/ca.crt \
-CAkey root/ca.key -in server/server.csr -out server/server.crt
  • 最最最需要注意的是 CA 的 CN 與 Server 的 CN 不能一樣,否則會出現意想不到的錯誤。

ca.crtserver.keyserver.crt 移至 /mosquitto/config/certs/ 目錄下。

1
2
3
4
5
6
7
8
# 創建目錄
user@pc:~$ sudo mkdir /mosquitto/config/certs

# 移動憑證
user@pc:~$ sudo cp root/ca.crt /mosquitto/config/certs/
user@pc:~$ sudo cp server/server.key /mosquitto/config/certs/
user@pc:~$ sudo cp server/server.crt /mosquitto/config/certs/

啟用 MQTTS 與 WSS

修改 /mosquitto/config/mosquitto.config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

allow_anonymous false
password_file /mosquitto/config/passwd_file
acl_file /mosquitto/config/acl

listener 1883
protocol mqtt

listener 9001
protocol websockets

listener 8883 # 在 Port 8883
protocol mqtt # 服務 MQTTS
keyfile /mosquitto/config/certs/server.key # Server 的私鑰
certfile /mosquitto/config/certs/server.crt # Server 的憑證
cafile /mosquitto/config/certs/ca.crt # CA 的憑證,若不驗證 client 可以不指定 cafile

listener 8884 # 在 Port 8884
protocol websockets # 服務 WSS MQTT
keyfile /mosquitto/config/certs/server.key # Server 的私鑰
certfile /mosquitto/config/certs/server.crt # Server 的憑證
cafile /mosquitto/config/certs/ca.crt # CA 的憑證,若不驗證 client 可以不指定 cafile

require_certificate false # 這行是不驗證 client 端的憑證

刪除正在運行的 MQTT Container,並建立一個比原先多綁定 Port 8883 與 8884 的 Container

1
2
3
4
user@pc:~$ sudo docker rm -f mqtt
user@pc:~$ sudo docker run -d --name mqtt -p 1883:1883 -p 9001:9001 -p 8883:8883 -p 8884:8884 \
-v /mosquitto:/mosquitto -v /mosquitto/data:/mosquitto/data \
-v /mosquitto/log:/mosquitto/log eclipse-mosquitto

測試 MQTTS

MQTTS 可使用指令來測試

開啟一個新的 Terminal (在此稱 tty1),使用指令來訂閱主題

1
2
user@pc:~$ mosquitto_sub --cafile /mosquitto/config/certs/ca.crt -t joker/Try/MQTT -h localhost \
-p 8883 -u joker -P 1234 --insecure
  • --cafile:CA 的憑證。
  • --insecure:不要驗證憑證合法性。使用此選項是因為這裡我們使用的是自簽憑證,若驗證憑證合法性一定有很多問題會出現,我們的目的只是想先測試 MQTTS 是否可以正常運作,因此不需驗證憑證合法性。
  • -p:指定要連接的 Port,若沒指定預設為 1883 Port。

開啟另一個新的 Terminal (在此稱 tty2)

1
2
user@pc:~$ mosquitto_pub --cafile /mosquitto/config/certs/ca.crt -t joker/Try/MQTT -m "Test Message" -h localhost \ 
-p 8883 -u joker -P 1234 --insecure

這時在 tty1 應該就能看到 Test Message 的訊息,代表 MQTTS 設定完成並且有正常運作!

測試 WSS MQTT

WSS 的測試與 WS 測試相同,利用 MQTTBox 來做測試,但是擴充套件的似乎無法處理 SSL 的部分,不管我如何設定都會出現連線錯誤,因此我使用另一台電腦 (Windows 10) 安裝 MQTTBox 本體來測試連線,WSS 連線設定與 WS 不同的一點是多了 SSL/TLS VersionSSL/TLS Certificate Type 欄位,只需更動 Type 選擇 CA Signed server certificate 即可

WSS MQTT 連線設定WSS MQTT 連線設定

WSS MQTT 訂閱與發佈測試WSS MQTT 訂閱與發佈測試

至此, MQTT/MQTTS/WS/WSS 四個通訊協定都已經正常運作,最終選擇所要使用的通訊協定還是要回歸到應用情境,若沒用到的 Port 可以選擇關閉以釋放系統資源。

結語

這篇文章記錄了如何使用 Docker 輕鬆架設 MQTT Broker (Mosquitto),並且使用身分驗證、ACL 權限控管,啟動 MQTT/MQTTS/WS/WSS 四個協定。當然,還有更多的進階設定沒有在此篇文章中紀載,但架設伺服器的第一步,就是先將基本設定完善,並確認能夠運行,才能繼續下一步的調教。

很高興能在這裡幫助到您,歡迎登入 Liker 為我鼓掌 5 次,或者成為我的讚賞公民,鼓勵我繼續創造優質文章。
以最優質的內容回應您的鼓勵