Forum Discussion

Maxim_Taskov_90's avatar
Maxim_Taskov_90
Icon for Nimbostratus rankNimbostratus
Sep 16, 2010

Universal or Hash Persistence for Windows Terminal Services Gateway Traffic

I need help creating persistence profile for Windows Terminal Services (TS) Gateway traffic. I really have no preference regarding the type of persistence but it looks like hash or universal with an iRule is the only way. You probably know this but I will say it anyway... the TS Gateway product is part of Windows and it encapsulates the terminal server RDP (tcp/3389) traffic in HTTPS. This eliminates the need for the 3389 port. Here is my situation...

 

 

If I use Source Address persistence, everything is fine but it causes uneven load distribution between the terminal servers as I have large groups of users coming from the same source IP address, e.g. remote offices. I also know that Windows Session Directory is probably the way to persist termianl services sessions but for many reasons I cannot use that service. My configuration is a single SSL Virtual Server (VS) used for the TS Web and TS Gateway services. The VS performs SSL offload. The pool assigned to teh VS is port 80 (HTTP). The connection sequence is:

 

 

1. A user connects to the TS Web service and the following is seen by the BIG-IP when I log the source IP and port:

 

 

Persisting on: 1.2.3.4:59401

 

Persisting on: 1.2.3.4:59402

 

Persisting on: 1.2.3.4:59403

 

Persisting on: 1.2.3.4:59404

 

Persisting on: 1.2.3.4:59405

 

Persisting on: 1.2.3.4:59406

 

Persisting on: 1.2.3.4:59407

 

 

2. When a user clicks on a connect button within the TS Web site, the following is logged:

 

 

Persisting on: 1.2.3.4:59410

 

Persisting on: 1.2.3.4:59411

 

 

As you can imagine the above log is from an attempt to persist on client IP and Port, which was not ideal and doesn't cover all requirements but it would have patched the situation for a while. I used the following iRule along with Universal persistence profile for the test:

 

 

when CLIENT_ACCEPTED {

 

if { [TCP::client_port] and [IP::client_addr] !=0 } {

 

persist uie "[IP::client_addr]:[TCP::client_port]"

 

log local0. "Persisting on: [IP::client_addr]:[TCP::client_port]"}

 

}

 

 

I also stumbled on and tried the following rule:

 

 

when CLIENT_ACCEPTED {

 

TCP::collect }

 

 

when CLIENT_DATA {

 

TCP::collect 25 binary scan [TCP::payload] x11a* msrdp

 

log local0. "Contents after binary scan: $msrdp"

 

if { [string equal -nocase -length 17 $msrdp "cookie: mstshash="] } {

 

set msrdp [string range $msrdp 17 end] set len [string first "\n" $msrdp] if { $len == -1 } { TCP::collect return }

 

if { $msrdp contains "@" } { if { $len > 5 } { incr len -1 log local0. "Data Persisting on: [getfield $msrdp "@" 1]"

 

persist uie [getfield $msrdp "@" 1] 300 } }

 

else { persist uie $msrdp 300 }

 

}

 

TCP::release

 

}

 

 

The above iRule logs the following on the TCP binary scan and never proceeds to the persistence section:

 

 

Contents after binary scan: L?6óÅÓÉ?èl0íÓ;1/7?¶?ë?/?ÚC?Õó / 5 ÀÀ ÀÀ 2 8 4 offsiteqa.mycompany.com ÿ

 

 

I think the iRule was designed for RDP traffic.

 

 

I also tried the built-in SSL persistence but that fails because of the two sessions and two source ports used for the initial connection.

 

 

And lastly, I tried the following iRule from the F5 guide on configuring Windows Terminal Services persistence:

 

 

when HTTP_REQUEST {

 

if { [HTTP::header exists "Authorization"] } {

 

persist uie [HTTP::header "Authorization"] }

 

}

 

 

Needless to say, it too failed. Any idea on how to persist on Terminal Services Gateway traffic, which is HTTPS but the client is not a browser, would be greatly appreciated.

 

 

Thanks, Maxim

 

