-
Notifications
You must be signed in to change notification settings - Fork 165
[connect-tcp] Improve recommendation for abrupt TLS termination #3141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
The current recommendation, to send a TLS Error Alert, is very difficult to implement. The new, relaxed recommendation allows a broader range of implementations, is much easier to implement, and is equally effective and secure.
draft-ietf-httpbis-connect-tcp.md
Outdated
|
|
||
| * Each CONNECT request requires a new TCP and TLS connection, imposing a higher cost in setup latency, congestion control convergence, CPU time, and data transfer. | ||
| * It may be difficult to implement the recommended unclean shutdown signals ({{closing-connections}}), as many TLS libraries do not support injecting TLS Alerts. | ||
| * It may be difficult to implement the recommended unclean shutdown signals ({{closing-connections}}), as TLS subsystems may close connections gracefully even when this is not desired. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The concern is not that TLS stacks do graceful closes even when asked to terminate a connection, but more that they do not use close_notify to terminate flows that end in normal circumstances, such that you cannot distinguish between a successful close (which is supposed to include close_notify) and an abortive close (which is supposed to exclude close_notify).
Now, if you are saying that it is ALSO hard to guarantee that an abortive close doesn't result in close_notify in other stacks, that's useful information. That further motivates the inclusion of FINAL_DATA. But it doesn't mean that you need to say anything here:
| * It may be difficult to implement the recommended unclean shutdown signals ({{closing-connections}}), as TLS subsystems may close connections gracefully even when this is not desired. |
After all, the stream is treated as incomplete unless a complete FINAL_DATA capsule is received.
Failing that, is "unclean" really the word that you want to use for this? Consider "abortive close" or "abrupt closure" or something along those lines.
| * It may be difficult to implement the recommended unclean shutdown signals ({{closing-connections}}), as TLS subsystems may close connections gracefully even when this is not desired. | |
| * It may be difficult to implement an abortive close ({{closing-connections}}), as TLS subsystems may close connections gracefully even when this is not desired. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The concern is not that TLS stacks do graceful closes even when asked to terminate a connection, but more that they do not use close_notify to terminate flows that end in normal circumstances
OK, I've added this to the list of reasons to avoid HTTP/1.1.
After all, the stream is treated as incomplete unless a complete FINAL_DATA capsule is received.
The purpose of abrupt closure is not to inform you that the incoming stream was truncated. FINAL_DATA indeed covers that. The purpose of abrupt closure is to inform you that your outgoing stream was truncated (or in reverse, that it was delivered in full). This can occur after the incoming FINAL_DATA, so it needs a separate signal.
Failing that, is "unclean" really the word that you want to use for this? Consider "abortive close" or "abrupt closure" or something along those lines.
Changed to "abrupt closure" throughout.
draft-ietf-httpbis-connect-tcp.md
Outdated
| - HTTP/2: RST_STREAM with CONNECT_ERROR | ||
| - HTTP/1.1 over TLS: a TLS Error Alert | ||
| - See {{!RFC9113}}, Sections 6.4 and 7. | ||
| - HTTP/1.1 over TLS: TCP shutdown without a TLS closure alert |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't this a TCP RST? It's true that a TCP shutdown will produce a similar effect, but a RST matches what you use for unsecured HTTP over TCP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sending a TCP RST (without sending close_notify) would be compliant here, but it is not the only option.
I wouldn't want to recommend TCP RST specifically. Sending a TCP RST on the outer connection breaks the usual confidentiality guarantees of "https" (e.g. revealing that this connection is likely a "connect-tcp" proxy, not a WebSocket). It's also harder to implement, unauthenticated, and increases the amount of data that is likely to be lost.
RST matches what you use for unsecured HTTP over TCP
In my view, each of these 4 recommendations describes how to signal an abrupt closure using the transport layer immediately below "connect-tcp".
draft-ietf-httpbis-connect-tcp.md
Outdated
| - HTTP/1.1 over TLS: a TLS Error Alert | ||
| - See {{!RFC9113}}, Sections 6.4 and 7. | ||
| - HTTP/1.1 over TLS: TCP shutdown without a TLS closure alert | ||
| - See {{!RFC8446, Section 6.1}}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed given that you are saying /without/ a TLS alert?
| - See {{!RFC8446, Section 6.1}}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"TLS closure alert" is a term of art that is not widely known outside of TLS. To know what a TLS closure alert is, and what it means to avoid sending one, I think we need a reference to the definition, which is in RFC 8446, Section 6.1.
Ideally I would hyperlink all of these references to the relevant sections without the explicit references (at least in the HTML output), but I couldn't figure out how to make kramdown-rfc do that.
Co-authored-by: Martin Thomson <[email protected]>
7992e54 to
de24813
Compare
kazuho
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have an editorial question but otherwise the PR looks good!
|
@martinthomson Do you still have concerns about this text change? |
martinthomson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for not tracking this closely enough. I don't think that where you landed is self-consistent.
| +---"abc"--->+-------DATA{"abc"}------->+---"abc"--->| | ||
| | | (... timeout @ A ...) | | | ||
| | +--------TLS Alert-------->+----RST---->| | ||
| | +--FIN (no close_notify)-->+----RST---->| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many implementation will treat this as a normal stream termination. That's because this is indistinguishable from a successful termination in the case that the TLS close_notify doesn't get sent (or doesn't make it through).
I would not show any message being exchanged here at all.
| | +--FIN (no close_notify)-->+----RST---->| |
Of course, this is based on you RECOMMENDING that no close_notify be sent. That's not something I would endorse.
The right way to deal with this in HTTP/1.1 is to use chunked encoding or Content-Length properly. If the connection ends before the request is terminated, then it's an abortive close.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many implementation will treat this as a normal stream termination.
Do you mean implementations of TLS, or of "connect-tcp"? I assume that the major implementations of TLS correctly surface an error when close_notify is omitted at shutdown; otherwise some uses would be vulnerable to truncation attacks. Implementations of "connect-tcp" do not yet exist, and can be asked to conform. My main concern would be about TLS-terminating load balancers that always send close_notify.
I would not show any message being exchanged here at all.
I don't understand. When A times out the TCP connection, what should it do with the HTTP connection?
Of course, this is based on you RECOMMENDING that no close_notify be sent. That's not something I would endorse.
Is there a reason why this recommendation is dangerous or problematic?
The right way to deal with this in HTTP/1.1 is to use chunked encoding or Content-Length properly.
In HTTP/1.1, "connect-tcp" uses HTTP Upgrade, so Content-Length and chunked encoding are not available. Instead, it uses the Capsule Protocol (similar to chunked encoding).
If the connection ends before the request is terminated, then it's an abortive close.
"connect-tcp" employs a FINAL_DATA capsule to represent the TCP FIN (end of incoming data), without any reliance on the HTTP transport/stream. However, according to RFC 9293 that's not the same as "closed":
"a user who expects no data in return need only wait to hear the connection was CLOSED successfully to know that all their data was received at the destination TCP endpoint."
To get to the CLOSED state, you need a signal for "received ACK of FIN". In H2/H3, the draft uses graceful stream closure for that. In H1 over TLS, close_notify seems to be the equivalent signal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many implementation will treat this as a normal stream termination.
Do you mean implementations of TLS, or of "connect-tcp"? I assume that the major implementations of TLS correctly surface an error when close_notify is omitted at shutdown; otherwise some uses would be vulnerable to truncation attacks. Implementations of "connect-tcp" do not yet exist, and can be asked to conform. My main concern would be about TLS-terminating load balancers that always send close_notify.
[...]
I don't understand. WhenAtimes out the TCP connection, what should it do with the HTTP connection?
I was talking about HTTP generally.
In the timeout case, you don't need to mandate any particular behaviour. Because a timeout can be many things, including a clean termination (FINAL_DATA included) or not. You are attempting to proscribe actions that don't need that.
Of course, this is based on you RECOMMENDING that no close_notify be sent. That's not something I would endorse.
Is there a reason why this recommendation is dangerous or problematic?
Because that's not how HTTP has worked so far and you are attempting to levy implementation requirements where they are most likely unwelcome. It might be appealing to have all implementations do the same thing here, but our experience is that they don't and they probably won't change just because it would be nicer if they would.
(ignore the stuff about message termination; that's what I get for not pulling in enough context)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a timeout can be many things, including a clean termination
I'm referring to a TCP user timeout, which is an error in TCP terms. It cannot be a clean shutdown because the endpoint does not know if all data was successfully received in both directions.
My point in showing this example was partly to show that even with this level of detail, some information is still lost: all errors are collapsed into RST.
that's not how HTTP has worked so far and you are attempting to levy implementation requirements where they are most likely unwelcome
- It's not really "HTTP". This is Upgrade, so the HTTP session is over, and the connection is now owned by a new protocol named "connect-tcp".
- It's a recommendation, not a requirement. If you ignore it, everything will mostly be fine, but debugging failures will be a bit harder and you'll lose this signal in HTTP/3 (where abrupt closure is not as controversial) if you have version-translating intermediaries.
- In at least some implementations, it's extremely easy to implement. In my Go prototype it's one line:
tlsConn.NetConn().Close(). If a behavior is helpful but not crucial, sometimes easy to implement and sometimes not worth the bother, that seems like a good case for RECOMMENDED. (Lowering this to OPTIONAL in HTTP/1.1, while it's still RECOMMENDED in HTTP/2 and HTTP/3, seems like extra text and complexity with no discernible benefit.)
| see {{!RFC9000, Section 19.4}} and {{?RFC9114, Section 8.1}} | ||
| - HTTP/2: reset the stream with CONNECT_ERROR; | ||
| see {{!RFC9113}}, Sections 6.4 and 7 | ||
| - HTTP/1.1 over TLS: TCP shutdown without a TLS closure alert; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| - HTTP/1.1 over TLS: TCP shutdown without a TLS closure alert; | |
| - HTTP/1.1 over TLS: TCP shutdown, with or without a TLS closure alert; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closure with a close_notify after receiving a FINAL_DATA capsule (representing FIN) would indicate graceful close. The purpose of this line is to provide a signal of ungraceful close (i.e. RST or timeout) that can be used after receiving the FIN (but before the connection is closed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted above, either works if you don't have FINAL_DATA, so why attempt to fix implementations.
draft-ietf-httpbis-connect-tcp.md
Outdated
| * The abrupt closure signals ({{closing-connections}}) are more likely to be missing or corrupted: | ||
| - Some implementations may be unable to omit the TLS closure alert or send a TCP RST, as recommended. | ||
| - Faulty implementations may fail to send a TLS closure alert during graceful shutdown. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to be consistent with what you are recommending above.
So rather than this, note that premature message termination might be hard to distinguish from successfully terminated messages, especially if there is no Content-Length or the terminal chunked encoding piece.
Including advice that you can't rely on the presence or absence of a TLS closure alert for clean message termination is useful. Then say that the best way to avoid that is to use Content-Length or chunked encoding. As long as TLS is involved and you have proof positive that the message is complete, then the nature of the abortive close is far less important.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that premature message termination might be hard to distinguish from successfully terminated messages
How is this different from the current text: "The abrupt closure signals ... are more likely to be missing or corrupted"?
As long as TLS is involved and you have proof positive that the message is complete, then the nature of the abortive close is far less important.
I agree. The draft notes this distinction by making the former mandatory, and the latter recommended.
Since the POSIX TCP APIs don't even really expose the CLOSING vs. CLOSED distinction, and applications should never rely on it, I'm not too concerned if it often gets lost in HTTP/1.1. However, I do think it is worth defining the distinction here to support debugging, allow propagation of this signal across HTTP version translating intermediaries, minimize the impact of transparent proxies, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The apparent inconsistency was
unable to omit the TLS closure alert or send a TCP RST, as recommended
with
- HTTP/1.1 over TLS: TCP shutdown without a TLS closure alert;
It wasn't clear to me that the "as recommended" applied to both the closure alert and TCP RST, due to English not being clear about whether such clauses distribute.
Ignore the other piece.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I've changed the text to
- The graceful and abrupt closure signals ({{closing-connections}}) are more likely to be missing or corrupted:
- Some implementations may be unable to emit the recommended abrupt closure signals, due to limitations in their TCP and TLS subsystems.
- Faulty implementations may fail to send a TLS closure alert during graceful shutdown, or fail to report an error when the expected closure alert is not received. These misbehaviors are not compliant with {{RFC8446}}, but they are common nonetheless among HTTP/1.1 implementations today.
|
As discussed at IETF 124, I would like to see if we can close out this discussion. Please review the latest version of this PR and leave a comment so we can see if there is a rough consensus. It seem like the main active topic is how to signal failure after half-close (i.e. "RST after FIN") in HTTP/1.1 over TLS. In my view, the current text does a good job of (1) defining a clear path for those who care about this case, (2) minimizing loss of functionality when it's not implemented as recommended, and (3) providing a clear rationale to avoid HTTP/1.1 entirely. |
The proposed changes look sane to me as they adapt the requirements to what is technically feasible.
We face similar difficulties in haproxy a few years ago during a rework of our error notifications. A failure after half-close is in fact a failure to send data in the other direction. At the implementation level, it will be reported to the send() call only. If we have a way to detect a send() failure (native or over TLS) then the half-close status will not be an issue since it won't affect the send path. The thing that you cannot easily detect is an error on the wire after the last successful send(), which happens on buffered data that's sent later. There are some OS-specific ways to query the state of pending output data on a socket but that's not pollable anyway. So in practice you have to assume that after the last successful send() and shutdown() on a half-closed socket, the data are on their own, and good luck to them. |
The current recommendation, to send a TLS Error Alert, is very difficult to implement. The new, relaxed recommendation allows a broader range of implementations, is much easier to implement, and is equally effective and secure.