Skip to content
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

Immediately close with INVALID_TOKEN #3107

Merged
merged 17 commits into from
Dec 3, 2019
Merged

Conversation

ianswett
Copy link
Contributor

@ianswett ianswett commented Oct 17, 2019

If the Retry token is known to be invalid by the server, then the server can close the connection with INVALID_TOKEN instead of waiting for a timeout.

Came out of discussion of Issue #3014
Fixes #3168

Related to #3127

If the Retry token is known to be invalid by the server, then the server can close the connection with INVALID_TOKEN instead of waiting for a timeout.
@nibanks
Copy link
Member

nibanks commented Oct 17, 2019

You need to update the "Transport Error Codes" section too.

@ianswett
Copy link
Contributor Author

Thanks @nibanks I did that in a subsequent update. Refresh and tell me if it looks correct.

@nibanks
Copy link
Member

nibanks commented Oct 17, 2019

@ianswett you're missing the section that gives some text to each error code #error-codes.

@@ -1641,6 +1641,14 @@ of connection establishment. By giving the client a different connection ID to
use, a server can cause the connection to be routed to a server instance with
more resources available for new connections.

If a server receives a client Initial with a unverifiable Retry token,
it knows the client will not accept another Retry token. It can either
proceed with the handshake without verifying the token or immediately close
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that we should allow the server to continue. Why not make the INVALID_TOKEN mandatory?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are to make this a mandatory requirement, under the assumption that the servers would act like that, should we also suggest that a server should issue tokens in a way that a Retry token and a NEW_TOKEN token are distinguishable from each other?

Unless we recommend servers building a token in that way, I do not think making this a MUST has benefits.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we assume that servers only require Retries when they’re under attack, they might not bother checking the validity of a token when they’re not under attack.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A token SHOULD be constructed in a way that allows the server to distinguish it from tokens that are sent in Retry packets as they are carried in the same field.

-- Section 8.1.2

We could strengthen that to MUST if you like. It probably should be anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server could decide it's not under heavy load and proceed with the handshake, even if the token doesn't constitute address validation. ie: The token could expire on a shorter timeline than the session ticket/etc, so 0-RTT could succeed, but address validation would fail.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm persuaded by Marten's comment here (which I missed at first, I think because we commented at almost the same time).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think the requirement to make them distinguishable should be a MUST. Otherwise, a change of token generation method could make an old NEW_TOKEN token appear to be an invalid Retry token.

draft-ietf-quic-transport.md Outdated Show resolved Hide resolved
Copy link
Contributor

@MikeBishop MikeBishop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending how the server distinguishes them, a change of method / confusion about what server you're sending it to could make you appear to be presenting a Retry token instead of a regular token. This error code enables the client to recover and retry quickly, but there's no language about what the client should do when it receives the code.

@@ -1641,6 +1641,14 @@ of connection establishment. By giving the client a different connection ID to
use, a server can cause the connection to be routed to a server instance with
more resources available for new connections.

If a server receives a client Initial with a unverifiable Retry token,
it knows the client will not accept another Retry token. It can either
proceed with the handshake without verifying the token or immediately close
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think the requirement to make them distinguishable should be a MUST. Otherwise, a change of token generation method could make an old NEW_TOKEN token appear to be an invalid Retry token.

@ianswett
Copy link
Contributor Author

ianswett commented Oct 24, 2019

@MikeBishop do you have a suggestion about what the client should do? Clearly it could start a new connection, but it would't want to do that an unlimited number of times, and it may depend upon whether a TCP fallback is available. Given that, it wasn't clear I should recommend something.

And I agree making them distinguishable should be a MUST, as stated in #3128

@martinthomson
Copy link
Member

I think that the best thing we can say is "the connection attempt failed". The first version of the PR strongly suggested that making another connection attempt was the right thing to do, but that gets into lots of difficult questions about identifying why. Given that this is an error that only happens if a Retry is spoofed, then I think that all we can say is:

it's a trap

@martinthomson martinthomson added the design An issue that affects the design of the protocol; resolution requires consensus. label Oct 29, 2019
@@ -1641,6 +1641,13 @@ of connection establishment. By giving the client a different connection ID to
use, a server can cause the connection to be routed to a server instance with
more resources available for new connections.

If a server receives a client Initial with an invalid Retry token,
it knows the client will not accept another Retry token. It can either
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this sentence. How would the server know this for sure?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a keen observation (I think).

There is a chance where a Retry token is considered invalid due to packet becoming corrupt while being transmitted. A server can only say for sure that "the client will not accept another Retry token" unless either of the following conditions are met:

  • The token is accompanied by a checksum (or an integrity protection). The checksum is correct, but the contents of the Retry token is invalid.
  • The server successfully unprotects the Initial.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The client could also connect to an unexpected endpoint or the token could have expired.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me retract my comment above. We do not need to care about the packet becoming corrupt on the wire, because the prerequisite for responding with a CONNECTION_CLOSE is that the Initial that has been received can be unprotected.

To avoid confusion, maybe change "If a server receives a client Initial with an invalid Retry token" to "If a server receives a client Initial that can be unprotected but contains an invalid Retry token"?

The client could also connect to an unexpected endpoint or the token could have expired.

