paho-mqtt'yi TLS üzerinden bağlamak için connect()'ten önce client.tls_set() çağırın ve broker'ın TLS portunu, genellikle 8883'ü kullanın. Argümansız çağrıldığında paho 2.x broker sertifikasını sisteminizin güvenilir CA'larına göre doğrular ve hostname'i kontrol eder; herkese açık sertifikalı bir broker için tam olarak istediğiniz budur. Özel bir CA için ca_certs= verirsiniz, mutual TLS için de certfile= ve keyfile= eklersiniz.
Bu rehber her tls_set() seçeneğini, eksiksiz bir mutual TLS örneğini ve paho'nun fırlattığı SSL hatalarını birebir metinleriyle, her birinin nasıl doğrulanıp düzeltileceğiyle birlikte ele alıyor.
En basit TLS bağlantısı
import os
import time
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, reason_code, properties):
print("CONNACK:", reason_code)
client.subscribe("testmqtt/tls-demo/#")
def on_message(client, userdata, msg):
print(msg.topic, msg.payload.decode())
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="tls-demo-1")
client.username_pw_set(os.environ["MQTT_USER"], os.environ["MQTT_PASS"])
client.tls_set() # sistem CA deposu, TLS 1.2+, hostname doğrulaması açık
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 8883, keepalive=60)
client.loop_start()
client.publish("testmqtt/tls-demo/hello", "encrypted hello", qos=1)
time.sleep(2)
client.loop_stop()
client.disconnect()İki kural sorunların çoğunu önler:
- IP adresiyle değil, sertifikadaki hostname ile bağlanın. paho,
connect()'e verdiğiniz host'u SNI olarak gönderir ve sertifikayla karşılaştırır. - Port ile protokolü eşleştirin. TLS 8883'e (secure WebSocket için 8084'e), şifresiz TCP 1883'e gider. Bunları karıştırmak kafa karıştırıcı hatalara yol açar; aşağıda açıklanıyor. Tam liste için MQTT portları yazısına bakın.
tls_set() seçenekleri
| Argüman | paho 2.x'te varsayılan | Ne zaman kullanılır |
|---|---|---|
ca_certs | None → işletim sistemi güven deposu | Broker özel veya self-signed bir CA kullanıyorsa: CA PEM dosyasının yolu |
certfile | None | Broker istemci sertifikası istiyorsa (mutual TLS) |
keyfile | None | Aynı dosyada değilse certfile'ın özel anahtarı |
keyfile_password | None | Anahtar şifreliyse; aksi halde paho terminalde sorabilir |
cert_reqs | ssl.CERT_REQUIRED | Neredeyse hiç değiştirmeyin; ssl.CERT_NONE doğrulamayı kapatır |
tls_version | ssl.PROTOCOL_TLS_CLIENT (en yüksek sürümü müzakere eder) | Nadiren; kendi context'inizde minimum sürüm belirlemeyi tercih edin |
ciphers | OpenSSL varsayılanları | Belirli bir cipher politikasına uymanız gerekiyorsa |
alpn_protocols | None | Uç nokta ALPN'e göre yönlendiriyorsa, örneğin 443 portunda MQTT |
İki ilgili metot tabloyu tamamlar:
tls_insecure_set(True)zincir doğrulamasını korur ama hostname kontrolünü atlar.tls_set()'ten sonra çağrılmalıdır; aksi halde pahoValueError: Must configure SSL context before using tls_insecure_set.fırlatır.tls_set_context(ctx)kendissl.SSLContextnesnenizi kabul eder.tls_set()'in sunmadığı ayarlara ihtiyaç duyduğunuzda kullanın; TLS istemci başına yalnızca bir kez yapılandırılabilir: ikinci çağrıValueError: SSL/TLS has already been configured.fırlatır.
import ssl
ctx = ssl.create_default_context(cafile="/etc/mqtt/ca.crt") # doğrulama + hostname kontrolü
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
client.tls_set_context(ctx)Özel CA veya self-signed broker
Yerel ağınızdaki bir Mosquitto broker'ı genellikle kendi CA'nızın imzaladığı bir sertifikaya sahiptir. paho'ya CA sertifikasını verin (broker sertifikasını değil ve asla özel anahtarı değil):
client.tls_set(ca_certs="/etc/mqtt/ca.crt")
client.connect("mqtt.home.internal", 8883)Bağlandığınız hostname, broker sertifikasının Subject Alternative Name alanında bulunmalıdır. IP ile bağlanıyorsanız IP, SAN'da IP: girdisi olarak yer almalıdır; bir DNS: girdisi bunu kapsamaz. Emin değilseniz kontrolleri kapatmak yerine sunucu sertifikasını doğru SAN ile yeniden oluşturun. Adım adım anlatım self-signed sertifika hatalarını çözme yazısında.
İstemci sertifikalarıyla mutual TLS
Mutual TLS'te broker da istemciyi doğrular. Her cihaz, broker'ın güvendiği bir CA tarafından imzalanmış kendi sertifikasını alır; birçok broker sertifikanın CN'ini MQTT kullanıcı adına eşleyebilir.
import ssl
import paho.mqtt.client as mqtt
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="device-0042")
client.tls_set(
ca_certs="/etc/mqtt/ca.crt", # broker sertifikasını imzalayan CA
certfile="/etc/mqtt/device-0042.crt", # bu istemcinin sertifikası (PEM)
keyfile="/etc/mqtt/device-0042.key", # özel anahtarı (PEM)
keyfile_password=None, # veya bir secret store'dan okuyun
cert_reqs=ssl.CERT_REQUIRED,
)
client.connect("mqtt.example.internal", 8883)
client.loop_forever()İnsanların takıldığı noktalar:
certfile, istemci sertifikasını ve ardından ara (intermediate) CA'ları içerebilir; broker yalnızca root'a güveniyorsa bu gereklidir.- Anahtarlar PEM olmalıdır. Bir PKCS#12 paketini
openssl pkcs12 -in device.p12 -out device.crt -clcerts -nokeysveopenssl pkcs12 -in device.p12 -out device.key -nocerts -nodesile dönüştürün. - Anahtar dosyasını koruyun (
chmod 600) ve asla commit etmeyin.
Broker'ınızın istemci sertifikalarını destekleyip desteklemediği ve bunları kimliklere nasıl eşlediği broker'a bağlıdır. Kavramlar ve broker tarafı kurulum MQTT istemci sertifikaları ve mTLS yazısında.
paho'da yaygın SSL hataları ve çözümleri
connect() ile TLS hataları doğrudan exception olarak fırlatılır ve okunması kolaydır. connect_async() ve loop_start() ile ağ thread'i bunun yerine yeniden denemeye devam eder; hatayı görmek için client.enable_logger() çağırın. (_ssl.c:NNNN) eki Python sürümüne göre değişir; ondan önceki metni arayın.
certificate verify failed: unable to get local issuer certificate
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1006)- Neden: Python, broker sertifikasını veren CA'yı bulamıyor. Ya broker özel bir CA kullanıyor ya da Python'unuzda CA paketi yok (macOS'taki python.org derlemelerinde tipiktir).
- Doğrulama:
python3 -c "import ssl; print(ssl.get_default_verify_paths())"Python'un nereye baktığını gösterir;openssl s_client(aşağıya bakın) sertifikayı kimin verdiğini gösterir. - Çözüm: özel CA için
ca_certs="ca.crt"verin. Paket eksikse macOS'ta/Applications/Python 3.x/Install Certificates.command'ı çalıştırın ya dapip install certifiyapıptls_set(ca_certs=certifi.where())kullanın.
self-signed certificate (in certificate chain)
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1006)- Neden: zincir, istemcinizin güvenmediği bir root ile bitiyor. Eski OpenSSL derlemeleri bunu
self signed certificateolarak yazar. - Çözüm: root CA'yı
ca_certs=ile verin. Broker sertifikasının kendisi self-signed ise (CA yoksa) o dosyayıca_certsolarak verin, daha iyisi sertifikayı küçük bir CA'dan üretin.
Hostname mismatch
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'localhost'. (_ssl.c:1006)- Neden:
connect()'teki ad veya IP, sertifikanın SAN listesinde yok. - Doğrulama:
openssl s_client -connect host:8883 -servername host </dev/null 2>/dev/null | openssl x509 -noout -ext subjectAltName. - Çözüm: SAN'da bulunan bir adla bağlanın veya sertifikayı yeniden üretin.
tls_insecure_set(True)sorunu yalnızca gizler ve production'a girmemelidir. Ayrıntılar MQTT hostname mismatch yazısında.
certificate has expired
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1006)- Neden: broker sertifikası (veya bir ara sertifika)
notAftertarihini geçmiş ya da cihazın saati yanlış. RTC'si olmayıp 1970'te açılan cihazlar tersini yaşar:certificate is not yet valid. - Çözüm: sertifikayı yenileyin; cihazlarda bağlanmadan önce saati senkronize edin (NTP).
Yanlış port: EOF veya wrong version number
ssl.SSLEOFError: [SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1006)
ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1006)- Neden:
tls_set()çağırdınız ama şifresiz bir porta bağlandınız. Bir MQTT listener'ı genellikle bağlantıyı kapatır (EOF veyaConnectionResetError); HTTP veya başka TLS olmayan byte'larla yanıt veren bir portWRONG_VERSION_NUMBERverir. - Çözüm: TLS portunu (8883) kullanın. Ters hata, yani 8883'e
tls_set()olmadan bağlanmak, işe yarar bir şey fırlatmaz: broker bağlantıyı düşürür ve hiçbir zaman CONNACK almazsınız.
İstemci sertifikası reddedildi (mutual TLS)
ssl.SSLError: [SSL: TLSV13_ALERT_CERTIFICATE_REQUIRED] tlsv13 alert certificate required (_ssl.c:2580)
ssl.SSLError: [SSL: TLSV1_ALERT_UNKNOWN_CA] tlsv1 alert unknown ca (_ssl.c:1006)
ssl.SSLError: [SSL] PEM lib (_ssl.c:3900)certificate required: broker bir istemci sertifikası istiyor ve siz göndermediniz;certfile/keyfileekleyin. TLS 1.3'te bu uyarı handshake'ten hemen sonra gelebilir, bu yüzden exception yerine anında kopma olarak görünebilir.unknown ca: broker, istemci sertifikanızı imzalayan CA'ya güvenmiyor ya da ara sertifikayı göndermediniz. Broker'ın CA dosyasını düzeltin veya ara sertifikayıcertfile'ın sonuna ekleyin.PEM libveya[X509: KEY_VALUES_MISMATCH] key values mismatch: sertifika veya anahtar okunamıyor, farklı bir şifreyle şifrelenmiş, PEM değil ya da anahtar sertifikaya ait değil. RSA anahtarları içinopenssl x509 -noout -modulus -in device.crt | openssl md5ileopenssl rsa -noout -modulus -in device.key | openssl md5çıktısını karşılaştırın.
paho'nun diğer hataları için Python paho-mqtt hataları yazısına bakın.
Çözümü nasıl test edersiniz
Önce Python'dan bağımsız olarak broker tarafını kontrol edin:
openssl s_client -connect broker.connectmq.com:8883 \
-servername broker.connectmq.com -showcerts </dev/null
# şunu arayın: Verify return code: 0 (ok)Ardından çalıştığını bildiğiniz bir istemcinin aynı dosyalarla çalıştığını doğrulayın:
mosquitto_sub -h broker.connectmq.com -p 8883 \
--capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" \
-t 'testmqtt/tls-demo/#' -v -C 1Son olarak Python script'inizi loglama açık şekilde çalıştırın; kalan sorun varsa ekrana yazılır:
import logging
logging.basicConfig(level=logging.DEBUG)
client.enable_logger()openssl başarılı olup Python başarısız oluyorsa fark neredeyse her zaman Python'un kullandığı CA paketidir. İkisi de başarısızsa sorun broker'da veya ağdadır. Tüm yol için adım adım bir kontrol listesi MQTT TLS bağlantısını test etme yazısında; broker'ı secure WebSocket üzerinden online MQTT istemcisi ile de çapraz kontrol edebilirsiniz.
paho'da TLS, doğru yapılmış tek bir çağrıya indirgenir: doğru CA, doğru port ve sertifikayla eşleşen bir hostname ile tls_set(). Doğrulamayı açık tutun, kontrolleri kapatmak yerine sertifikaları düzeltin; ek kod yazmadan şifreleme ve broker kimlik doğrulaması elde edersiniz.
Sıkça sorulan sorular
tls_set() argümansız çağrıldığında ne yapar?
paho-mqtt 2.x'te Python'un desteklediği en yüksek TLS sürümüyle bir istemci TLS context'i oluşturur, işletim sisteminin güvenilir CA sertifikalarını yükler, geçerli bir sunucu sertifikası ister ve hostname'in eşleştiğini kontrol eder. Herkesçe güvenilen sertifikalara sahip broker'lar için doğru ayar budur.
tls_insecure_set(True) ile cert_reqs=ssl.CERT_NONE arasındaki fark nedir?
tls_insecure_set(True) sertifika zincirini doğrulamaya devam eder ama hostname kontrolünü atlar. cert_reqs=ssl.CERT_NONE doğrulamayı tamamen kapatır ve her sertifikayı kabul eder. İkisi de man-in-the-middle saldırılarına kapı açar ve yalnızca kısa yerel testlerde kullanılmalıdır.
paho neden certificate verify failed: unable to get local issuer certificate diyor?
Python, broker sertifikasını imzalayan CA'yı bulamadı. Ya broker özel bir CA kullanıyor (dosyasını ca_certs= ile verin) ya da Python'unuzda CA paketi yok; macOS'taki python.org kurulumlarında bu yaygındır. Install Certificates.command'ı çalıştırarak veya ca_certs=certifi.where() vererek çözün.

