Débogage de MQTT sur websockets sur Envoy 1.28.0

J'ai migré notre installation Envoy de la version 1.11.1 à la version 1.28.0, et j'utilise maintenant aussi SNI pour sélectionner le bon certificat.

Une grande partie de cette migration consiste à mettre à jour la syntaxe de la configuration d'Envoy de l'API v2 à l'API v3.

La mise à jour s'est bien déroulée, à l'exception de notre service MQTT basé sur websocket (basé sur VerneMQ) qui ne fonctionnait pas comme prévu.

J'ai d'abord supposé que le problème venait d'envoy. Après avoir essayé plusieurs options de timeout, et consulté la documentation d'envoy, j'ai décidé d'expérimenter une nouvelle route, et un broker différent (Mosquitto) derrière elle.

La configuration suivante fonctionne avec Mosquitto en tant que courtier, au cas où quelqu'un d'autre rencontrerait le même problème.

Voici un extrait de mon envoy.yaml (la configuration complète fait plus de 87000 lignes, générées par un script template, à cause du SNI et de la nécessité d'avoir des listeners individuels par domaine comme je l'ai mentionné plus haut) :

static_resources :
  auditeurs :
  - adresse :
      adresse_socket :
        adresse : 0.0.0.0
        valeur_du_port : 443
    limite_de_tampon_par_connexion_bytes : 32768 # 32 KiB
    listener_filters :
    - name : tls_inspector
      typed_config :
        "@type" : type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
    filter_chains :
    - filter_chain_match :
        server_names : ["picockpit.com", "www.picockpit.com", "picockpit.com:443", "www.picockpit.com:443"]
      transport_socket :
        name : envoy.transport_sockets.tls
        typed_config :
          "@type" : type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context :
            tls_certificates :
            - certificate_chain : { filename : "/certs/letsencrypt/live/picockpit.com/fullchain.pem" }
              private_key : { filename : "/certs/letsencrypt/live/picockpit.com/privkey.pem" }
            alpn_protocols : [ "h2,http/1.1" ]
      filters :
      - name : envoy.filters.network.http_connection_manager
        typed_config :
          '@type' : type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix : ingress_http
          codec_type : AUTO

          http_filters :
          - name : envoy.filters.http.compressor
            typed_config :
              '@type' : type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor
              compressor_library :
                name : text_optimized
                typed_config :
                  '@type' : type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip
                  niveau_de_compression : BEST_SPEED
                  compression_strategy : DEFAULT_STRATEGY
                  niveau_mémoire : 9
                  window_bits : 15
                  taille_du_chunk : 16384
          - nom : envoy.filters.http.router
            typed_config :
             "@type" : type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
          common_http_protocol_options :
            idle_timeout : 3600s # 1 heure

          use_remote_address : true
          xff_num_trusted_hops : 0
          route_config :
            virtual_hosts :
            - name : backend
              domains : ["picockpit.com", "www.picockpit.com", "picockpit.com:443", "www.picockpit.com:443"]
              routes :

              - match : { path : "/pidoctor"}
                redirect :
                  path_redirect : "/raspberry-pi/pidoctor-raspberry-pi-system-health-monitor/"
              - match : { prefix : "/pidoctor/"}
                redirect :
                  path_redirect : "/raspberry-pi/pidoctor-raspberry-pi-system-health-monitor/"


              - match : { prefix : "/mqtt/test" }
                route :
                  prefix_rewrite : "/mqtt"
                  cluster : target_test
                  timeout : 0s
                  idle_timeout : 0s
                  upgrade_configs :
                    - upgrade_type : "websocket"
                      enabled : true
                      
              - match : { prefix : "/" }
                route :
                  cluster : target_main
                  timeout : 0s
  clusters :

    - name : target_test
      connect_timeout : 5s
      per_connection_buffer_limit_bytes : 32768 # 32 KiB
      type : STRICT_DNS
      lb_policy : ROUND_ROBIN
      load_assignment :
        nom_du_cluster : target_test
        endpoints :
        - lb_endpoints :
          - endpoint :
              adresse :
                adresse_socket :
                  adresse : mosquitto-test.test-network
                  valeur_du_port : 8025

Notez que j'ai omis une grande partie de la configuration des autres services, des routes, et que je ne vous ai pas donné les informations sur le cluster target_main (parce qu'elles ne sont pas pertinentes pour la situation MQTT over websockets).

Remarquez la valeur de timeout : 0s, ce qui est important pour que les connexions MQTT continuent au lieu d'être interrompues au bout de 15 secondes comme c'est le cas par défaut.

J'ai également mis en évidence d'autres parties qui sont, à mon avis, pertinentes pour permettre aux connexions d'être mises à niveau vers des websockets (de sorte que MQTT puisse être transporté à travers eux). Notez également le numéros de port sont transmises en tant que correspondances de domaines supplémentaires.

Mosquitto docker-compose.yml :

version : '3.6'

services :
  mosquitto :
    image : eclipse-mosquitto
    nom_du_conteneur : mosquitto-test
    nom d'hôte : mosquitto-test
    réseaux :

      - test_net
    restart : "no
    user : "root:root"
    volumes :
      - type : bind
        source : ./mosquitto.conf
        target : /mosquitto/config/mosquitto.conf


réseaux :

  test_net :
    externe :
      name : test-network


mosquitto.conf :

auditeur 8025
protocole websockets

allow_anonymous true
log_type all

Outil utilisé pour vérifier la connexion :

Client websocket HiveMQ

Mise à jour 13.11.2023

MQTT est de nouveau en ligne, avec VerneMQ également :

Alors que j'avais redémarré VerneMQ plusieurs fois, je n'ai apparemment pas attendu assez longtemps pour qu'il se stabilise. Un collègue l'a redémarré aujourd'hui et il fonctionne désormais. Il semble qu'il faille 10 à 15 minutes (dans notre configuration) pour que VerneMQ devienne totalement réactif et fonctionne correctement.

Je peux donc confirmer que la configuration ci-dessus pour envoy fonctionne également avec VerneMQ.

Leçon apprise

Si quelque chose ne fonctionne pas, essayer de reproduire le problème dans l'interaction avec un autre outil - si cela fonctionne, il est possible que le problème ne vienne pas du premier outil que vous avez modifié, mais du second avec lequel il doit fonctionner.

et quelques autres produits :

Documentation en ligne

Outils utiles