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

Add clock skew correction on the client #2668

Merged
merged 9 commits into from
Jun 7, 2024
Merged

Add clock skew correction on the client #2668

merged 9 commits into from
Jun 7, 2024

Conversation

Madrigal
Copy link
Contributor

@Madrigal Madrigal commented Jun 4, 2024

Fixes #2321

This change adjusts signing request to account for the difference in time between client and server (aka clock skew). This PR makes values that we know are caused by clock skew retryable by default, and adds logic to classify probable clock skew errors as retryable.

This value is set on the client so subsequent requests can read the adjusted time.

For changes to files under the /codegen/aws-models folder, and manual edits to autogenerated code (e.g. /service/s3/api.go) please create an Issue instead of a PR for those type of changes.

Verified by

  1. Making a call to S3 with my local date set 1 hour in the past without these changes. Ensured the request failed and wasn't retried
  2. Made a call with an S3 client with my local date set 1 hour in the past. Ensure the call fails and a retry is successful
  3. Made a new call with the same client. Ensure it succeeds without having to retry

If the PR addresses an existing bug or feature, please reference it here.
#2321

To help speed up the process and reduce the time to merge please ensure that Allow edits by maintainers is checked before submitting your PR. This will allow the project maintainers to make minor adjustments or improvements to the submitted PR, allow us to reduce the roundtrip time for merging your request.

Madrigal added 2 commits June 4, 2024 20:42
Fixes #2321

This change adjusts signing request to account for the difference in time between client and server Caka clock skew). This PR makes values that we know are caused by clock skew retryable by default, and adds logic to classify probable clock skew errors as retryable.

This value is set on the client so subsequent requests can read the adjusted time.
@Madrigal Madrigal force-pushed the feature-clock-skew branch from 5b26861 to 4e03615 Compare June 5, 2024 11:50
type clockSkew struct{}

// SetAttemptSkewContext sets the clock skew value on the context
func SetAttemptSkewContext(ctx context.Context, v time.Duration) context.Context {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should do this in internal/context so it can't be picked up on by callers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to a new internalmiddleware package


// AddTimeOffsetDeserializeMiddleware sets the clock skew on the client if it's present on the context
// at the end of the request
type AddTimeOffsetDeserializeMiddleware struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

You can actually have one structure implement both phases FYI.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice, moved this to be just a single structure

return err
}
skew := awsmiddle.GetAttemptSkewContext(ctx)
if !(skew > 4*time.Minute) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, skew <= 4*time.Minute?

Copy link
Contributor

Choose a reason for hiding this comment

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

also probably worth consting

Copy link
Contributor Author

Choose a reason for hiding this comment

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

consted

// (difference between server time and client time).
// This is returned when there's certain confidence that adjusting the client time
// could allow a retry to succeed
type ProbClockSkewError struct{ Err error }
Copy link
Contributor

Choose a reason for hiding this comment

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

Definitely should be unexported, there's no reason to do otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed

}

private void generateClockSkewInsertMiddleware(GoWriter writer) {
Symbol stackSymbol = SymbolUtils.createPointableSymbolBuilder("Stack", SmithyGoDependency.SMITHY_MIDDLEWARE)
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI we are trying to eradicate calls to these garbage createXBuilder things with extreme prejudice:

SmithyGoDependency.SMITHY_MIDDLEWARE.struct("Stack")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed

@@ -3,7 +3,7 @@ package smithy
import (
"context"
"fmt"

"github.com/aws/aws-sdk-go-v2/aws/middleware"
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you running gofmt? It should be putting that blank line here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. Discussed offline and this separation is actually done via goimports. Will send a separate PR to add it to our formatter target

) {
v := GetAttemptSkewContext(ctx)
if v != 0 {
m.Offset.Store(v.Nanoseconds())
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: should we be storing skew every time, or just if not previously set?

Definitely something we can change later but don't remember if we discussed it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed this offline. We could only store this once when we see it, on the assumption that you only need to collect skew once instead of on every call. This could free some of the calls to lock that atomic is likely doing behind the scenes.

However, the downside is that if we ever get a bad Date back from the server, it can end up poisoning the time for all subsequent requests.

Decided it's probably safer to update it every time

@@ -187,3 +191,239 @@ func TestAttemptClockSkewHandler(t *testing.T) {
})
}
}

type HTTPClient interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, idiomatically all of these test definitions are always unexported.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed

var possibleSkewCodes = map[string]struct{}{
"InvalidSignatureException": {},
"SignatureDoesNotMatch": {},
"AuthFailure": {},
Copy link
Contributor

Choose a reason for hiding this comment

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

Are RequestTimeTooSkewed, RequestInTheFuture and RequestExpired (added below) omitted here intentionally?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, they were added to the list of definitely retry always. Moved to a separate list as discussed on comment below

@@ -51,8 +51,11 @@ var DefaultRetryableHTTPStatusCodes = map[int]struct{}{
// DefaultRetryableErrorCodes provides the set of API error codes that should
// be retried.
var DefaultRetryableErrorCodes = map[string]struct{}{
"RequestExpired": {},
Copy link
Contributor

Choose a reason for hiding this comment

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

Thinking we should probably just use these to do the probSkew wrapping instead of adding them to the default policy.

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 see your point. Was debating between keeping all skew stuff together. Moved them and expanded the probSkew logic to include this

// AddTimeOffsetBuildMiddleware sets a value representing clock skew on the request context.
// This can be read by other operations (such as signing) to correct the date value they send
// on the request
type AddTimeOffsetBuildMiddleware struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

We almost certainly want this under the blanket internal/ as well. In general, we just don't export things unless we know we absolutely need to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to internal

if !errors.As(err, &v) {
return err
}
_, ok := possibleSkewCodes[v.ErrorCode()]
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, can shorten

if _, ok := possibleSkewCodes[v.ErrorCode()]; !ok {
  return err
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shortened

ClientMemberResolver resolver = ClientMemberResolver.builder()
.resolver(TIME_OFFSET_RESOLVER)
.build();
MiddlewareRegistrar initializeMiddleware = MiddlewareRegistrar.builder()
Copy link
Contributor

@lucix-aws lucix-aws Jun 5, 2024

Choose a reason for hiding this comment

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

We tend to also static decl these but this 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.

OK, moved all definitions to be static

@Madrigal Madrigal marked this pull request as ready for review June 6, 2024 20:36
@Madrigal Madrigal requested a review from a team as a code owner June 6, 2024 20:36
@Madrigal Madrigal merged commit 4892b05 into main Jun 7, 2024
12 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Detect clock skew
2 participants