3 Replies

  • Things are starting to look even worse. The following is a log of the SSL session IDs for the first connection:

     

     

    : b721ff0ccc8aaed400c42d76ed878cf6cc83f7449a0b2b16eb9d2ec3a1bb330b

     

    : b721ff0ccc8aaed700c42d76ed878cf7cc83f7449a0b2b16eb9d2ec3a1bb330b

     

    : b721ff0ccc8aaed700c42d76ed878cf7cc83f7449a0b2b16eb9d2ec3a1bb330b

     

    : b721ff0ccc8aaed400c42d76ed878cf6cc83f7449a0b2b16eb9d2ec3a1bb330b

     

     

    My heart skipped a beat at first because these look the same...oh well, there is a one digit difference.
  • I've heard SSL is a bad persistence strategy because of the constant re-negotiations...

    Here are some good reads:

    http://devcentral-sea.f5.com/Forums/tabid/1082223/asg/50/showtab/groupforums/aff/5/aft/5702/afv/topic/Default.aspx

    http://www.f5.com/solutions/applications/microsoft/windows-terminal-server/

    http://devcentral.f5.com/Forums/tabid/1082223/asg/50/showtab/groupforums/aff/5/aft/32250/afv/topic/Default.aspx32284

    Here's a rule Jason did:

    
    when CLIENT_ACCEPTED { 
    TCP::collect 
    } 
    when CLIENT_DATA { 
    TCP::collect 25 
    binary scan [TCP::payload] x11a* msrdp 
    if { [string equal -nocase -length 12 $msrdp "cookie: msts"] } { 
    set msrdp [string range $msrdp 12 end] 
    set len [string first "\n" $msrdp] 
    if { $len == -1 } { 
     Didnt get whole cookie collect more 
    TCP::collect 
    return 
    } 
    if { $msrdp starts_with "hash=" } { 
     No session directory - username used instead 
    if { $len > 5 } { 
    incr len -1 
    set record [string tolower [string range $msrdp 5 $len] ]
    log "adding persistence record - $record" 
    persist uie $record 1801  adjust your timeout (in seconds)
    } else { 
    log "No username - not persisting" 
    } 
    } 
    } else { 
    log "Cookie not found" 
    } 
    TCP::release 
    }
    

    And John's rule in the last link:

    
    
    when CLIENT_ACCEPTED { 
    TCP::collect 
    } 
    when CLIENT_DATA { 
    
    TCP::collect 25 
    
    binary scan [TCP::payload] x11a* msrdp 
    log local0. "Contents after binary scan: $msrdp" 
    
    if { [string equal -nocase -length 17 $msrdp "cookie: mstshash="] } { 
    
    set msrdp [string range $msrdp 17 end] 
    set len [string first "\n" $msrdp] 
    if { $len == -1 } { 
    TCP::collect 
    return 
    } 
    if { $msrdp contains "@" } { 
    log local0. "Setting data to: [getfield $msrdp "@" 1]" 
    set username [getfield $msrdp "@" 1] 
    } elseif { $msrdp contains "\\" } { 
    log local0. "Setting data to: [getfield $msrdp "\\" 3]" 
    set username [getfield $msrdp "\\" 3] 
    } else { 
    set username $msrdp 
    log local0. "Setting data to: $msrdp" 
    } 
    set finalusername [string tolower $username] 
    set finalusername [string trim $finalusername] 
    log local0. "User Being Persisted is: |$finalusername|" 
    persist uie $finalusername 10800 
    } 
    TCP::release 
    } 
    
  • Thanks for the reply Chris. I agree on the SSL persistence.

     

     

    The resources you suggested are great but do not work. I have tried Jason's iRule before and I just tried John's iRule and both with the same result. The log shows the TCP payload after the binary scan (see below) but the iRule never moves past the binary scan and the connection never succeeds; no persistence, therefore the two sessions initiated by the client end up on different servers:

     

     

    Contents after binary scan: L”ƨ2—F÷a~£?6Uí–©®]_Eƒ¾ùJ¡������/��5���� ÀÀ ÀÀ��2��8��������4������������qa.mysite.com�� ����������������ÿ���� Contents after binary scan: L”ÆÜüoæ\Šé,NéûðahAµRl|àÍ“&������/��5���� ÀÀ ÀÀ��2��8��������4������������qa.mysite.com�� ����������������ÿ����

     

     

    I think John and Jason's rules are more for a native RDP traffic rather than for TS Gateway traffic, which is HTTP encapsulated RDP. The iRule suggested in the F5 deployment guide is simple and should work, but it doesn't and again it is something I have tried. I contacted my local F5 support engineer and we modified the F5 deployment guide iRule to add some logging and see what happens. The iRule we used is:

     

     

    when HTTP_REQUEST {

     

    log local0. "In HTTP_REQUEST"

     

    if { [HTTP::header exists "Authorization"] } {

     

    log local0. "Auth header contains: [HTTP::header "Authorization"]

     

    persist uie [HTTP::header "Authorization"] }

     

    }

     

     

    The result in the log is:

     

     

    In HTTP_REQUEST Auth header contains: NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAHIXAAAADw==

     

    In HTTP_REQUEST Auth header contains: NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAHIXAAAADw==

     

    In HTTP_REQUEST Auth header contains: NTLM T1RMTVNTUAADAAAAGAAYAI4AAABgAWABpgAAABYAFgBYAAAADgAOAG4AAAASABIAfAAAAAAAAAAGAgAABYKIogYAchcAAAAPQcVyhyOrxM5Ogw7Sww+A/mEAcgByAF8AcQB1AGEAbABpAHQAeQB0AGEAcwBrAG8AdgBtAFcAMAAxADcAQQAyADgANgAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQYlDTW6rrDzWrCOEq5KMsBAQAAAAAAAFWgzrTLVssBj02c1n/8pMsAAAAAAgAWAEEAUgBSAF8AUQBVAEEATABJAFQAWQABABQAMAAxAFcAVABTAFEAQQAxADIAMQAEADQAcQB1AGEAbABpAHQAeQAuAGMAZQBuAGQAYQBuAHQAbQBvAGIAaQBsAGkAdAB5AC4AcQBhAAMASgAwADEAVwBUAFMAUQBBADEAMgAxAC4AcQB1AGEAbABpAHQAeQAuAGMAZQBuAGQAYQBuAHQAbQBvAGIAaQBsAGkAdAB5AC4AcQBhAAUAJABjAGUAbgBkAGEAbgB0AG0AbwBiAGkAbABpAHQAeQAuAHEAYQAHAAgAVaDOtMtWywEGAAQAAgAAAAgAMAAwAAAAAAAAAAAAAAAAMAAAoEwYmtbo42zqTq1A3sqmz1vJ7c7c4bx3bJHWAf5XesMAAAAAAAAAAAAAAAA= In HTTP_REQUEST Auth header contains: NTLM TlRMTVNTUAADAAAAGAAYAI4AAABgAWABpgAAABYAFgBYAAAADgAOAG4AAAASABIAfAAAAAAAAAAGAgAABYKIogYAchcAAAAP4KAqIPKyP+a3pYFJ50DqDmEAcgByAF8AcQB1AGEAbABpAHQAeQB0AGEAcwBrAG8AdgBtAFcAMAAxADcAQQAyADgANgAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN2GS3qAt2c0BsoYQNPqeTYBAQAAAAAAAFWgzrTLVssBZqkb+Fb6A5QAAAAAAgAWAEEAUgBSAF8AUQBVAEEATABJAFQAWQABABQAMAAxAFcAVABTAFEAQQAxADIAMQAEADQAcQB1AGEAbABpAHQAeQAuAGMAZQBuAGQAYQBuAHQAbQBvAGIAaQBsAGkAdAB5AC4AcQBhAAMASgAwADEAVwBUAFMAUQBBADEAMgAxAC4AcQB1AGEAbABpAHQAeQAuAGMAZQBuAGQAYQBuAHQAbQBvAGIAaQBsAGkAdAB5AC4AcQBhAAUAJABjAGUAbgBkAGEAbgB0AG0AbwBiAGkAbABpAHQAeQAuAHEAYQAHAAgAVaDOtMtWywEGAAQAAgAAAAgAMAAwAAAAAAAAAAAAAAAAMAAAoEwYmtbo42zqTq1A3sqmz1vJ7c7c4bx3bJHWAf5XesMAAAAAAAAAAAAAAAA=

     

    And the BIGIP persistence record created is:

     

     

    NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAHIXAAAADw==

     

     

    When the next user comes in, the result is:

     

     

    In HTTP_REQUEST Auth header contains: NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAHIXAAAADw==

     

    In HTTP_REQUEST Auth header contains: NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAHIXAAAADw==

     

    In HTTP_REQUEST Auth header contains: NTLM TlRMTVNTUAADAAAAGAAYAJAAAABgAWABqAAAABYAFgBYAAAAEAAQAG4AAAASABIAfgAAAAAAAAAIAgAABYKIogYAchcAAAAPQDE6mX1RhAWbUbtkXS8RF2EAcgByAF8AcQB1AGEAbABpAHQAeQB0AHMAYwBsAGkAZQBuAHQAVwAwADEANwBBADIAOAA2ADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbjR17/6/wJofRAPGQyDjxwEBAAAAAAAAuVvI+ctWywHVFUos0PDTCgAAAAACABYAQQBSAFIAXwBRAFUAQQBMAEkAVABZAAEAFAAwADEAVwBUAFMAUQBBADEAMgAxAAQANABxAHUAYQBsAGkAdAB5AC4AYwBlAG4AZABhAG4AdABtAG8AYgBpAGwAaQB0AHkALgBxAGEAAwBKADAAMQBXAFQAUwBRAEEAMQAyADEALgBxAHUAYQBsAGkAdAB5AC4AYwBlAG4AZABhAG4AdABtAG8AYgBpAGwAaQB0AHkALgBxAGEABQAkAGMAZQBuAGQAYQBuAHQAbQBvAGIAaQBsAGkAdAB5AC4AcQBhAAcACAC5W8j5y1bLAQYABAACAAAACAAwADAAAAAAAAAAAAAAAAAwAACgTBia1ujjbOpOrUDeyqbPW8ntztzhvHdskdYB/ld6wwAAAAAAAAAAAAAAAA== In HTTP_REQUEST Auth header contains: NTLM TlRMTVNTUAADAAAAGAAYAJAAAABgAWABqAAAABYAFgBYAAAAEAAQAG4AAAASABIAfgAAAAAAAAAIAgAABYKIogYAchcAAAAPDQffWvWP9QCEpQWgjk+IZ2EAcgByAF8AcQB1AGEAbABpAHQAeQB0AHMAYwBsAGkAZQBuAHQAVwAwADEANwBBADIAOAA2ADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1ujO7St7VqpQ1A3v95NqUwEBAAAAAAAAF77K+ctWywG+7hjFHTDUSAAAAAACABYAQQBSAFIAXwBRAFUAQQBMAEkAVABZAAEAFAAwADEAVwBUAFMAUQBBADEAMgAxAAQANABxAHUAYQBsAGkAdAB5AC4AYwBlAG4AZABhAG4AdABtAG8AYgBpAGwAaQB0AHkALgBxAGEAAwBKADAAMQBXAFQAUwBRAEEAMQAyADEALgBxAHUAYQBsAGkAdAB5AC4AYwBlAG4AZABhAG4AdABtAG8AYgBpAGwAaQB0AHkALgBxAGEABQAkAGMAZQBuAGQAYQBuAHQAbQBvAGIAaQBsAGkAdAB5AC4AcQBhAAcACAAXvsr5y1bLAQYABAACAAAACAAwADAAAAAAAAAAAAAAAAAwAACgTBia1ujjbOpOrUDeyqbPW8ntztzhvHdskdYB/ld6wwAAAAAAAAAAAAAAAA==

     

    And the original persistence record is overwritten as the only part BIGIP sees and records is:

     

     

    NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAHIXAAAADw==

     

     

    This header appears to be the same for every user and connection and is the same for the first two of four connections opened by the client. It is not until the third connection that the header becomes unique but BIGIP never records that part. Again with the help of my local F5 support engineer, we tried to create an MD5 hash of the whole thing using the following iRule.

     

     

    when HTTP_REQUEST {

     

    log local0. "In HTTP_REQUEST"

     

    if { [HTTP::header exists "Authorization"] } {

     

    log local0. "Auth md5:[md5 [HTTP::header "Authorization"]]"

     

    persist uie [md5 [HTTP::header "Authorization"]] }

     

    }

     

     

    The result was the same, the persistence record is only one and all users end up on the same server. So persistence with this iRule works but it is "too" persistent ;-). I guess BIGIP never goes past the first of the four records and it really needs to see and persist on the third and forth one. The F5 support engineer suggested that I open a support case with F5 since the TS Gateway persistence iRule is theirs and it is supposed to work. I will do that and hopefully this yields some better results. In the mean time, if anyone knows how to skip the first two HTTP headers and make BIGIP look at the third and fourth ones... please speak up.

     

     

    Good thing I don't have much hair or I would be pulling it all out just about now ;-)