Broker testleri

MQTT ACL'leri ve Topic Yetkileri Nasıl Test Edilir

MQTT ACL'lerini topic topic doğrulayın: MQTT 3.1.1'de reddedilen publish neden sessizdir, MQTT 5'te 0x87 reason code'u, SUBACK 0x80 ve otomatik bir yetki testi.

Güncellendi · 9 dk okuma

MQTT ACL'leri ve Topic Yetkileri Nasıl Test Edilir

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.

İşlemMQTT 3.1.1MQTT 5
Publish QoS 0, reddedildiMesaj atılır, geri bildirim yokMesaj atılır, geri bildirim yok (broker 0x87 ile bağlantıyı kesebilir)
Publish QoS 1, reddedildiNormal PUBACK, mesaj atılır — veya bağlantı kapatılır0x87 Not authorized reason code'lu PUBACK
Publish QoS 2, reddedildiNormal PUBREC, mesaj atılır — veya bağlantı kapatılır0x87 reason code'lu PUBREC
Subscribe, reddedildi0x80 (Failure) return code'lu SUBACK0x87 reason code'lu SUBACK (veya 0x80 Unspecified error)
Reddedilen topic'leri kapsayan wildcard aboneliğiBroker'a bağlı: reddedilir ya da kabul edilip mesaj bazında filtrelenirAynı

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:

acl_file (Mosquitto)
# 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
1testmqtt/acl/telemetry/sensor-01/temp'e publishTeslim edilir
2testmqtt/acl/telemetry/sensor-02/temp'e publish (başka cihaz)Reddedilir, teslim edilmez
3testmqtt/acl/commands/sensor-01/reboot'a publish (kendi komut topic'i)Reddedilir — salt okunur
4testmqtt/acl/commands/sensor-01/#'a subscribeİzin verilir
5testmqtt/acl/commands/sensor-02/#'a subscribeSUBACK hatası
6testmqtt/acl/#'a subscribeReddedilir, ya da izin verilir ama yalnızca kendi komutları gelir
7# ve $SYS/#'a subscribeReddedilir, ya da kendi topic'leri dışında hiçbir şey gelmez
8Başka bir cihazın komut topic'indeki retained mesajı almakAsla teslim edilmez
9testmqtt/acl/telemetry/sensor-01'e publish (son seviye olmadan)Niyetinize göre — pattern'i kontrol edin
  1. 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:

Terminal 1 — gözlemci
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:

Terminal 2 — izinli ve reddedilen publish
# 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 99

Reddedilen 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:

Reddedilen abonelik
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:

test_acl.py
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'da acl_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.
  • pattern kuralları eşleşmiyor. %u kullanıcı adı, %c client 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 SIGHUP gö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 /a ile a aynı 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.