Forum Discussion
If I may add, stacked APMs in an "air gap" configuration like this can be challenging. When you first connect to an APM VIP, you get that initial 302 redirect to /my.policy to start the session (and get the session cookie). So it's not just the cookies that are getting confused, but when you start an APM session on the external APM, then transit to the internal APM, that internal APM's initial 302 redirect will confuse the external APM. The way I've generally handled this is to:
Use an iRule on the external APM to "manage and store" the session cookies from the internal APM. So when the internal APM sends its cookies, I store those in the external APM's user session and remove them from the stream. When a request comes in I remove the external APM's cookies from the stream and replace with the internal APM's cookies.
Put the internal APM into "clientless-mode". This has the effect of disabling the initial 302 redirect to /my.policy, but also prohibits "blocking" mechanisms like logon forms, message boxes, and webtops. You can do pretty much anything else in the visual policy and all SSO should work. To get user credentials to the internal APM (in the absence of an internal logon form), I would request them on the external APM and send them as HTTP headers in the stream.
Here's a version of the iRule used on the external APM:
when HTTP_REQUEST_RELEASE {
This event fires at the end of the access policy (just before releasing to the internal LTM) and
blocks external APM session cookies from reaching the internal APM
foreach externalcookie {"MRHSession" "LastMRH_Session" "F5_ST"} {
HTTP::cookie remove $externalcookie
}
When initiating an APM policy, it sets a series of client session cookies. In this case the external
APM captures the internal APM policy session cookies, stores them in the external session cache as
session variables (so that the internal session is "linked" to the external session), and removes
them from the wire to the client. When the client makes a new request, the internal session tokens
are retrieved from the external session cache and replayed to the internal APM as if the client
sent them. This code section extracts the internal session tokens from the external session cache
and inserts them as cookies to the internal APM.
set cookielist [list]
if { [ACCESS::session data get session.custom.internalMRH] ne "" } {
lappend cookielist "MRHSession=[ACCESS::session data get session.custom.internalMRH];"
}
if { [ACCESS::session data get session.custom.internalLMRH] ne "" } {
lappend cookielist "LastMRH_Session=[ACCESS::session data get session.custom.internalLMRH];"
}
if { [ACCESS::session data get session.custom.internalF5ST] ne "" } {
lappend cookielist "F5_ST=[ACCESS::session data get session.custom.internalF5ST];"
}
reformat incoming Cookie header to include all of the locally managed cookies in one Cookie header
set cookiehdr [HTTP::header "Cookie"]
HTTP::header remove "Cookie"
HTTP::header insert "Cookie" "[join $cookielist] $cookiehdr"
The clientless-mode header tells the internal APM to create the session without the normal series of
redirects. The remaining headers are used to transmit authenticated user data to the internal APM.
HTTP::header replace "clientless-mode" 1
This is the data that will be passing to the internal APM - define HTTP headers and values here
HTTP::header replace AGUSER "joe.user"
HTTP::header replace AGDOMAIN "CHARLIE"
}
when HTTP_RESPONSE {
The internal APM's session initiation process sends a series of Set-Cookie headers. This code section
captures those Set-Cookies, stores them in the external session cache, and removes them from the wire to the client.
if { [HTTP::cookie exists "MRHSession"] } {
ACCESS::session data set session.custom.internalMRH [HTTP::cookie value "MRHSession"]
HTTP::cookie remove "MRHSession"
}
if { [HTTP::cookie exists "LastMRH_Session"] } {
ACCESS::session data set session.custom.internalLMRH [HTTP::cookie value "LastMRH_Session"]
HTTP::cookie remove "LastMRH_Session"
}
if { [HTTP::cookie exists "F5_ST"] } {
ACCESS::session data set session.custom.internalF5ST [HTTP::cookie value "F5_ST"]
HTTP::cookie remove "F5_ST"
}
}
Then on the internal APM VIP, something like this:
when CLIENT_ACCEPTED {
This command MUST be here
ACCESS::restrict_irule_events disable
}
when ACCESS_SESSION_STARTED {
The authenticated user data is being transmitted from the external APM via HTTP headers. This section
extracts those values and inserts them back into APM access policy session variables for use by the
server side authentication process.
if { [HTTP::header AGUSER] ne "" } {
ACCESS::session data set session.logon.last.username [string trim [HTTP::header AGUSER]]
}
}
It would conceivably be possible to not set clientless-mode on the internal APM VIP, and then handle all of the redirects and cookies in the external APM VIP's iRule, but that would be even less intuitive, and require not just changing the session cookies but also changing the redirects and internal APM URI patterns.
- MarvinNov 04, 2021Cirrocumulus
Hi Kevin, great Irule seems to work when having portal mode because all URLs are rewritten, when using LTM+APM mode (as you already mentioned) we need to map and redirect or rewrite all embedded F5 urls so they wont be processed by the external F5. We tried to redirect /my.policy to /policy and then at request release to rewrite it back to /my.policy and seems to work however the cookie header is empty when using your Irule. Do you have any guidance on how we could perform this correctly in APM+LTM mode with 2 F5 APM chained?
- MarvinNov 04, 2021Cirrocumulus
we cant modify the internal F5 so it needs to be fixed on the external F5