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

Backport of VAULT-18307 Vault incorrectly requeues credentials when the rotation period is updated into release/1.15.x #23768

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions builtin/logical/aws/path_static_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,20 @@ func (b *backend) pathStaticRolesWrite(ctx context.Context, req *logical.Request
if err != nil {
return nil, fmt.Errorf("failed to add item into the rotation queue for role %q: %w", config.Name, err)
}
} else {
// creds already exist, so all we need to do is update the rotation
// what here stays the same and what changes? Can we change the name?
i, err := b.credRotationQueue.PopByKey(config.Name)
if err != nil {
return nil, fmt.Errorf("expected an item with name %q, but got an error: %w", config.Name, err)
}
i.Value = config
// update the next rotation to occur at now + the new rotation period
i.Priority = time.Now().Add(config.RotationPeriod).Unix()
err = b.credRotationQueue.Push(i)
if err != nil {
return nil, fmt.Errorf("failed to add updated item into the rotation queue for role %q: %w", config.Name, err)
}
}

return &logical.Response{
Expand Down
65 changes: 59 additions & 6 deletions builtin/logical/aws/path_static_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,21 @@ func TestStaticRolesWrite(t *testing.T) {
bgCTX := context.Background()

cases := []struct {
name string
opts []awsutil.MockIAMOption
name string
// objects to return from mock IAM.
// You'll need a GetUserOutput (to validate the existence of the user being written,
// the keys the user has already been assigned,
// and the new key vault requests.
opts []awsutil.MockIAMOption // objects to return from the mock IAM
// the name, username if updating, and rotation_period of the user. This is the inbound request the cod would get.
data map[string]interface{}
expectedError bool
findUser bool
isUpdate bool
// if data is sent the name "johnny", then we'll match an existing user with rotation period 24 hours.
isUpdate bool
newPriority int64 // update time of new item in queue, skip if isUpdate false. There is a wiggle room of 5 seconds
// so the deltas between the old and the new update time should be larger than that to ensure the difference
// can be detected.
}{
{
name: "happy path",
Expand Down Expand Up @@ -168,7 +177,7 @@ func TestStaticRolesWrite(t *testing.T) {
expectedError: true,
},
{
name: "update existing user",
name: "update existing user, decreased rotation duration",
opts: []awsutil.MockIAMOption{
awsutil.WithGetUserOutput(&iam.GetUserOutput{User: &iam.User{UserName: aws.String("john-doe"), UserId: aws.String("unique-id")}}),
awsutil.WithListAccessKeysOutput(&iam.ListAccessKeysOutput{
Expand All @@ -187,8 +196,33 @@ func TestStaticRolesWrite(t *testing.T) {
"name": "johnny",
"rotation_period": "19m",
},
findUser: true,
isUpdate: true,
findUser: true,
isUpdate: true,
newPriority: time.Now().Add(19 * time.Minute).Unix(),
},
{
name: "update existing user, increased rotation duration",
opts: []awsutil.MockIAMOption{
awsutil.WithGetUserOutput(&iam.GetUserOutput{User: &iam.User{UserName: aws.String("john-doe"), UserId: aws.String("unique-id")}}),
awsutil.WithListAccessKeysOutput(&iam.ListAccessKeysOutput{
AccessKeyMetadata: []*iam.AccessKeyMetadata{},
IsTruncated: aws.Bool(false),
}),
awsutil.WithCreateAccessKeyOutput(&iam.CreateAccessKeyOutput{
AccessKey: &iam.AccessKey{
AccessKeyId: aws.String("abcdefghijklmnopqrstuvwxyz"),
SecretAccessKey: aws.String("zyxwvutsrqponmlkjihgfedcba"),
UserName: aws.String("john-doe"),
},
}),
},
data: map[string]interface{}{
"name": "johnny",
"rotation_period": "40h",
},
findUser: true,
isUpdate: true,
newPriority: time.Now().Add(40 * time.Hour).Unix(),
},
}

Expand Down Expand Up @@ -269,6 +303,11 @@ func TestStaticRolesWrite(t *testing.T) {
expectedData = staticRole
}

var actualItem *queue.Item
if c.isUpdate {
actualItem, _ = b.credRotationQueue.PopByKey(expectedData.Name)
}

if u, ok := fieldData.GetOk("username"); ok {
expectedData.Username = u.(string)
}
Expand All @@ -289,6 +328,20 @@ func TestStaticRolesWrite(t *testing.T) {
if en, an := expectedData.Name, actualData.Name; en != an {
t.Fatalf("mismatched role name, expected %q, but got %q", en, an)
}

// one-off to avoid importing/casting
abs := func(x int64) int64 {
if x < 0 {
return -x
}
return x
}

if c.isUpdate {
if ep, ap := c.newPriority, actualItem.Priority; abs(ep-ap) > 5 { // 5 second wiggle room for how long the test takes
t.Fatalf("mismatched updated priority, expected %d but got %d", ep, ap)
}
}
})
}
}
Expand Down
3 changes: 3 additions & 0 deletions changelog/23528.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
secrets/aws: update credential rotation deadline when static role rotation period is updated
```
3 changes: 2 additions & 1 deletion website/content/api-docs/secret/aws.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ $ curl \
}
```

## Create static role
## Create/Update static role
This endpoint creates or updates static role definitions. A static role is a 1-to-1 mapping
with an AWS IAM User, which will be adopted and managed by Vault, including rotating it according
to the configured `rotation_period`.
Expand All @@ -613,6 +613,7 @@ is specified as part of the URL.
- `rotation_period` `(string/int: <required>)` – Specifies the amount of time
Vault should wait before rotating the password. The minimum is 1 minute. Can be
specified in either `24h` or `86400` format (see [duration format strings](/vault/docs/concepts/duration-format)).
Updating the rotation period will 'reset' the next rotation to occur at `now` + `rotation_period`.

### Sample payload

Expand Down