Files
Qris-Soundbox/18-mqtt-broker-mosquitto-debian13.md
2026-06-07 00:44:54 +07:00

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 8883 setelah 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-backend dipakai backend platform.
  • Username device memakai device_id UUID dari platform, supaya ACL pattern bisa mengikat topic devices/{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

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 bisa dipakai untuk membatasi tiap device hanya membaca 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.1 untuk local-only;
  • listener 1883 0.0.0.0 untuk pilot public non-TLS.

Catatan Debian:

  • Jangan set ulang persistence, persistence_location, log_dest, atau log_type di conf.d/qris.conf jika sudah ada di /etc/mosquitto/mosquitto.conf.
  • Jika muncul error Duplicate persistence_location value, hapus persistence dan persistence_location dari qris.conf.
  • Jika muncul error Duplicate "log_dest file" value, hapus blok logging dari qris.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:8883
  • 127.0.0.1:1883 untuk local-only, atau 0.0.0.0:1883 untuk 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

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 dengan device_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 pattern bergantung pada username yang sama dengan device_id; jangan mengganti username device menjadi device_code kecuali 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_messages tetap menjadi outbox/trace awal di aplikasi.
  • Benchmark sebelum milestone 20k, 50k, dan 100k connected device.