Load balancing Windows Terminal Server – HAProxy and RDP Cookies or Microsoft Connection Broker


by Nick Chalk

When you have users depending on Windows Terminal Services for their main desktop, it’s a good idea to have more than one Terminal Server. RDP, however, is not an easy protocol to load balance; sessions are long-lived and need to be persistent to a particular server, and users may connect from different source addresses during one session.

The current development version of HAProxy has made an important step forward in making this possible. Thanks to work by Exceliance, it now supports RDP Cookies, offering a solution to the persistence problem.

We have been testing the latest development release of HAProxy, 1.4-dev4, on a loadbalancer.org Enterprise R16 device. The real servers were two Windows Server 2008 machines, with identical test users set up on both.

We settled upon the following HAProxy configuration (RDP Cookies):

   defaults
        clitimeout 1h
        srvtimeout 1h
   listen VIP1 192.168.0.10:3389
        mode tcp
        tcp-request inspect-delay 5s
        tcp-request content accept if RDP_COOKIE
        persist rdp-cookie
        balance rdp-cookie
        option tcpka
        option tcplog
        server Win2k8-1 192.168.0.11:3389 weight 1 check   inter 2000 rise 2 fall 3
        server Win2k8-2 192.168.0.12:3389 weight 1 check   inter 2000 rise 2 fall 3
        option redispatch

Note that this is only a fragment of the haproxy.cfg file, showing the relevant options.

The load balancer’s Virtual IP is set to 192.168.0.10, listening on port 3389 for RDP. The two real servers are on 192.168.0.11 and 192.168.0.12, in the same subnet as the Virtual IP.

The two new configuration directives are persist rdp-cookie and balance rdp-cookie. These instruct HAProxy to inspect the incoming RDP connection for a cookie; if one is found, it is used to persistently direct the connection to the correct real server. The two tcp-request lines help to ensure that HAProxy sees the cookie on the initial request.

The only other tweak needed is to increase the clitimeout and srvtimeout values to one hour. In testing, this was found to be necessary to keep idle RDP sessions established.

Testing involved making multiple connections with different usernames, from varying IP addresses, using both Windows XP Professional and Linux clients. Sessions were disconnected and reconnected, and real servers removed from the cluster and re-inserted.

We found that, once a user had established a session with a particular real server, that user consistently reconnected to the correct server if it was available. When we removed and re-inserted servers, existing sessions were unaffected. After a simulated server failure, users could start a session on the remaining server.

When a failed server was brought back on-line, users that had been connected to that server would reconnect to it again – even if they had started a new session on the other server in the meantime. This may not be what you want, and requires further testing.

With client and server time-outs set to one hour, we were able to leave idle sessions running for 16 hours without problems.

For more information on the new configuration options, see the development version of HAProxy’s Configuration Manual.

NB. For some daft reason Microsoft restricted the login cookie in RDP to 9 characters! Now as the domain is usually listed first (mydomain/myusername) the first 9 characters may always be the same and RDP cookie session persistence wont work. Two work arounds for this are either reduce the length of your domain name (ouch!) OR use the myusername@mydomain format when you log in….

So what about Microsoft Connection Broker (session directory or whatever they call it) ?

A simple one line change in your HAProxy configuration (RDP Connection Broker):

#Balance rdp-cookie ->        balance leastconn
i.e.
   defaults
        clitimeout 1h
        srvtimeout 1h
   listen VIP1 192.168.0.10:3389
        mode tcp
        tcp-request inspect-delay 5s
        tcp-request content accept if RDP_COOKIE
        persist rdp-cookie
        balance leastconn
        option tcpka
        option tcplog
        server Win2k8-1 192.168.0.11:3389 weight 1 check   inter 2000 rise 2 fall 3
        server Win2k8-2 192.168.0.12:3389 weight 1 check   inter 2000 rise 2 fall 3
        option redispatch

Note that this is only a fragment of the haproxy.cfg file, showing the relevant options.

Its about time we updated this post for the juicy new features in HAProxy – Development 1.5-dev7

Their were a couple of the problems with the hash method used with RDP cookie load balancing (as described above):

  1. Lots of people would like to use least connection load balancing with WTS/RDP clusters (this is not possible with a HASH based persistence method).
  2. When you add or remove servers the HASh table gets re-configured i.e. users hit the wrong server.

