# MQTT Broker Mosquitto on Debian 13 Panduan operasional untuk menyiapkan broker MQTT awal platform QRIS Soundbox di Debian 13 dengan subdomain `mqtt.iptek.co`. 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: `mqtt.iptek.co`. - MQTT TLS publik: `8883/tcp`. - MQTT local-only: `1883/tcp` pada `127.0.0.1`. - TLS: Let's Encrypt. - Auth: username/password. - Authorization: ACL topic per user/device. - Anonymous access: disabled. ## DNS dan Paket Pastikan DNS `mqtt.iptek.co` sudah mengarah ke public IP server. ```bash dig +short mqtt.iptek.co curl -4 ifconfig.me ``` Install paket: ```bash sudo apt update sudo apt install -y mosquitto mosquitto-clients certbot ufw sudo systemctl enable --now mosquitto ``` ## Firewall ```bash 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 ``` Jangan buka `1883/tcp` ke internet. Listener `1883` hanya untuk localhost/internal test. ## Sertifikat TLS Ambil sertifikat Let's Encrypt: ```bash sudo certbot certonly --standalone -d mqtt.iptek.co ``` Copy sertifikat ke lokasi yang bisa dibaca Mosquitto: ```bash sudo install -d -o root -g mosquitto -m 750 /etc/mosquitto/certs sudo install -o root -g mosquitto -m 640 \ /etc/letsencrypt/live/mqtt.iptek.co/fullchain.pem \ /etc/mosquitto/certs/fullchain.pem sudo install -o root -g mosquitto -m 640 \ /etc/letsencrypt/live/mqtt.iptek.co/privkey.pem \ /etc/mosquitto/certs/privkey.pem ``` Buat renewal hook: ```bash sudo nano /etc/letsencrypt/renewal-hooks/deploy/mosquitto-cert-copy.sh ``` Isi: ```bash #!/usr/bin/env bash set -euo pipefail DOMAIN="mqtt.iptek.co" 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: ```bash sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/mosquitto-cert-copy.sh ``` ## User dan Password Buat user backend dan device test: ```bash 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: ```bash sudo nano /etc/mosquitto/acl ``` Isi awal: ```conf user qris-backend topic readwrite devices/# pattern write devices/%u/uplink/# pattern read devices/%u/downlink/# pattern write devices/%u/heartbeat ``` Permission: ```bash sudo chown root:mosquitto /etc/mosquitto/acl sudo chmod 640 /etc/mosquitto/acl ``` ## Konfigurasi Mosquitto Buat file: ```bash sudo nano /etc/mosquitto/conf.d/qris.conf ``` Isi minimal: ```conf 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 ``` 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: ```bash sudo mosquitto -c /etc/mosquitto/mosquitto.conf -v ``` Jika tidak ada error, tekan `Ctrl+C`, lalu restart service: ```bash sudo systemctl restart mosquitto sudo systemctl status mosquitto --no-pager ``` Pastikan listener terbuka: ```bash sudo ss -lntp | grep mosquitto ``` Expected: - `0.0.0.0:8883` - `127.0.0.1:1883` ## Test Publish Subscribe Terminal 1, subscribe sebagai backend: ```bash mosquitto_sub \ -h mqtt.iptek.co \ -p 8883 \ -u qris-backend \ -P 'PASSWORD_BACKEND' \ -t 'devices/DEVICE_UUID_FROM_PLATFORM/uplink/#' \ -v ``` Terminal 2, publish sebagai device: ```bash mosquitto_pub \ -h mqtt.iptek.co \ -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: ```bash mosquitto_pub \ -h mqtt.iptek.co \ -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. ## Monitoring ```bash sudo journalctl -u mosquitto -f sudo tail -f /var/log/mosquitto/mosquitto.log sudo ss -lntp | grep mosquitto ``` Test renewal TLS: ```bash sudo certbot renew --dry-run ``` ## Environment Backend Nanti Saat adapter broker sungguhan dipasang ke platform: ```env MQTT_PUBLISH_MODE=broker MQTT_BROKER_URL=mqtts://mqtt.iptek.co:8883 MQTT_USERNAME=qris-backend MQTT_PASSWORD=... MQTT_CLIENT_ID=qris-platform-backend MQTT_CONNECT_TIMEOUT_MS=5000 MQTT_TLS=true ``` Topic kontrak yang harus dipertahankan: ```text 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 ``` ## 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: ```bash 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: ```bash 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: ```bash 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.