10 KiB
MQTT Broker Mosquitto on Debian 13
Panduan operasional untuk menyiapkan broker MQTT awal platform QRIS Soundbox di Debian 13 dengan subdomain broker.bizone.id.
Keputusan arsitektur terkait:
D-026: broker MQTT sungguhan ditunda sampai infrastruktur siap; simulator/outbox tetap dipakai selama transisi.D-027: broker awal memakai Mosquitto, dengan kontrak topic dan adapter backend tetap migration-ready ke EMQX/managed MQTT.
Target Setup
- Broker: Eclipse Mosquitto.
- Domain:
broker.bizone.id. - MQTT TLS publik:
8883/tcp. - MQTT non-TLS pilot:
1883/tcp, default disarankan local-only; boleh dibuka publik sementara jika firmware device belum support SSL/TLS. - TLS: Let's Encrypt.
- Auth: username/password.
- Authorization: ACL topic per user/device.
- Anonymous access: disabled.
DNS dan Paket
Pastikan DNS broker.bizone.id sudah mengarah ke public IP server.
dig +short broker.bizone.id
curl -4 ifconfig.me
Install paket:
sudo apt update
sudo apt install -y mosquitto mosquitto-clients certbot ufw
sudo systemctl enable --now mosquitto
Firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 8883/tcp
sudo ufw enable
sudo ufw status verbose
Default paling aman: jangan buka 1883/tcp ke internet. Listener 1883 cukup untuk localhost/internal test.
Jika firmware device belum support SSL/TLS dan harus memakai MQTT non-SSL, port 1883/tcp boleh dibuka untuk pilot dengan risiko credential MQTT lewat clear-text. Pastikan:
allow_anonymous false;- password kuat dan unik;
- ACL aktif;
- tidak memakai credential admin/backend untuk device;
- segera pindah ke
8883setelah firmware support TLS.
Untuk pilot public non-TLS:
sudo ufw allow 1883/tcp
sudo ufw status verbose
Jika sumber IP device bisa diprediksi, batasi firewall:
sudo ufw allow from <DEVICE_OR_NAT_PUBLIC_IP> to any port 1883 proto tcp
Sertifikat TLS
Ambil sertifikat Let's Encrypt:
sudo certbot certonly --standalone -d broker.bizone.id
Copy sertifikat ke lokasi yang bisa dibaca Mosquitto:
sudo install -d -o root -g mosquitto -m 750 /etc/mosquitto/certs
sudo install -o root -g mosquitto -m 640 \
/etc/letsencrypt/live/broker.bizone.id/fullchain.pem \
/etc/mosquitto/certs/fullchain.pem
sudo install -o root -g mosquitto -m 640 \
/etc/letsencrypt/live/broker.bizone.id/privkey.pem \
/etc/mosquitto/certs/privkey.pem
Buat renewal hook:
sudo nano /etc/letsencrypt/renewal-hooks/deploy/mosquitto-cert-copy.sh
Isi:
#!/usr/bin/env bash
set -euo pipefail
DOMAIN="broker.bizone.id"
install -o root -g mosquitto -m 640 \
"/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" \
/etc/mosquitto/certs/fullchain.pem
install -o root -g mosquitto -m 640 \
"/etc/letsencrypt/live/${DOMAIN}/privkey.pem" \
/etc/mosquitto/certs/privkey.pem
systemctl reload mosquitto
Aktifkan:
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/mosquitto-cert-copy.sh
User dan Password
Buat user backend dan device test:
sudo mosquitto_passwd -c /etc/mosquitto/passwd qris-backend
sudo mosquitto_passwd /etc/mosquitto/passwd DEVICE_UUID_FROM_PLATFORM
sudo chown root:mosquitto /etc/mosquitto/passwd
sudo chmod 640 /etc/mosquitto/passwd
Rekomendasi:
qris-backenddipakai backend platform.- Username device memakai
device_idUUID dari platform, supaya ACLpatternbisa mengikat topicdevices/{deviceId}/...ke user/device.
ACL Topic
Buat file ACL:
sudo nano /etc/mosquitto/acl
Isi awal:
user qris-backend
topic readwrite devices/#
topic readwrite soundbox/#
pattern write devices/%u/uplink/#
pattern read devices/%u/downlink/#
pattern write devices/%u/heartbeat
pattern read soundbox/%u/down
pattern write soundbox/%u/down/heartbeat
Untuk firmware QF100 sample saat ini, config server mengembalikan topic berbasis serial number:
soundbox/{dev-sn}/down
Jika masih memakai user MQTT bersama qris-backend untuk pilot, rule topic readwrite soundbox/# wajib ada. Jika nanti per-device credential memakai username sama dengan dev-sn, rule pattern read soundbox/%u/down dan pattern write soundbox/%u/down/heartbeat bisa dipakai untuk membatasi tiap device hanya membaca/publish heartbeat topic miliknya sendiri.
Permission:
sudo chown root:mosquitto /etc/mosquitto/acl
sudo chmod 640 /etc/mosquitto/acl
Konfigurasi Mosquitto
Buat file:
sudo nano /etc/mosquitto/conf.d/qris.conf
Isi minimal:
per_listener_settings true
listener 8883 0.0.0.0
protocol mqtt
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
certfile /etc/mosquitto/certs/fullchain.pem
keyfile /etc/mosquitto/certs/privkey.pem
listener 1883 127.0.0.1
protocol mqtt
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
Untuk device yang belum support SSL/TLS dan harus connect dari internet, ubah listener 1883 menjadi publik:
listener 1883 0.0.0.0
protocol mqtt
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
Jangan jalankan dua listener 1883 sekaligus. Pilih salah satu:
listener 1883 127.0.0.1untuk local-only;listener 1883 0.0.0.0untuk pilot public non-TLS.
Catatan Debian:
- Jangan set ulang
persistence,persistence_location,log_dest, ataulog_typediconf.d/qris.confjika sudah ada di/etc/mosquitto/mosquitto.conf. - Jika muncul error
Duplicate persistence_location value, hapuspersistencedanpersistence_locationdariqris.conf. - Jika muncul error
Duplicate "log_dest file" value, hapus blok logging dariqris.conf.
Test config:
sudo mosquitto -c /etc/mosquitto/mosquitto.conf -v
Jika tidak ada error, tekan Ctrl+C, lalu restart service:
sudo systemctl restart mosquitto
sudo systemctl status mosquitto --no-pager
Pastikan listener terbuka:
sudo ss -lntp | grep mosquitto
Expected:
0.0.0.0:8883127.0.0.1:1883untuk local-only, atau0.0.0.0:1883untuk pilot public non-TLS.
Test Publish Subscribe
Terminal 1, subscribe sebagai backend:
mosquitto_sub \
-h broker.bizone.id \
-p 8883 \
-u qris-backend \
-P 'PASSWORD_BACKEND' \
-t 'devices/DEVICE_UUID_FROM_PLATFORM/uplink/#' \
-v
Terminal 2, publish sebagai device:
mosquitto_pub \
-h broker.bizone.id \
-p 8883 \
-u DEVICE_UUID_FROM_PLATFORM \
-P 'PASSWORD_DEVICE' \
-t 'devices/DEVICE_UUID_FROM_PLATFORM/uplink/dynamic-qr/request' \
-m '{"request_id":"test-001","amount":10000}'
Test ACL negatif:
mosquitto_pub \
-h broker.bizone.id \
-p 8883 \
-u DEVICE_UUID_FROM_PLATFORM \
-P 'PASSWORD_DEVICE' \
-t 'devices/OTHER_DEVICE_UUID/uplink/dynamic-qr/request' \
-m '{}'
Pesan ke topic device lain harus ditolak atau tidak sampai ke subscriber.
Test Non-TLS 1883
Jika port 1883 dibuka untuk device non-SSL, test tanpa parameter TLS:
Terminal 1, subscribe sebagai backend:
mosquitto_sub \
-h broker.bizone.id \
-p 1883 \
-u qris-backend \
-P 'PASSWORD_BACKEND' \
-t 'devices/DEVICE_UUID_FROM_PLATFORM/uplink/#' \
-v
Terminal 2, publish sebagai device:
mosquitto_pub \
-h broker.bizone.id \
-p 1883 \
-u DEVICE_UUID_FROM_PLATFORM \
-P 'PASSWORD_DEVICE' \
-t 'devices/DEVICE_UUID_FROM_PLATFORM/uplink/dynamic-qr/request' \
-m '{"request_id":"test-1883","amount":10000}'
Jika device memakai config server /speaker/dev-config, set app server agar response MQTT ke device memakai port 1883:
QF100_MQTT_BROKER_HOST=broker.bizone.id
QF100_MQTT_BROKER_PORT=1883
QF100_MQTT_USERNAME=qris-backend
QF100_MQTT_PASSWORD=...
Response config device akan mengirim topic:
{
"mqtt": {
"client-id": "soundbox-DEVICE_SN",
"subscribe-topic": "soundbox/DEVICE_SN/down",
"publish-topic": "soundbox/DEVICE_SN/up"
}
}
Jika firmware tidak bisa resolve domain, isi QF100_MQTT_BROKER_HOST dengan IP broker:
dig +short broker.bizone.id
Monitoring
sudo journalctl -u mosquitto -f
sudo tail -f /var/log/mosquitto/mosquitto.log
sudo ss -lntp | grep mosquitto
Test renewal TLS:
sudo certbot renew --dry-run
Environment Backend Nanti
Saat adapter broker sungguhan dipasang ke platform:
MQTT_PUBLISH_MODE=broker
MQTT_BROKER_URL=mqtts://broker.bizone.id:8883
MQTT_USERNAME=qris-backend
MQTT_PASSWORD=...
MQTT_CLIENT_ID=qris-platform-backend
MQTT_CONNECT_TIMEOUT_MS=5000
MQTT_TLS=true
Backend sebaiknya tetap memakai TLS 8883. Untuk device non-SSL, cukup ubah env khusus response config device:
QF100_MQTT_BROKER_HOST=broker.bizone.id
QF100_MQTT_BROKER_PORT=1883
Topic kontrak yang harus dipertahankan:
devices/{deviceId}/uplink/dynamic-qr/request
devices/{deviceId}/downlink/dynamic-qr/response
devices/{deviceId}/downlink/payment/success
devices/{deviceId}/downlink/config/push
devices/{deviceId}/uplink/config/ack
devices/{deviceId}/heartbeat
soundbox/{dev-sn}/down
soundbox/{dev-sn}/up
soundbox/{dev-sn}/down/heartbeat
Provisioning Credential Device
Credential MQTT device dibuat dari aplikasi, lalu password satu kali tersebut dimasukkan ke Mosquitto. Jalankan backend platform lebih dulu, lalu rotate credential:
ADMIN_TOKEN=admin-dev-token \
npm run mqtt:provision-device -- \
--base-url http://127.0.0.1:3000 \
--device-id DEVICE_UUID_FROM_PLATFORM
Output berisi:
mqtt_username: sama dengandevice_id.mqtt_password: secret satu kali untuk dimasukkan ke broker dan device.mosquitto_commands: perintah yang bisa dijalankan di server broker.
Jika script dijalankan langsung di host broker:
ADMIN_TOKEN=admin-dev-token \
npm run mqtt:provision-device -- \
--base-url http://127.0.0.1:3000 \
--device-id DEVICE_UUID_FROM_PLATFORM \
--apply-local
Setelah password dimasukkan, reload Mosquitto:
sudo systemctl reload mosquitto
Catatan penting:
- Platform hanya menyimpan fingerprint secret, bukan plaintext password.
- Jika password hilang, lakukan rotate ulang dan update device + broker.
- ACL
patternbergantung pada username yang sama dengandevice_id; jangan mengganti username device menjadidevice_codekecuali kontrak topic/ACL ikut diubah.
Migration Ready Notes
- Jangan mengunci business logic di fitur Mosquitto-specific.
- Credential device tetap dikelola domain platform.
- Topic tetap
devices/{deviceId}/.... mqtt_messagestetap menjadi outbox/trace awal di aplikasi.- Benchmark sebelum milestone 20k, 50k, dan 100k connected device.