So Loadbalancer.org took the decission to sponsor the development of a stick-table based RDP persistence (we sponsored the origional source IP stick table work as well). When we looked at it in more detail we decided that what we needed was:

  1. Flexible stick tables that could be used for multiple future requirements i.e. SSL Session ID persistence.
  2. RDP stick table support in order to enable least connection based scheduling.
  3. Some way of restoring stick tables on session restart (and also replication to other HAProxy instances).
  4. Ensuring that TCP connections are properly closed on server failure (especially important on long connections).
  5. Ensuring that the stick table is cleared out on server failure.
  6. And finaly making sure that the fallback server can be made non-sticky! (really irritating if you get stuck on the sorry site down page).

To cut a long story short lets just dive in with a full configuration file and explain it as we go:

#HAProxy configuration file generated by LB Cloud appliance
global
	#uid 99
	#gid 99
	daemon
	stats socket /var/run/haproxy.stat mode 600 level admin
	log 127.0.0.1 local4
	maxconn 40000
	ulimit-n 81001
	pidfile /var/run/haproxy.pid
defaults
	log global
	mode	http
	timeout connect	4000
	timeout client	42000
	timeout server	43000
	balance	roundrobin
peers 	localpeer
	peer loadbalancer localhost:8888
listen	stats :7777
	stats	enable
	stats	uri /
	stats hide-version
	option	httpclose
frontend F1
	bind *:3389
	maxconn 40000
	default_backend B1
	mode tcp
	option tcplog
backend B1
	mode tcp
	option tcpka
	balance leastconn
	tcp-request inspect-delay 5s
	tcp-request content accept if RDP_COOKIE
	persist rdp-cookie
	stick-table type string size 204800 expire 120m
	stick on rdp_cookie(mstshash)
	server R1 www.loadbalancer.org:3389 weight 1 check port 3389 inter 2000 rise 2 fall 3 on-marked-down shutdown-sessions
	server R2 www.clusterscale.com:3389 weight 1 check port 3389 inter 2000 rise 2 fall 3 on-marked-down shutdown-sessions
	server backup us.loadbalancer.org backup non-stick
	option redispatch
	option abortonclose

An important new section is the peers section:

peers 	localpeer
	peer loadbalancer localhost:8888

