Forum Discussion
Chrome V 124+ on MacOS - Virtual Server Access Issue
- Apr 22, 2024
Brad_Baker / LiefZimmerman / Eric_Chen / Stan_PIRON_F5 / Stanislas_Piro2
I have patched my copy of the original iRule with what I think is a working fix, but I would love if someone in the community could validate it, because my knowledge of the intricacies of the TLS packet is not strong.
Essentially, I have replaced this if check in the original...
# If valid TLS 1.X CLIENT_HELLO handshake packet if { [binary scan $payload cH4Scx3H4x32c tls_record_content_type tls_version tls_recordlen tls_handshake_action tls_handshake_version tls_handshake_sessidlen] == 6 && \ ($tls_record_content_type == 22) && \ ([string match {030[1-3]} $tls_version]) && \ ($tls_handshake_action == 1) && \ ($payloadlen == $tls_recordlen+5)} {
...with this block, which is intended to determine whether the reported TLS record length is longer than the payload we have and, if so, collect more packets until we have enough:
# Keep collecting if CLIENT_HELLO messages that span more than one packet... if {![info exists payloadscan]} { set payloadscan [binary scan $payload cH4Scx3H4x32c tls_record_content_type tls_version tls_recordlen tls_handshake_action \ tls_handshake_version tls_handshake_sessidlen] } if {($payloadscan == 6)} { if {($tls_recordlen < 0 || $tls_recordlen > 16389)} { # if we are asked to collect more than we will handle, bail... log local0.warn "[IP::remote_addr] : parsed TLS record length ${tls_recordlen} outside of handled length (0..16389)" reject return } elseif {($payloadlen < $tls_recordlen+5)} { # if we have not collected enough yet, collect some more TCP::collect [expr {$tls_recordlen+5 - $payloadlen}] return } } # If valid TLS 1.X CLIENT_HELLO handshake packet if {($payloadscan == 6) && \ ($tls_record_content_type == 22) && \ ([string match {030[1-3]} $tls_version]) && \ ($tls_handshake_action == 1) && \ ($payloadlen == $tls_recordlen+5)} {
This has allowed the rest of the original logic to capture the SNI server name and my services to resume operation.
However, one new thing of note, is that the resulting values in $tls_handshake_preferred_version now include an extra value. If I log this value, I can see...
- Firefox returns 0304 0303 0302 0301
- Chrome/Edge with --ssl-version-max=tls1.2 return 0303 only
- Chrome/Edge v124 return xAxA 0304 0303
The xAxA value changes every time -- so far, I have seen 1A1A, 7A7A, 8A8A, DADA and FAFA. (I do not see a 7Fxy, as indicated in the switch statement).
I am now wondering if this is just an implementation detail of the new Chrome TLS packet, or if I/we are now reading the preferred values from the wrong position in the payload.
Is anyone able to verify the above?
OK, so my $payloadlen for Chrome is now always 1380, while my $tls_recordlen is always 1750+. I'm guessing that 1380 is the MTU, so another TCP::collect needs to be worked into the procedure to capture the entire TLS record.
Now, I remember reading somewhere that running multiple TCP::collect was A Bad Thing(TM), but I don't recall the exact reasoning...
Brad_Baker / LiefZimmerman / Eric_Chen / Stan_PIRON_F5 / Stanislas_Piro2
I have patched my copy of the original iRule with what I think is a working fix, but I would love if someone in the community could validate it, because my knowledge of the intricacies of the TLS packet is not strong.
Essentially, I have replaced this if check in the original...
# If valid TLS 1.X CLIENT_HELLO handshake packet
if { [binary scan $payload cH4Scx3H4x32c tls_record_content_type tls_version tls_recordlen tls_handshake_action tls_handshake_version tls_handshake_sessidlen] == 6 && \
($tls_record_content_type == 22) && \
([string match {030[1-3]} $tls_version]) && \
($tls_handshake_action == 1) && \
($payloadlen == $tls_recordlen+5)} {
...with this block, which is intended to determine whether the reported TLS record length is longer than the payload we have and, if so, collect more packets until we have enough:
# Keep collecting if CLIENT_HELLO messages that span more than one packet...
if {![info exists payloadscan]} {
set payloadscan [binary scan $payload cH4Scx3H4x32c tls_record_content_type tls_version tls_recordlen tls_handshake_action \
tls_handshake_version tls_handshake_sessidlen]
}
if {($payloadscan == 6)} {
if {($tls_recordlen < 0 || $tls_recordlen > 16389)} { # if we are asked to collect more than we will handle, bail...
log local0.warn "[IP::remote_addr] : parsed TLS record length ${tls_recordlen} outside of handled length (0..16389)"
reject
return
} elseif {($payloadlen < $tls_recordlen+5)} { # if we have not collected enough yet, collect some more
TCP::collect [expr {$tls_recordlen+5 - $payloadlen}]
return
}
}
# If valid TLS 1.X CLIENT_HELLO handshake packet
if {($payloadscan == 6) && \
($tls_record_content_type == 22) && \
([string match {030[1-3]} $tls_version]) && \
($tls_handshake_action == 1) && \
($payloadlen == $tls_recordlen+5)} {
This has allowed the rest of the original logic to capture the SNI server name and my services to resume operation.
However, one new thing of note, is that the resulting values in $tls_handshake_preferred_version now include an extra value. If I log this value, I can see...
- Firefox returns 0304 0303 0302 0301
- Chrome/Edge with --ssl-version-max=tls1.2 return 0303 only
- Chrome/Edge v124 return xAxA 0304 0303
The xAxA value changes every time -- so far, I have seen 1A1A, 7A7A, 8A8A, DADA and FAFA. (I do not see a 7Fxy, as indicated in the switch statement).
I am now wondering if this is just an implementation detail of the new Chrome TLS packet, or if I/we are now reading the preferred values from the wrong position in the payload.
Is anyone able to verify the above?
- candcApr 22, 2024Cirrus
The 1A1A, 2A2A, etc values appear to align with a TLS extension called GREASE: and are probably OK.
I would now just appreciate any confirmation that calling TCP::collect from within CLIENT_DATA isn't going to cause any problems later down the line. At most I have tried to limit to two calls (the original one in CLIENT_ACCEPTED and one extra in CLIENT_DATA).
There is one call to TCP::release at the end of CLIENT_DATA, but it is only reached if we have collected enough data across those two TCP::collect calls.
So far, in my logging, the count of bytes released does appear to match the count of bytes collected -- I'm hoping that's all that needs to happen.
- Brad_BakerApr 22, 2024Cirrus
This works for me in Google Chrome 124 so tentative yay!! :) I guess my only concern would be your question about doing to TCP Collects.
- Brad_BakerApr 23, 2024Cirrus
candcI do see this:
Note: If multiple iRules attached to the same virtual execute this command, the last collect wins, meaning the earlier one will be ignored.
Here: https://clouddocs.f5.com/api/irules/TCP__collect.html
Could this be what you're thinking of/referring to?
- Brad_BakerApr 23, 2024Cirrus
For what its worth we just started using the revised iRule on our LTMs with around 25.5K concurrent connections. No issues have been found so far but we'll keep a close eye on things.
- LiefZimmermanApr 22, 2024Admin
candc - I'm no technician but I recognize work when I see it. Thank you.
(gotta say...my spam-radar went up for a second when I saw the external site link to GREASE. 😅)
I'll see if I can get anyone to help assess your long-term concern and the wrong payload position questions.Cheers.
- candcApr 23, 2024Cirrus
Yes, it was possibly that. It is not always easy to understand the implications of those advisories.
From what I can tell, it means TCP::payload will always be updated by the result of the most-recent call to TCP::collect and, because TCP::collect can be called with different parameters (length and skip), you may lose data if two calls collide with each other.
I don't think that is happening in my patch code above, but if another iRule was also calling TCP::collect, you could not be certain what TCP::payload would contain.
I also wondered whether there had to be a TCP::release for every call to TCP::collect, or if the one call to TCP::release would be enough. From what I can tell, the one call is enough, but I am keeping an eye on the memory stats of my device to try to determine if I am leaking by not explicitly calling TCP::release for every TCP::collect.
(To be honest, if I had to work an extra TCP::release into the code, I can't see where it would reliably go, and I assume I would have to therefore create my own buffer instead of using TCP::payload. If that is the case, I'm not sure it would be anywhere near as straightforward a fix.)
Recent Discussions
Related Content
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com