21-Mar-2020 14:38
Hello,
Trying to add a virtual server behind an APM policy. The APM and LTM works on different F5 appliances and both are physical. LTM side is very basic and it was built by an iApp. It just serves an RDP service on two different Windows hosts. LTM virtual server uses Msrdp persistence profile. While testing, i can connect to that virtual server with native rdp application and it serves well. LTM virtual server manages sessions perfectly and keeps persistence record for each connection asit sholud be.
The APM side is already been there since couple of years. I just trying to add that LTM virtual Server as an RDP resource into the APM policy. The policy building phase is already done and when i click on RDP link on the webtop, APM prepare rdp file and download it. When i click "Open" button on for that file native rdp client application loads session and opens a window for it. So far good.
When session established by APM with SSO option enabled, LTM virtual server can't see the username and this is causing the persistence issue. If i disable SSO on APM, the LTM virtual server sees the username field and maintains the persistence.
While struggling this issue a couple of idea came up.
I feel like i missed some point obviously. Is there any other way to solve this issue ?
Solved! Go to Solution.
14-Mar-2021
00:39
- last edited on
22-Nov-2022
15:12
by
JimmyPackets
There is a good news 🙂
We solved it and provided the solution to F5 TAC to maybe enhance it a little bit or publish it to help others after examining it.
But it works fine in our environment and solved the persistence problem.
We used some of your irule and enhance it a little to use LTM load balance.
So, we have an iRule that is doing OTP SMS authentication and it is called from the VPE to send the OTP code to the end user. So we inserted at the top on the iRule a process to pick an available pool member using round robin method:
proc pickOneAvailable { } {
set poolNameEnc TWFydmFblablablaBTg==
pool [b64decode $poolNameEnc]
if {[active_members [b64decode $poolNameEnc]] < 1}{
set mypick "NULL"
} else {
LB::mode rr
set mypick [LB::select]
#log local0. "my pick is $mypick"
eval $mypick
set retVal [getfield [LB::server addr] % 1]
#log local0. "my retVal is $retVal"
}
}
And added this section ""PopulateDestIP"" at the very end of "when ACCESS_POLICY_AGENT_EVENT { " section
"PopulateDestIP" {
set uName [ACCESS::session data get session.logon.last.username]
set sessionSID [ACCESS::session sid]; log local0. "DEBUG ACCESS SID = $sessionSID"
set nextNode [call pickOneAvailable ]
if { $nextNode == "NULL" } {
#log local0. "Sorry, No Member could pick from pool."
} else {
ACCESS::session data set "session.logon.rdp.dest" $nextNode
#log local0. "This user $uName has picked this server: $nextNode"
}
unset uName
unset sessionSID
}
The " PopulateDestIP " will run/call the process " pickOneAvailable " and get the available node and put it into a variable named " $nextNode "
Then it will put this variable into a APM variable named " session.logon.rdp.dest "
After then I take the "session.logon.rdp.dest" variable and use it in the APM RDP configuration [access > connectivity/VPN > VDI/RDP > in the RDP destination I choose type "hostname" and put this variable there " %{session.logon.rdp.dest} " ].
Now, in the VPE, before doing the LDAP query for group membership, I put a Custom iRule Event Agent to call the " PopulateDestIP " )
That’s it. Now when the user login, he will get at the first beginning the IP address of the RDP server using the LTM pool and method round robin.
Now, even if the user got disconnected from the RDP session for any reason or logged out from the RDP server but did not got disconnected from the APM session (he didn't logout entirely from the APM) he will always get the same RDP server because it is written "coded" in the native RDP file he downloads.
So the strategy is :
28-Mar-2020 07:03
Hi
For the records, i've solved this issue with an iRule.
29-Apr-2020 10:01
that is very nice Torijori, could you share the iRule for others that have this issue?
29-Apr-2020 12:44
Sure. But i think that i choose the hard way. Because, i needed session persistence feature before starting to write an iRule. While writing the iRule i've realised that i have to write other features like load balancing, monitoring. All those features are built-in abilities of F5 and deciding to use an iRule for them is a risky move. By deciding to use an irule for that i forced myself to refuse to use basic principles of the load balancing. Regret ? Nope, i think it was the funiest way learn something.
Here is the link:
https://github.com/GlynisBarberBrandon/F5.git
01-May-2020 09:48
haha, awesome, best way to learn for sure.
08-Feb-2021 01:12
Hello,
I'm struggling with the same
Can't see neither the usename nor the client IP any more after moving from ActiveX RDP to Native. And msrdp persistence not working also
I created ticket for that, and I hope if the support will share an irule which is much simple for us
08-Feb-2021 02:58
Fingers crossed
10-Feb-2021 06:16
No luck actually.
They say it is an internal known bug "MSRDP based persistence with encryption enabled "and no iRule they can create for us in the mean time
This is all because of the encrypted entire RDP traffic because of the SSO.
So it is either to remove sso on the native rdp, or this is what it is ..
15-Feb-2021
05:45
- last edited on
04-Jun-2023
21:03
by
JimmyPackets
I found this iRule that might help
https://www.devcentral.f5.com/s/question/0D51T00006i7eRw/source-ip-and-port-persistence
it is hashing the source IP & source port and uses that hash into a universal persistence for 5 minutes idle timeout
when CLIENT_ACCEPTED {
set client_remote "[IP::client_addr]:[TCP::client_port]"
persist hash ($client_remote) 300
log local0. "Connection: Client($client_remote)"
}
I'll test it to see if this is the solution
But I wonder if source port will be used twice then does it generated the same hash ?
15-Feb-2021 08:38
That attitude in their response is totally unacceptable. The identified bug is still in their responbility.
15-Feb-2021 08:43
That is clever actually. The iRule is small and promising. Why i couldn't think an irule like this before ?
21-Feb-2021 08:16
actually and after my experience, I don't recommend the use of this irule. because it is using hash of the source port and source ip, and the problem here is with the source port.
So let's assume that a client made a connection, established, but after few minutes for some reason his local internet disconnected and he need to connect back, then most likely he will use different source port. the persistence will not work ....
your irule is good but not for our situation, we have RDP pool contains 80 servers. imagine how big and complicated the irule would look like if we used it.
still waiting for advise from f5 support. I hope they will help us.
21-Feb-2021
12:18
- last edited on
04-Jun-2023
21:02
by
JimmyPackets
Continuing to what I mentioned before
The hash irule is not useful for another reason, which is because of the gatewayaccesstoken, which is valid to run within 90 seconds And if the client have a bad internet connection and got disconnected let say after 5 minutes the token will not be valid anymore and the rdp will not even count to 20 to reconnect and this message with the three reasons will appear:
RemoteApp Disconnected
Remote Desktop can’t connect to the remote computer <remote computer> for one of these reasons
Back to the client, he connects again via new rdp file, and then mapped to difference rdp server, which means all his unsaved work is gone.
13-Mar-2021 09:03
Hi again,
Is there any good or bad news ?
14-Mar-2021
00:39
- last edited on
22-Nov-2022
15:12
by
JimmyPackets
There is a good news 🙂
We solved it and provided the solution to F5 TAC to maybe enhance it a little bit or publish it to help others after examining it.
But it works fine in our environment and solved the persistence problem.
We used some of your irule and enhance it a little to use LTM load balance.
So, we have an iRule that is doing OTP SMS authentication and it is called from the VPE to send the OTP code to the end user. So we inserted at the top on the iRule a process to pick an available pool member using round robin method:
proc pickOneAvailable { } {
set poolNameEnc TWFydmFblablablaBTg==
pool [b64decode $poolNameEnc]
if {[active_members [b64decode $poolNameEnc]] < 1}{
set mypick "NULL"
} else {
LB::mode rr
set mypick [LB::select]
#log local0. "my pick is $mypick"
eval $mypick
set retVal [getfield [LB::server addr] % 1]
#log local0. "my retVal is $retVal"
}
}
And added this section ""PopulateDestIP"" at the very end of "when ACCESS_POLICY_AGENT_EVENT { " section
"PopulateDestIP" {
set uName [ACCESS::session data get session.logon.last.username]
set sessionSID [ACCESS::session sid]; log local0. "DEBUG ACCESS SID = $sessionSID"
set nextNode [call pickOneAvailable ]
if { $nextNode == "NULL" } {
#log local0. "Sorry, No Member could pick from pool."
} else {
ACCESS::session data set "session.logon.rdp.dest" $nextNode
#log local0. "This user $uName has picked this server: $nextNode"
}
unset uName
unset sessionSID
}
The " PopulateDestIP " will run/call the process " pickOneAvailable " and get the available node and put it into a variable named " $nextNode "
Then it will put this variable into a APM variable named " session.logon.rdp.dest "
After then I take the "session.logon.rdp.dest" variable and use it in the APM RDP configuration [access > connectivity/VPN > VDI/RDP > in the RDP destination I choose type "hostname" and put this variable there " %{session.logon.rdp.dest} " ].
Now, in the VPE, before doing the LDAP query for group membership, I put a Custom iRule Event Agent to call the " PopulateDestIP " )
That’s it. Now when the user login, he will get at the first beginning the IP address of the RDP server using the LTM pool and method round robin.
Now, even if the user got disconnected from the RDP session for any reason or logged out from the RDP server but did not got disconnected from the APM session (he didn't logout entirely from the APM) he will always get the same RDP server because it is written "coded" in the native RDP file he downloads.
So the strategy is :