Syncronize token expiration behaviour between session key storage and SDK #1229

Closed
opened 2024-07-04 15:21:33 +00:00 by alexvanin · 0 comments

Expected Behavior

Session token considered expired at the same epoch on client and server side.

SDK declares that token is declined, when current epoch is strictly greater than token expiration epoch.

// Client side using SDK
var tok session.Object
tok.SetExp(10)
tok.ExpiredAt(10) // returns false, token is valid at epoch 10

Current Behavior

Session key storage uses greater or equal comparison to check expiration.

if pToken.ExpiredAt() <= s.networkState.CurrentEpoch() {
	return nil, new(apistatus.SessionTokenExpired)
}

Possible Solution

Use the same comparison in Node and SDK.

Steps to Reproduce (for bugs)

  1. Run FrostFS environment and find out current epoch, e.g. 99
  2. Create session token with expiration epoch 100 and save it in file
	resp, err := cli.SessionCreate(ctx, PrmSessionCreate{
		Expiration: 100,
		Key:        &k.PrivateKey,
	})
	require.NoError(t, err)

	var (
		pubk frostfsecdsa.PublicKey
		id   uuid.UUID
	)
	require.NoError(t, id.UnmarshalBinary(resp.id))
	require.NoError(t, pubk.Decode(resp.sessionKey))

	var s session.Object
	s.SetAuthKey(&pubk)
	s.SetExp(100)
	s.SetID(id)
	s.ForVerb(session.VerbObjectPut)
	s.BindContainer(cnrID)
	require.NoError(t, s.Sign(k.PrivateKey))

	data, err := s.MarshalJSON()
	require.NoError(t, err)
	require.NoError(t, os.WriteFile("./session", data, 0777))
  1. Tick new epoch 99 -> 100
  2. Read token and make sure it is considered valid by SDK
	data, err := os.ReadFile("./session")
	require.NoError(t, err)

	var s session.Object
	require.NoError(t, s.UnmarshalJSON(data))
	require.False(t, s.ExpiredAt(100)) // pass
  1. Try to upload new object. Error occurs because key storage removed expired token, even though it is not quite expired.
	var putHeader object.Object
	putHeader.SetContainerID(cnrID)
	putHeader.SetOwnerID(u)

	objectWriter, err := cli.ObjectPutInit(ctx, PrmObjectPutInit{
		Session: &s,
		Key:     &k.PrivateKey,
	})
	require.NoError(t, err)

	ok := objectWriter.WriteHeader(ctx, putHeader)
	require.True(t, ok)

	objectWriter.WritePayloadChunk(ctx, payload)
	r, err := objectWriter.Close(ctx)
	require.NoError(t, err) // status: code = 4096 message = session token not found

Context

SDK Pool component tries to update token with 1 epoch before expiration, but with this issue it tries to update at the exact expiration epoch, which produce some session token not found and session token is expired errors on client side.

Regression

Don't think so.

Your Environment

frostfs-node v0.38.6

## Expected Behavior Session token considered expired at the same epoch on client and server side. SDK declares that token is declined, when current epoch is strictly greater than token expiration epoch. ```go // Client side using SDK var tok session.Object tok.SetExp(10) tok.ExpiredAt(10) // returns false, token is valid at epoch 10 ``` ## Current Behavior Session key storage uses greater _or equal_ comparison to [check expiration](https://git.frostfs.info/TrueCloudLab/frostfs-node/src/commit/cfd5e3d40371cb3e26c0637e3773135d7bfd0058/pkg/services/object/util/key.go#L70). ```go if pToken.ExpiredAt() <= s.networkState.CurrentEpoch() { return nil, new(apistatus.SessionTokenExpired) } ``` ## Possible Solution Use the same comparison in Node and SDK. ## Steps to Reproduce (for bugs) 1. Run FrostFS environment and find out current epoch, e.g. 99 2. Create session token with expiration epoch 100 and save it in file ```go resp, err := cli.SessionCreate(ctx, PrmSessionCreate{ Expiration: 100, Key: &k.PrivateKey, }) require.NoError(t, err) var ( pubk frostfsecdsa.PublicKey id uuid.UUID ) require.NoError(t, id.UnmarshalBinary(resp.id)) require.NoError(t, pubk.Decode(resp.sessionKey)) var s session.Object s.SetAuthKey(&pubk) s.SetExp(100) s.SetID(id) s.ForVerb(session.VerbObjectPut) s.BindContainer(cnrID) require.NoError(t, s.Sign(k.PrivateKey)) data, err := s.MarshalJSON() require.NoError(t, err) require.NoError(t, os.WriteFile("./session", data, 0777)) ``` 3. Tick new epoch 99 -> 100 4. Read token and make sure it is considered valid by SDK ```go data, err := os.ReadFile("./session") require.NoError(t, err) var s session.Object require.NoError(t, s.UnmarshalJSON(data)) require.False(t, s.ExpiredAt(100)) // pass ``` 5. Try to upload new object. Error occurs because key storage removed expired token, even though it is not quite expired. ```go var putHeader object.Object putHeader.SetContainerID(cnrID) putHeader.SetOwnerID(u) objectWriter, err := cli.ObjectPutInit(ctx, PrmObjectPutInit{ Session: &s, Key: &k.PrivateKey, }) require.NoError(t, err) ok := objectWriter.WriteHeader(ctx, putHeader) require.True(t, ok) objectWriter.WritePayloadChunk(ctx, payload) r, err := objectWriter.Close(ctx) require.NoError(t, err) // status: code = 4096 message = session token not found ``` ## Context SDK Pool component tries to update token with 1 epoch before expiration, but with this issue it tries to update at the exact expiration epoch, which produce some `session token not found` and `session token is expired` errors on client side. ## Regression Don't think so. ## Your Environment frostfs-node v0.38.6
alexvanin added the
bug
triage
labels 2024-07-04 15:21:33 +00:00
aarifullin was assigned by fyrchik 2024-07-05 06:31:46 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: TrueCloudLab/frostfs-node#1229
There is no content yet.