-
Notifications
You must be signed in to change notification settings - Fork 2.1k
AccessRequest: Stop using Expires as MaxDuration #59708
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -490,7 +490,7 @@ func ApplyAccessReview(req types.AccessRequest, rev types.AccessReview, author U | |||||||||||||||||||||||||
| req.SetReviews(append(req.GetReviews(), rev)) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if rev.AssumeStartTime != nil { | ||||||||||||||||||||||||||
| if err := types.ValidateAssumeStartTime(*rev.AssumeStartTime, req.GetAccessExpiry(), req.GetCreationTime()); err != nil { | ||||||||||||||||||||||||||
| if err := types.ValidateAssumeStartTime(*rev.AssumeStartTime, req.GetMaxDuration(), req.GetCreationTime()); err != nil { | ||||||||||||||||||||||||||
| return trace.Wrap(err) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| req.SetAssumeStartTime(*rev.AssumeStartTime) | ||||||||||||||||||||||||||
|
|
@@ -514,7 +514,7 @@ func ApplyAccessReview(req types.AccessRequest, rev types.AccessReview, author U | |||||||||||||||||||||||||
| req.SetPromotedAccessListName(rev.GetAccessListName()) | ||||||||||||||||||||||||||
| req.SetPromotedAccessListTitle(rev.GetAccessListTitle()) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| req.SetExpiry(req.GetAccessExpiry()) | ||||||||||||||||||||||||||
| req.SetExpiry(req.GetMaxDuration()) | ||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
@@ -1364,15 +1364,9 @@ func (m *RequestValidator) validate(ctx context.Context, req types.AccessRequest | |||||||||||||||||||||||||
| maxAccessDuration = sessionTTL | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // This is the final adjusted access expiry where both max duration | ||||||||||||||||||||||||||
| // and session TTL were taken into consideration. | ||||||||||||||||||||||||||
| accessExpiry := now.Add(maxAccessDuration) | ||||||||||||||||||||||||||
| // Adjusted max access duration is equal to the access expiry time. | ||||||||||||||||||||||||||
| req.SetMaxDuration(accessExpiry) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Setting access expiry before calling `calculatePendingRequestTTL` | ||||||||||||||||||||||||||
| // matters since the func relies on this adjusted expiry. | ||||||||||||||||||||||||||
| req.SetAccessExpiry(accessExpiry) | ||||||||||||||||||||||||||
| // This is the final adjusted max duration where both max duration and session TTL | ||||||||||||||||||||||||||
| // were taken into consideration. | ||||||||||||||||||||||||||
| req.SetMaxDuration(now.Add(maxAccessDuration)) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Calculate the expiration time of the Access Request (how long it | ||||||||||||||||||||||||||
| // will await approval). | ||||||||||||||||||||||||||
|
|
@@ -1384,7 +1378,7 @@ func (m *RequestValidator) validate(ctx context.Context, req types.AccessRequest | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if req.GetAssumeStartTime() != nil { | ||||||||||||||||||||||||||
| assumeStartTime := *req.GetAssumeStartTime() | ||||||||||||||||||||||||||
| if err := types.ValidateAssumeStartTime(assumeStartTime, accessExpiry, req.GetCreationTime()); err != nil { | ||||||||||||||||||||||||||
| if err := types.ValidateAssumeStartTime(assumeStartTime, req.GetMaxDuration(), req.GetCreationTime()); err != nil { | ||||||||||||||||||||||||||
| return trace.Wrap(err) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
@@ -1487,7 +1481,7 @@ func (m *RequestValidator) maxDurationForRole(roleName string) time.Duration { | |||||||||||||||||||||||||
| // approval). request TTL is capped to the smaller value between the const requestTTL and the | ||||||||||||||||||||||||||
| // access request access expiry. | ||||||||||||||||||||||||||
| func (m *RequestValidator) calculatePendingRequestTTL(r types.AccessRequest, now time.Time) (time.Duration, error) { | ||||||||||||||||||||||||||
| accessExpiryTTL := r.GetAccessExpiry().Sub(now) | ||||||||||||||||||||||||||
| accessExpiryTTL := r.GetMaxDuration().Sub(now) | ||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I fully understand the fix. E.g. why are we replacing access expiry with max duration everywhere? It seems you're trying to address the issue on validation side? Shouldn't we be setting correct expiration on the created access request instead? We should be really careful making changes here without understanding the full impact so we don't break existing access request guarantees/properties.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because access expiry is not max duration. I should replace this line with Access expiry is in fact the requested session TTL: teleport/lib/services/access_request.go Lines 1525 to 1526 in b27d44c
I wish we could get rid of it and use
This is because the validation is broken. To fix it on the client side I'd have to do code like: oldAccessExpiry := req.GetAccessExpiry()
req, err = clt.CreateAccessRequestV2(ctx, req)
if err == nil {
req.SetAccessExpiry(oldAccessExpiry)
}
return trace.Wrap(err)I can do that if the changes here are too scary, but TBH that basically looks like working around a bug.
I get that. The only bit that I don't fully get is how this teleport/lib/services/access_request.go Line 517 in b27d44c
stayed compatible after we started doing But having said this it should be compatible because teleport/lib/services/access_request.go Lines 1367 to 1375 in b27d44c
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // If no expiration provided, use default. | ||||||||||||||||||||||||||
| expiry := r.Expiry() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
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.
What is the effect of not setting access expiry here anymore?
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, but I don't have a simple answer. The very short answer is: the effect of not setting this is after running
CreateAccessRequestV2with request withDryRun: truethe returned request is valid.The long answer:
The issue is that executing
CreateAccessRequestV2withDryRun: truemay lead producing invalid request because we were usingAccessExpiry (i.e. spec.expires)for two purposes.So on the call to
CreateAccessRequestV2we validate the request. At this pointspec.expiresis being used as the requested session TTL. This is done by sessionTTL call which truncates the session TTL tor.GetAccessExpiry() // value of spec.expires.Then the same
sessionTTLfunc checks if r.GetAccessExpiry() // value of spec.expires is not bigger than the calculated session TTL. This is because requesting session TTL longer than the remaining time of the session of the current logged in identity is an error.Now we have
sessionTTLcalculated, and we havespec.expires <= sessionTTLvalidated.After that (we are still in validation) there is this call
req.SetAccessExpiry(accessExpiry)which basically setsspec.expiresto the same value asspec.max_duration(it's a line afterreq.SetMaxDuration(accessExpiry)call) and from now on, we start usingspec.expiresasspec.max_duration(situation this PR changes).If it's a dry-run request we receive back a request with
spec.expiresoverwritten to the same value asspec.max_duration. And obviously max duration can be longer than the session TTL. So if you try to callCreateAccessRequestV2with the request returned by thedry-runcall you're in trouble because sessionTTL fails, because at this time spec.expires // from r.GetAccessExpiry() is now longer than the session TTL, unlessmaxDuration == sessionTTL.BTW
maxDuration == sessionTTLis pretty common. This happens when no requested role hasmax_durationset.tsh sshauto request runsdry-runfirst and that causes the problem. Possibly it isn't necessary to run dry-run first there, but I think we shouldn't have fields which semantics change during the execution because it makes it very hard to reason about the system. And surely, request successfully returned by a dry-run should be valid.