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.crtとclient.crt、client.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に送信する。ユーザー名とパスワードによるユーザー認証を行うにはUSER、PASSWORD、mqttc.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上に構築することができた。
Discussion