SSL handshake failure with rdp clients

Hello

I have problems to configure haproxy correctly to use it as “rdp broker”.

Use case:
We have several Windows 7 virtual machines. No terminal services are installed, so that only one rdp connection at the same time per virtual machine is allowed. The virtual machines are divided in several pools/groups. A user should be able to connect to a pool via windows remote desktop client. A free virtual machine in this pool should be automatically chosen for the user. Stickiness is not required.

To achieve this, I tried the following:

DNS-Records like “pool1.foo.bar.com”, “pool2.foo.bar.com”, etc. pointing to a haproxy server.
A haproxy frontend is listening on port 3389. Newer versions of microsoft’s remote desktop client should use SSL to protect the rdp session. The frontend should use the ssl sni to chose a backend. I wrote the following config:

frontend rdpbroker
    mode tcp
    option tcplog
    option clitcpka
    log global
    timeout client 1h
    bind :3389 ssl crt /etc/ssl/foo.bar.com.pem #Same result, when enable this: crt-ignore-err all verify none    
    tcp-request content accept if { req_ssl_hello_type 1 }

    #Same result when enable or disable this:
    #tcp-request inspect-delay 5s
    #tcp-request content accept if RDP_COOKIE

    acl pool1_sni req_ssl_sni -i pool1.foo.bar.com
    acl pool2_sni req_ssl_sni -i pool2.foo.bar.com

    use_backend pool1_bkd if pool1_sni
    use_backend pool2_bkd if pool2_sni
    #default_backend pool1_bkd

backend pool1_bkd
    mode tcp
    option tcplog
    option tcp-check
    log global
    timeout server 1h
    timeout connect 4s
    balance leastconn
    server vm1 vm1.foo.bar.com:3389 maxconn 1
    server vm2 vm1.foo.bar.com:3389 maxconn 1

[...]

My problem is, that the windows rdp client and xfreerdp can’t connect to any pool{n}.foo.bar.com-Pool. The ssl negotiation fails:

  • xfreerdp: ERRCONNECT_SECURITY_NEGO_CONNECT_FAILED [0x2000C]
  • windows rdp client: Can not connect to the remote computer
  • haproxy log: rdpbroker/1: SSL handshake failure

When I use “openssl s_client” or curl to connect to pool{n}.foo.bar.com:3389, the ssl connection can be established. So openssl and the cert are not generally broken.

I captured the tcp traffic on the haproxy server when a rdp client tries to connect:

client ------ SYN ------> proxy
client <---- SYN ACK ---- proxy
client ------ ACK ------> proxy
client -- TPKT v3, COTP-Package with RDP cookie --> proxy
client <---- FIN ACK ---- proxy
client ---- FIN ACK ----> proxy
client <------ FIN ------ proxy

The haproxy tears down the tcp connection after the first TPKT package arrived. I don’t know why this happen. Is something wrong in my considerations or the configuration?

Update:
I’m using HA-Proxy version 1.5.14 2015/07/02 and HA-Proxy version 1.6.3 2015/12/25

p.s. The following config works, but I can not determine a pool on this way:

    frontend rdpbroker
        mode tcp
        option tcplog
        option clitcpka
        log global
        timeout client 1h
        bind :3389
        tcp-request inspect-delay 5s
        tcp-request content accept if RDP_COOKIE
        default_backend pool1_bkd
    backend pool1_bkd
    [...]

Thank you for your help,
Elia

This configuration is wrong, you cannot use req_ssl_sni when terminating TLS on haproxy.You have to use ssl_fc_sni instead, please read the documentation:
https://cbonte.github.io/haproxy-dconv/1.6/configuration.html#7.3.4-ssl_fc_sni
https://cbonte.github.io/haproxy-dconv/1.6/configuration.html#req_ssl_sni

@lukastribus Thank you for the hint. This solves it. Some rdp clients don’t use the sni extension. So now I’m using different ports to map incoming connections to backends.

To: Elia
Could you share a working cfg-file for this sni extension?
I tried to build it according to your post, but failed.
I have the same task as yours.
Thank you in advanced.

Hi philz

Unfortunately I didn’t solve the problem with using sni extensions. I could not found this extension in the ssl communication with the rdp client we are using, so sni was not longer an option. I’m now using different ports to determine the backend. Incoming RDP connections on 3390 port goes to backend 1, port 3391 to backend 2, and so on.