In this configuration we are syncronising all of the stick table information with localhost:8888 (it could be with another HAProxy instance for session table high-availability).
When HAProxy restarts it will run existing sessions on the old process until they expire, only new sessions will run on the new HAProxy instance (this can get quite confusing as the stats socket or page will only show the new sessions (not the old ones)
You will need to change your HAProxy start up scripts:

start() {
  /usr/local/sbin/$BASENAME -L loadbalancer -c -q -f /etc/$BASENAME/$BASENAME.cfg
  if [ $? -ne 0 ]; then
    echo "Errors found in configuration file."
    return 1
  fi

  echo -n "Starting $BASENAME: "
  daemon /usr/local/sbin/$BASENAME -D -f /etc/$BASENAME/$BASENAME.cfg -p /var/run/$BASENAME.pid -L loadbalancer
  RETVAL=$?
  echo
  [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$BASENAME
  return $RETVAL
}
reload() {
  /usr/local/sbin/$BASENAME -L loadbalancer -c -q -f /etc/$BASENAME/$BASENAME.cfg
  if [ $? -ne 0 ]; then
    echo "Errors found in configuration file."
    return 1
  fi
  /usr/local/sbin/$BASENAME -D -L loadbalancer -f /etc/$BASENAME/$BASENAME.cfg -p /var/run/$BASENAME.pid -sf $(cat /var/run/$BASENAME.pid)
}

The important thing is that the peers definition “loadbalancer” must be prsent in both the start up scripts and the haproxy.cfg file.

Now we have the new section to make the stick table use RDP cookies and the least connection scheduler:

	balance leastconn
	tcp-request inspect-delay 5s
	tcp-request content accept if RDP_COOKIE
	persist rdp-cookie
	stick-table type string size 204800 expire 120m
	stick on rdp_cookie(mstshash)

And the new clean and quick session kill options + making the backup server not go in the stick table:

	server R2 www.clusterscale.com:3389 weight 1 check port 3389 inter 2000 rise 2 fall 3 on-marked-down shutdown-sessions
	server backup us.loadbalancer.org backup non-stick

I probably havent explained all that very well…

but feel free to ask questions :-) .




18 Responses to “Load balancing Windows Terminal Server – HAProxy and RDP Cookies or Microsoft Connection Broker”

  1. Malcolm Says:

    It is great news that HAProxy now supports RDP Cookies! Now all it really needs for full functionality is some kind of CPU idle feedback method from the real servers. Enabling HAProxy to distribute new clients to the least loaded servers, this has been talked about on the HAProxy mailing list quite a lot so no doubt it will be on the way soon.
    We have a Windows feedback service installer that responds to telnet on port 6666 with the CPU idle figure (0-100), I’d be happy to get the programmer to open source it under the GPL if anyone wants to use it?

  2. Terry Vercoe Says:

    Hi Malcom

    It is Terry from New Zealand, (It’s been a few years since we last talked).

    I think it might be of interest to evaluate using this type of perpetual trial model (1Mb) and then pay as you grow for your excellent VSA product.

    It would gain traction with certain cloud providers.

    http://jariangibson.com/2009/09/18/netscaler-vpx-express-is-a-must-for-all-xenserver-environments/

    http://jariangibson.com/2009/12/01/choosing-a-netscaler-hardware-appliance-or-virtual-appliance/

    Regards

    Terry Vercoe

  3. Malcolm Turnbull Says:

    Terry,
    Yes long time no talk.. I’ve just got back from a cruise round New Zealand with the family (It was fantastic).

    At the moment XEN proprietary or open source has very little traction with paying customers. But yes long term we are planning a whole host of virtualized load balancers like our current VMWare appliance (similar idea to Netscalers) i.e. an exact copy of our hardware OS/Software Stack runing on XEN/Microsoft etc. We have an Amazon EC2 platform close to release which relies heavily on HAProxy (as a lot of the layer 4 technology won’t work in the Amazon cloud). We also have several projects with ISPs integrating our load balancer to their various control panels.

    Long term I think it would be great for the market if Google and Amazon came together to offer an open source cloud standard that would provide a platform with enough users to make commercial development of products a worthwhile effort; production cost wise (we’ve only had 3 serious XEN enquires but loads of VMWare sales…).

    Our EC2 cloud development has taken over almost a year and that starts getting expensive! Maybe we should look at outsourcing all of our development to India?

  4. Shyam Says:

    Hi Guys

    I have tried using haproxy for rdp with Win Xp. As you know win xp allows only one connection. I am using HAproxy and above configuration. when i try connecting to xp through my haproxy server it does the connection perfectly, but the problems are
    1) No persistence
    2) Existing connection gets disconnected if someone else tries to get connected to the same machine.[XP problem]

    How do i overcome these issues.
    All help greatly appreciated.

    Thanks
    Shyam

  5. Simon Says:

    Shyam,

    Set the maxconn 1 attribute for each server.

    Here’s an example from our webserver haproxy config:

    listen webfarm *:8080
    mode http
    stats enable
    stats uri /admin?stats
    stats auth admin:secretpass
    balance roundrobin
    log global
    cookie JSESSIONID prefix
    option httpclose
    option forwardfor

    server web01 192.168.2.101:8000 maxconn 1 check
    server web02 192.168.2.102:8000 maxconn 1 check
    server web03 192.168.2.103:8000 maxconn 1 check

  6. steve Says:

    hi guys
    i have followed this work and i get much help by your method .but i still have some questions for the balance “rdp- cookie”, can you give me some advices:
    1: this balance mode “rdp- cookie” support rdp version 5.1, 6.1 or 7.0 ? in my test it can work normally only in rdp5.1.
    2: this balance mode said”Note that for this to work, the frontend must ensure that an RDP cookie is already present in the request buffer” , what kind of content is stored in this RDP cookie , where and how can i get this file.
    thanks

  7. Malcolm Turnbull Says:

    Steve,
    1) the method supports all versions you mentioned. We’ve tested several here, you can also refer to the following Microsoft link that gives more details:
    http://download.microsoft.com/download/9/5/E/95EF66AF-9026-4BB0-A41D-A4F81802D92C/MS-RDPBCGR.pdf
    its a big document! but on page 410 it does mention the versions that are supported.
    2) to view the ‘cookie’ , you could run a tcp dump and view it there , although the following Microsoft link gives an example:
    http://msdn.microsoft.com/en-us/library/cc240842(v=prot.10).aspx

  8. steve Says:

    thanks for your reply so soon, and it give me much help.
    i’ll introduce my test environment for you so you can make an exact analyse
    1: i follows your first configuration,but i have changed the win2k8 for win7, it means my terminal servers is win7, if this replacement can take any effect?
    2 : when a client send a rdp request to haproxy, what kind of information (the client’s IP address? MAC? or the username ?) will be stored in haproxy, so it can make sure that the same client will persist the same terminal server after disconnected for a few time ?
    3:if haproxy remember the IP address, this means that the same IP address will persist connecting the same terminal server?
    thank again

  9. Malcolm Turnbull Says:

    Steve,
    you should be able to use Win7, although you’ll need either Professional, Ultimate or Enterprise. The ‘balance rdp-cookie’ line causes haproxy to distribute incoming requests based on a hash of the username that is entered. If no username is present , then haproxy reverts to round robin. The load balancer does not actually log/store anything, when a new request comes in, the username is again hashed and for the same user this results in the same hash, and the request is then forwarded on to the same terminal server.

  10. steve Says:

    thank you very much !
    by your way i have done a test :
    i have three terminal servers of win7(they are all allows many connections ) and they are all in the same domain sdw-king.com, six users and the username is “test1, test2, test3, test4, test5, test6″, the haproxy is 1.4.15 in centos 5.5, i try to connect the terminal servers through haproxy with the same computer of win7, i open six rdp connection for test1 to test6 , badly the six users are sent to the same terminal server.
    how can it happen, if the test1…test6 have the same hash??
    another: if it have some rules for length of username ,for example 9 characters limit?

  11. steve Says:

    dear sir
    i have found a question for your answer, you said “The load balancer does not actually log/store anything” and ” when a new request comes in, the username is again hashed and for the same user this results in the same hash, and the request is then forwarded on to the same terminal server.” ,it seems they are contradiction.

    the question is if the load balancer doesn’t log anything, how can the haproxy know the same user result in the same hash, how does haproxy know “this hash” is same as the “last time hash”, and the last time hash was sent to 192.168.22.113(one of win7 terminal server for example) so this time the user should be send to 192.168.22.113 ??
    may be this is a large doubt for me!

  12. Malcolm Turnbull Says:

    Steve,

    The great thing about a HASH method on login/server is that it is always consistent (thats why HASHes get used so much in networking). However we are working with the Open Source community to add stick table support for RDP cookies which will be more flexible in the long term i.e. store the data in a table.

  13. steve Says:

    hi malcolm:
    i am reading the haproxy Configuration Manual of 1.4.15 , the content for “persist rdp-cookie” may be give us some advise.
    it says “This statement enables persistence based on an RDP cookie. The RDP cookie
    contains all information required to find the server in the list of known
    servers. So when this option is set in the backend, the request is analysed
    and if an RDP cookie is found, it is decoded. If it matches a known server
    which is still UP (or if “option persist” is set), then the connection is
    forwarded to this server. ”
    and ” Note that this only makes sense in a TCP backend, but for this to work, the
    frontend must have waited long enough to ensure that an RDP cookie is present
    in the request buffer.”
    but it still don’t tell us how does haproxy mach a RDP cookie to a known server ??
    i really thanks for your patience!

  14. Malcolm Turnbull Says:

    Steve,
    There are really two kinds of ‘cookies’, the first is the cookie that’s sent from the client in the Connection Request PDU, the second is actually called a ‘routing token’ which is used with microsoft session broker. The line ‘balance rdp-cookie’ in the haproxy config file causes haproxy to interact with the cookie in the request PDU, whilst ‘persist rdp-cookie’ causes haproxy to ustilise the routing token for interaction with session broker, both methods are detailed in the haproxy documentation. So it does depend on how you’re trying to set things up. For basic setups without session broker/directory, you only need ‘balance rdp-cookie’ in the config file. Also, for this to work you need to make sure that you enter the username and password on the initial login prompt, otherwise the cookie will not be present and therefore the username cannot be hashed. Another useful Micrsoft article that looks at the routing token is: http://go.microsoft.com/fwlink/?LinkId=90204

  15. steve Says:

    hi malcolm

    we are now using haproxy very well , thanks for your guide.

    can you tell me how many servers does one haproxy support?

  16. Malcolm Turnbull Says:

    Steve,

    I’m not entirely sure, somewhere between 200 and 40000?……
    The main thing restricting you will be the CPU load of all of the health checks…

  17. George Says:

    Excuse me can you post the whole conf file?

  18. Malcolm Turnbull Says:

    I think the short answer is ‘a lot’ , your two pinch points will be the total number of TCP sockets and the total load of the health checks but I don’t see a problem with 300 servers. Probably best to ask the HAProxy mailing list and give them very specific information about the load you are expecting….

Leave a Reply

CAPTCHA Image Audio Version
Reload Image