🦔

AWS IoT Coreの代用になるMQTTブローカをUbuntuで作成する

に公開

動機

  • ESP32(M5Stack)からのセンサ計測データをこれまではAWSのIoT CoreとDynamoDBを使って保存してきたが、外部のサービスを使わずに実現したくなったため。
  • Oracle Cloud Infrastructure(OCI)で無料で使える仮想マシンを手に入れたのでそこにMQTTサーバーを構築できれば何かと使えそう

MQTTブローカへの要求事項

AWS IoT Coreとの互換性を取るため構築するMQTTブローカには以下の事項を要求

  • TLS対応(今回はRoot CAを自前で作成)
  • クライアント証明書の必須化
  • ログイン認証(ユーザー名、パスワード)は不要とする

動作環境

  • Ubuntu 24.04 x 2

今回はOCI上にインスタンスを2つ作成して検証を行った

事前準備

いつもの

sudo apt update -y
sudo apt upgrade -y

OpenSSL、MQTTブローカ、MQTTクライアントのインストールと起動

sudo apt-get install openssl mosquitto mosquitto-clients -y
sudo systemctl start mosquitto
sudo systemctl enable mosquitto

証明書、秘密鍵、公開鍵の作成

証明書、鍵用のディレクトリを作成し、そこで作業を行う

mkdir ~/certs
cd ~/certs

認証機関(CA)用ファイル

CAの秘密鍵とルート証明書を生成。ここでは証明書の有効期限を100年に設定している。

openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 36500 -key ca.key -out ca.crt

いくつか質問がでてくるが、基本的には空で良い。ただし、Common Nameはこれ以降作成するサーバー証明書やクライアント証明書とは異なる名前に設定すること。

サーバー証明書

openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 36500

クライアント証明書

openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 36500

ファイルのコピー、MQTTブローカの設定

デフォルトでは生成された秘密鍵(.key)のパーミッションが600になっており、これではサービス側が秘密鍵を読み込めないので所有者以外にファイルのRead権限を追加する必要がある。

chmod 644 server.key

mosquittoの鍵配置用フォルダにCAとサーバーの証明書、秘密鍵をコピーする。

sudo cp ca.crt server.crt server.key /etc/mosquitto/certs/

次に、/etc/mosquitto/mosquitto.confに次を追記する。なお、ポート番号はMQTTで1883、MQTTSで8883がデフォルトになっている。

# Allow no username and passwd
allow_anonymous true

# Enable TLS
listener 8883

# Set CA, Server certificate, Server private key
cafile    /etc/mosquitto/certs/ca.crt
certfile  /etc/mosquitto/certs/server.crt
keyfile   /etc/mosquitto/certs/server.key

# Require certificate from clients
require_certificate true

mosquittoを再起動して設定を反映させる。

sudo systemctl restart mosquitto

接続するクライアントにはca.crtclient.crtclient.keyを共有しておく。

テスト(mosquitto-clients)

MQTTブローカと異なるネットワークから接続する場合は8883番ポートを開放するのを忘れずに。

Publish側の例

mosquitto_pub -h localhost -p 8883 --cafile ~/certs/ca.crt --cert ~/certs/client.crt --key ~/certs/client.key -t test -m "hello tls" -d

Subscribe側の例

mosquitto_sub -h localhost -p 8883 --cafile ~/certs/ca.crt --cert ~/certs/client.crt --key ~/certs/client.key -t test -d

テスト(paho-mqtt)

Pythonで接続する例

あらかじめpaho-mqttをインストールしておく

pip install paho-mqtt

以下の例では3秒ごとにPublish側から乱数をトピックtestに送信する。ユーザー名とパスワードによるユーザー認証を行うにはUSERPASSWORDmqttc.username_pw_set(username=USER, password=PASSWORD)の行をコメントアウトする。

Subscribe側(HOSTにMQTTブローカのIPかDNSを指定。証明書などのパスは各自の環境に応じて変更)

import paho.mqtt.client as mqtt
import ssl

# MQTT Broker
HOST = 'Set HOST IP or DNS here'
PORT = 8883
KEEP_ALIVE = 60
TOPIC = 'test'
# USER = 'testuser'
# PASSWORD = 'password'
CA_CERT = '/home/ubuntu/certs/ca.crt'
DEVICE_CERT = '/home/ubuntu/certs/client.crt'
DEVICE_KEY = '/home/ubuntu/certs/client.key'

def on_connect(mqttc, obj, flags, rc):
    print('connected: ',rc)

def on_message(mqttc, obj, msg):
    print(msg.topic + ' ' + str(msg.qos) + ' ' + str(msg.payload.decode('utf-8')))

# Connect to MQTT broker
mqttc = mqtt.Client(protocol=mqtt.MQTTv311)
mqttc.on_connect = on_connect
mqttc.on_message = on_message

mqttc.tls_set(ca_certs=CA_CERT, certfile=DEVICE_CERT, keyfile=DEVICE_KEY)
# mqttc.username_pw_set(username=USER, password=PASSWORD)
mqttc.tls_insecure_set(True)

mqttc.connect(HOST,PORT,KEEP_ALIVE)
mqttc.subscribe(TOPIC)
mqttc.loop_forever()

Publish側(HOSTにMQTTブローカのIPかDNSを指定。証明書などのパスは各自の環境に応じて変更)

import paho.mqtt.client as mqtt
import time
import random
import ssl

# MQTT Broker
HOST = 'Set HOST IP or DNS here'
PORT = 8883
KEEP_ALIVE = 60
TOPIC = 'test'
# USER = 'testuser'
# PASSWORD = 'password'
CA_CERT = '/home/ubuntu/certs/ca.crt'
DEVICE_CERT = '/home/ubuntu/certs/client.crt'
DEVICE_KEY = '/home/ubuntu/certs/client.key'

def on_connect(mqttc, obj, flags, rc):
    print('connected: ',rc)

# Connect to MQTT broker
mqttc = mqtt.Client()
mqttc.on_connect = on_connect
mqttc.tls_set(ca_certs=CA_CERT, certfile=DEVICE_CERT, keyfile=DEVICE_KEY)
mqttc.tls_insecure_set(True)
# mqttc.username_pw_set(username=USER, password=PASSWORD)
mqttc.connect(HOST,PORT,KEEP_ALIVE)

while True:
    test_data1 = random.randint(0, 100)
    test_data2 = random.randint(0, 100)
    test_data3 = random.randint(0, 100)
    mqttc.publish(TOPIC, f'{{"test_data1": {test_data1}, "test_data2": {test_data2}, "test_data3": {test_data3},}}')
    print('publish')
    time.sleep(3)

以上によりAWS IoT Coreと同様なMQTTブローカをUbuntu上に構築することができた。

参考にしたもの

GitHubで編集を提案

Discussion