Forum Discussion

khaldayeh's avatar
khaldayeh
Copper Contributor
Oct 27, 2021

Azure SDK python client to Azure iothub over HAproxy (SSL handshake failure)

I am trying to fix an IP address for Azure Iothub via Load Balencer and HAproxy as suggested in this https://medium.com/cloudzone/azure-iot-hub-how-to-expose-it-using-fixed-ip-and-create-a-more-secure-environment-along-the-way-988661a8f67ahttps://i.stack.imgur.com/gyQ9j.png I have configured the HAproxy as suggested to pass the SSL handshake to the server:

 

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # Default ciphers to use on SSL-enabled listening sockets.
        # For more information, see ciphers(1SSL). This list is from:
        #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
        # An alternative list with additional directives can be obtained from
        #  https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
        ssl-default-bind-options no-sslv3
defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http frontend haproxy_iothub
        bind *:8883
        bind *:443
        bind *:5671
        mode tcp
        default_backend iothub
backend iothub
        mode tcp
        server iothub [Server URL]:8883 check
        server iothub [Server URL]:443 check
        server iothub [Server URL]:5671 check

 

 

To simulate the device, I used Azure V2 SDK (azure-iot-device) and defined a proxy option and created a client from a connection string.

 

 

proxy_opts = ProxyOptions(proxy_type=socks.HTTP, proxy_addr="Proxy_ IP", proxy_port=8883)
device_client = IoTHubDeviceClient.create_from_connection_string("IOTHUB_DEVICE_CONNECTION_STRING", websockets=True, proxy_options=proxy_opts )

 

I was not able to reach the iothub, I tried debugging the library to get more information and it turned out that the blocking occurs due to a general proxy error ("connection closed unexpectedly") in _negotiate_HTTP. socks.HTTPError :504 : Gateway Time-out (in socks.py)

 

HAproxy logging showes :

 

Oct 18 08:48:37 vmss2xigg000000 haproxy[27470]: *..:59000 [18/Oct/2021:08:48:37.451] haproxy_iothub iothub/iothub1 1/1/38 0 -- 1/1/0/0/0 0/0

 

Any help much appreciated

HA-Proxy version 1.8.8-1ubuntu0.11

Azure-iot-device Version 2.8.0

1 Reply

  • Take this:

     

    1. Use TCP Mode Only (No SSL Termination)

    Azure IoT Hub expects TLS to be terminated only at its endpoint. Your HAProxy config must forward raw TCP traffic without interpreting or modifying SSL.

    • Ensure frontend and backend are in mode tcp (Done).

    ✅ 2. Avoid Port Multiplexing

    Each port (443, 8883, 5671) serves a different protocol:

    • 8883 → MQTT over TLS
    • 443 → HTTPS/WebSockets
    • 5671 → AMQP over TLS

    Instead of binding all ports in one frontend, create separate frontends per port:

    frontend iothub_mqtt
        bind *:8883
        mode tcp
        default_backend iothub_mqtt_backend
    
    backend iothub_mqtt_backend
        mode tcp
        server iothub_mqtt <iot-hub-hostname>:8883 check
    
    frontend iothub_websockets
        bind *:443
        mode tcp
        default_backend iothub_websockets_backend
    
    backend iothub_websockets_backend
        mode tcp
        server iothub_ws <iot-hub-hostname>:443 check

    3. Use Correct SDK Protocol

    Your SDK call uses websockets=True, which means it connects via port 443 using WebSockets. Ensure:

    • HAProxy frontend for port 443 is active.
    • Backend points to Azure IoT Hub’s FQDN on port 443.
    • No SSL termination or inspection is done.

    4. Proxy Configuration in SDK

    The SDK’s ProxyOptions is intended for HTTP proxies, not TCP-level proxies like HAProxy. If you’re using HAProxy as a TCP passthrough, you don’t need to set proxy_options.

    Instead:

    device_client = IoTHubDeviceClient.create_from_connection_string(
        "IOTHUB_DEVICE_CONNECTION_STRING", websockets=True
    )

    And configure your system-level routing to direct traffic to HAProxy’s fixed IP.

Resources