I'd say that that's a failure in the server deployment. We do not discuss how that should be handled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say that that's a failure in the server deployment. We do not discuss how that should be handled.

Since Retry is processed before handshake, why can't it just be the client sending to the wrong IP address?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3128 requires being able to distinguish between the two types of tokens. I updated the text with Kazuho's suggestion about being able to unprotect the Initial.

Copy link
Member

@kazuho kazuho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I have one editorial suggestion.

@@ -1641,6 +1641,13 @@ of connection establishment. By giving the client a different connection ID to
use, a server can cause the connection to be routed to a server instance with
more resources available for new connections.

If a server receives a client Initial with an invalid Retry token,
it knows the client will not accept another Retry token. It can either
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me retract my comment above. We do not need to care about the packet becoming corrupt on the wire, because the prerequisite for responding with a CONNECTION_CLOSE is that the Initial that has been received can be unprotected.

To avoid confusion, maybe change "If a server receives a client Initial with an invalid Retry token" to "If a server receives a client Initial that can be unprotected but contains an invalid Retry token"?

The client could also connect to an unexpected endpoint or the token could have expired.

I'd say that that's a failure in the server deployment. We do not discuss how that should be handled.

About being able to unprotect the Initial packet
Copy link
Contributor

@janaiyengar janaiyengar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes, @kazuho's text suggestion answers my question. I have a couple more comments.

draft-ietf-quic-transport.md Outdated Show resolved Hide resolved
draft-ietf-quic-transport.md Outdated Show resolved Hide resolved
It can either proceed with the handshake without verifying the token or
immediately close ({{immediate-close}}) the connection with a connection
error of INVALID_TOKEN to cause the handshake to fail quickly instead of
waiting for the client to timeout.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suggests 2 options at the server, when there are 3: it can simply drop the packet and let the client time out. You may want to call out all three options.

Suggestion: "If a server receives a client Initial that can be unprotected but contains an invalid Retry token, it knows the client will not accept another Retry token. The server can drop such a packet and allow the client to time out to detect loss of this connection, but that is a significant latency penalty to the client. If possible, the server SHOULD either immediately close the connection with an INVALID_TOKEN error or proceed with the handshake without verifying the token."

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that listing all the three options would be an improvement.

At the same time, I have a concern regarding the suggestion of doing either an immediate close or proceeding the handshake. Those two options both require the server to retain state that cannot be delayed by providing a Retry to token to the client.

I think it we might want to downgrade the SHOULD to MAY, or, suggest that a server can send a CONNECTION_CLOSE frame (with INVALID_TOKEN) without retaining state.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to detect an invalid token without per-connection state, right?

That said, I realized that I don't think we should recommend that a server that might be getting DoSsed SHOULD send out another packet in response. How about "A server that is able to maintain enough state MAY either ..."

Copy link
Member

@kazuho kazuho Nov 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By saying state, I mean the closing and the draining state that gets built and retained once you invoke {immediate-close}. We should not recommend such behavior as it would create a DoS vector. But if it is not going to be a SHOULD but instead “can” or MAY, I think we can be less precise and therefor the text we have now is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a sentence about not creating any state in this case. PTAL.

Copy link
Member

@martinthomson martinthomson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found some of the wording to be awkward. I'm not sure that my formulation is better, but you can integrate as you like.

draft-ietf-quic-transport.md Outdated Show resolved Hide resolved
draft-ietf-quic-transport.md Outdated Show resolved Hide resolved
ianswett and others added 2 commits November 11, 2019 21:43
Copy link
Member

@kazuho kazuho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rereading the changes, I have the following editorial suggestion.

server chooses not to proceed with the handshake, it SHOULD immediately close
({{immediate-close}}) the connection with an INVALID_TOKEN error. Note that a
server has not established any state for the connection at this point and so
does not enter the closing period.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to change the last two sentences to something like: If a server chooses not to proceed with the handshake, it SHOULD immediately send a CONNECTION_CLOSE frame with an INVALID_TOKEN error without creating any state. As the server does not establish any state for the connection, it does not enter the closing period ({{immediate-close}}).

Current text is confusing as the normative text suggests following the rule in {{immediate-close}}, then argues as if it is a fact that a server would not be retaining any state, even though what we want to do here is to recommend the server to send CONNECTION_CLOSE in a stateless manner.

@@ -1641,6 +1641,17 @@ of connection establishment. By giving the client a different connection ID to
use, a server can cause the connection to be routed to a server instance with
more resources available for new connections.

If a server receives a client Initial that can be unprotected but contains an
invalid Retry token, it knows the client will not accept another Retry token.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only applies if the token is a Retry token (and not a NEW_TOKEN token).
When the client uses a NEW_TOKEN token, it would exchange this token for the Retry token when it receives a Retry packet, wouldn't it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the text explicitly says "Retry token", so I don't see how this is a problem.

@martinthomson martinthomson merged commit 7d17a20 into master Dec 3, 2019
@martinthomson martinthomson deleted the ianswett-invalid-token branch December 3, 2019 03:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
-transport design An issue that affects the design of the protocol; resolution requires consensus.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow servers to close connections immediately when the token is corrupted
8 participants