MQTT ACL'lerini test etmek için kontrol etmek istediğiniz kullanıcıyla bağlanın, cihazın yapabilmesi ve yapamaması gereken her publish ve subscribe işlemini deneyin ve sonucu istemcinin dışından doğrulayın. Bu son kısım kritiktir: MQTT 3.1.1'de reddedilen bir publish, başarılı olanla birebir aynı görünür; bu yüzden tek güvenilir kanıt, mesajın gelip gelmediğini izleyen ikinci bir istemcidir.
Bu rehber her protokol sürümünün reddi nasıl bildirdiğini, bir yetki test matrisini, mosquitto ile elle yapılan kontrolleri ve her ACL değişikliğinden sonra çalıştırabileceğiniz otomatik bir testi anlatır.
Broker'lar reddedilen bir işlemi nasıl bildirir
ACL'ler (erişim kontrol listeleri), bir kullanıcının hangi topic'lere publish edebileceğini (yazma) ve hangilerine abone olabileceğini (okuma) belirler. Bu noktada kimlik doğrulama zaten başarılı olmuştur — CONNACK 0x00 idi — dolayısıyla tüm geri bildirim sonraki paketlerle gelir.
| İşlem | MQTT 3.1.1 | MQTT 5 |
|---|---|---|
| Publish QoS 0, reddedildi | Mesaj atılır, geri bildirim yok | Mesaj atılır, geri bildirim yok (broker 0x87 ile bağlantıyı kesebilir) |
| Publish QoS 1, reddedildi | Normal PUBACK, mesaj atılır — veya bağlantı kapatılır | 0x87 Not authorized reason code'lu PUBACK |
| Publish QoS 2, reddedildi | Normal PUBREC, mesaj atılır — veya bağlantı kapatılır | 0x87 reason code'lu PUBREC |
| Subscribe, reddedildi | 0x80 (Failure) return code'lu SUBACK | 0x87 reason code'lu SUBACK (veya 0x80 Unspecified error) |
| Reddedilen topic'leri kapsayan wildcard aboneliği | Broker'a bağlı: reddedilir ya da kabul edilip mesaj bazında filtrelenir | Aynı |
3.1.1 davranışı broker'ınızdaki bir hata değildir. Spesifikasyon, sunucu bir PUBLISH'e yetki vermediğinde bunu istemciye bildirmenin bir yolu olmadığını ve mesajı ya normal QoS kurallarına göre onaylaması ya da ağ bağlantısını kapatması gerektiğini söyler. Birçok istemci iki durumda da hiçbir şey loglamaz; "mesajlarım kayboluyor" şikâyetinin bu kadar yaygın bir ACL belirtisi olmasının nedeni budur — bkz. MQTT ACL'in sessizce reddetmesi.
Bir yetki test matrisi oluşturun
Temsili bir kullanıcının yapabilmesi gerekenleri listeleyin, ardından yapmaması gerekenleri ekleyin. Aşağıda kendi telemetrisini publish edebilen ve kendi komutlarını okuyabilen bir sensör için örnek ve bunu uygulayan bir Mosquitto ACL'i var:
# every device: own telemetry and own commands only
pattern write testmqtt/acl/telemetry/%u/#
pattern read testmqtt/acl/commands/%u/#
# a monitoring user that may read everything under testmqtt/acl
user acl-observer
topic read testmqtt/acl/#| # | sensor-01 kullanıcısının yaptığı | Beklenen |
|---|---|---|
| 1 | testmqtt/acl/telemetry/sensor-01/temp'e publish | Teslim edilir |
| 2 | testmqtt/acl/telemetry/sensor-02/temp'e publish (başka cihaz) | Reddedilir, teslim edilmez |
| 3 | testmqtt/acl/commands/sensor-01/reboot'a publish (kendi komut topic'i) | Reddedilir — salt okunur |
| 4 | testmqtt/acl/commands/sensor-01/#'a subscribe | İzin verilir |
| 5 | testmqtt/acl/commands/sensor-02/#'a subscribe | SUBACK hatası |
| 6 | testmqtt/acl/#'a subscribe | Reddedilir, ya da izin verilir ama yalnızca kendi komutları gelir |
| 7 | # ve $SYS/#'a subscribe | Reddedilir, ya da kendi topic'leri dışında hiçbir şey gelmez |
| 8 | Başka bir cihazın komut topic'indeki retained mesajı almak | Asla teslim edilmez |
| 9 | testmqtt/acl/telemetry/sensor-01'e publish (son seviye olmadan) | Niyetinize göre — pattern'i kontrol edin |
- durum pattern'lerin insanları şaşırttığı yerdir:
telemetry/%u/#,telemetry/sensor-01'in kendisiyle de eşleşir, çünkü#üst seviyeyi de kapsar. Testi yazmadan önce sınır durumlarını kontrol etmek için pattern'lerinizi ve topic'lerinizi topic matcher aracına yapıştırın. 8. durum önemlidir, çünkü retained mesajlar abonelik anında teslim edilir; burada okuma ACL'ini uygulamayı unutan bir broker her topic'in son değerini sızdırır. Yanlış giden diğer pattern'ler için bkz. MQTT wildcard güvenliği.
mosquitto ile elle kontrol
Önce bir gözlemci açın. Geniş okuma yetkisi olan bir hesap kullandığı için broker'a gerçekte neyin ulaştığını gösterir:
mosquitto_sub -h broker.connectmq.com -p 1883 \
-u acl-observer -P "$OBSERVER_PASS" -t 'testmqtt/acl/#' -vŞimdi test edilen cihaz olarak MQTT 5 ve QoS 1 ile publish edin; böylece bir ret reason code üretir:
# durum 1: gözlemcide görünmeli
mosquitto_pub -h broker.connectmq.com -p 1883 -V mqttv5 -q 1 \
-u sensor-01 -P "$MQTT_PASS" -t testmqtt/acl/telemetry/sensor-01/temp -m 21.5
# durum 2: görünMEmeli; MQTT 5 reddi bildirir
mosquitto_pub -h broker.connectmq.com -p 1883 -V mqttv5 -q 1 \
-u sensor-01 -P "$MQTT_PASS" -t testmqtt/acl/telemetry/sensor-02/temp -m 99Reddedilen publish için mosquitto istemcisi Warning: Publish 1 failed: Not authorized. gibi bir uyarı yazdırır. Aynı komutu -V mqttv311 ile çalıştırırsanız hiçbir uyarı olmadan temiz şekilde çıkar — gözlemcinin sessiz kalması tek kanıtınızdır.
Abonelikleri doğrulamak daha kolaydır, çünkü iki sürüm de SUBACK ile yanıt verir:
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 -d \
-u sensor-01 -P "$MQTT_PASS" -t 'testmqtt/acl/commands/sensor-02/#'-d ile MQTT 5'teki bir ret için (0x87) Subscribed (mid: 1): 135, 3.1.1'de ise Subscribed (mid: 1): 128 (0x80) görürsünüz. Güncel mosquitto sürümleri ardından All subscription requests were denied. yazdırıp çıkar. Broker'ınız aboneliğe izin veriyorsa başka bir hesaptan o topic'e publish edin ve hiçbir şeyin teslim edilmediğini doğrulayın. Kodlar SUBACK 0x80 ve Not authorized 0x87 yazılarında ayrıntılı açıklanıyor.
Aynısını online MQTT istemcisinde etkileşimli olarak da yapabilirsiniz: farklı kullanıcılarla iki oturum açın, birinde gözlemci olarak abone olun, diğerinde cihaz olarak publish edin.
Otomatik bir ACL testi
Bu paho-mqtt 2.x betiği matristeki publish ve subscribe durumlarını çalıştırır. MQTT 5 reason code'larını kontrol eder ve gözlemciyi doğruluk kaynağı olarak kullanır; böylece reddedilen mesajları sessizce atan broker'larda da çalışır:
import os, threading, time, uuid
import paho.mqtt.client as mqtt
HOST, PORT = "broker.connectmq.com", 1883
DEVICE = ("sensor-01", os.environ["MQTT_PASS"])
OBSERVER = ("acl-observer", os.environ["OBSERVER_PASS"])
BASE = "testmqtt/acl"
def client(creds):
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"acl-{uuid.uuid4().hex[:8]}", protocol=mqtt.MQTTv5)
c.username_pw_set(*creds)
c.connect(HOST, PORT)
c.loop_start()
time.sleep(0.5)
return c
seen = set()
obs = client(OBSERVER)
obs.on_message = lambda c, u, m: seen.add(m.topic)
obs.subscribe(f"{BASE}/#", qos=1)
dev = client(DEVICE)
acks = {}
dev.on_publish = lambda c, u, mid, rc, props: acks.__setitem__(mid, str(rc))
subacks = {}
dev.on_subscribe = lambda c, u, mid, rcs, props: subacks.__setitem__(mid, [str(r) for r in rcs])
def check_publish(topic, allowed):
info = dev.publish(topic, "acl-test", qos=1)
time.sleep(1)
delivered = topic in seen
print(f"PUB {topic}: puback={acks.get(info.mid)} delivered={delivered}")
assert delivered == allowed, f"publish to {topic}: expected allowed={allowed}"
if not allowed:
assert acks.get(info.mid) == "Not authorized"
def check_subscribe(topic, allowed):
_, mid = dev.subscribe(topic, qos=1)
time.sleep(1)
granted = not subacks[mid][0].startswith(("Not authorized", "Unspecified"))
print(f"SUB {topic}: suback={subacks[mid]}")
assert granted == allowed, f"subscribe to {topic}: expected allowed={allowed}"
check_publish(f"{BASE}/telemetry/sensor-01/temp", True)
check_publish(f"{BASE}/telemetry/sensor-02/temp", False)
check_publish(f"{BASE}/commands/sensor-01/reboot", False)
check_subscribe(f"{BASE}/commands/sensor-01/#", True)
check_subscribe(f"{BASE}/commands/sensor-02/#", False)
print("all ACL checks passed")ACL dosyasında veya kimlik doğrulama backend'inde yapılan her değişiklikten sonra ve CI'da bir staging broker'a karşı çalıştırın. Bir kontrol başarısız olduğunda yazdırılan reason code ve delivered bayrağı, broker'ın mesajı reddettiğini mi, sessizce attığını mı yoksa geçirdiğini mi hemen gösterir.
ACL testinin başarısız olmasının yaygın nedenleri
- Her şey reddediliyor. ACL dosyası yüklenmemiştir veya kuraldaki kullanıcı adı
CONNECT'tekiyle eşleşmiyordur. Açılışta broker logunu kontrol edin; Mosquitto'daacl_file, parola dosyasıyla aynı listener kapsamında olmalıdır — bkz. Mosquitto kimlik doğrulama. - Her şeye izin veriliyor. Dosyanın üst kısmında
topic readwrite #gibi geniş bir kural veya kendi serbest kuralları olan anonim erişim. patternkuralları eşleşmiyor.%ukullanıcı adı,%cclient ID'dir; yanlışını kullanmak, ID'si kullanıcı adından farklı olan her cihazı reddeder.- Değişiklikler etkili olmuyor. Çoğu broker ACL'leri açılışta veya yeniden yüklemede okur. Testi tekrar çalıştırmadan önce Mosquitto'ya
SIGHUPgönderin veya yeniden başlatın. - Test geçiyor ama canlı ortam başarısız. İstemci başında eğik çizgiyle (
/testmqtt/...) veya farklı büyük/küçük harfle publish ediyordur. Topic'ler büyük/küçük harfe duyarlıdır ve/aileaaynı değildir.
ACL test trafiğini online istemcide izleyin
ACL bir güvenlik kontrolüdür ve güvenlik kontrolleri, kontrol bozulduğunda başarısız olan testlere ihtiyaç duyar. Her rol için izin verilen ve reddedilen işlemlerin bir matrisini tutun, publish edene güvenmek yerine teslimatı bir gözlemciyle doğrulayın ve retlerin yutulmak yerine bildirilmesi için MQTT 5'i tercih edin.
Sıkça sorulan sorular
Reddedilen MQTT publish'im neden hata döndürmüyor?
MQTT 3.1.1'in bir istemciye publish'in reddedildiğini bildirme yolu yoktur. Broker ya mesajı normal şekilde onaylayıp atar ya da bağlantıyı kapatır. 0x87 reason code'lu bir PUBACK almak için MQTT 5 kullanın veya ikinci bir abone ile doğrulayın.
SUBACK 0x80 ne anlama gelir?
0x80, SUBACK içindeki hata kodudur. MQTT 3.1.1'de reddedilen bir aboneliği bildirmenin tek yoludur; MQTT 5'te Unspecified error anlamına gelir ve broker'lar ACL retlerinde genellikle 0x87 Not authorized gönderir.
Wildcard aboneliği topic ACL'lerini atlatır mı?
Atlatmamalıdır. Broker'a göre # gibi bir filtreye abonelik ya reddedilir ya da kabul edilir ve her mesaj teslimden önce okuma ACL'ine karşı kontrol edilir. İkisini de test edin: SUBACK kodunu ve gerçekte hangi mesajların geldiğini.

