Skip to content

Commit

Permalink
backport of commit 93f5777
Browse files Browse the repository at this point in the history
  • Loading branch information
vinay-gopalan authored Dec 3, 2024
1 parent 28debb6 commit 9e87a1f
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 0 deletions.
70 changes: 70 additions & 0 deletions builtin/logical/database/path_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,76 @@ func TestBackend_StaticRole_Role_name_check(t *testing.T) {
}
}

// TestStaticRole_NewCredentialGeneration verifies that new
// credentials are generated if a retried credential continues
// to fail
func TestStaticRole_NewCredentialGeneration(t *testing.T) {
ctx := context.Background()
b, storage, mockDB := getBackend(t)
defer b.Cleanup(ctx)
configureDBMount(t, storage)

roleName := "hashicorp"
createRole(t, b, storage, mockDB, "hashicorp")

t.Run("rotation failures should generate new password on retry", func(t *testing.T) {
// Fail to rotate the role
generateWALFromFailedRotation(t, b, storage, mockDB, roleName)

// Get WAL
walIDs := requireWALs(t, storage, 1)
wal, err := b.findStaticWAL(ctx, storage, walIDs[0])
if err != nil || wal == nil {
t.Fatal(err)
}

// Store password
initialPassword := wal.NewPassword

// Rotate role manually and fail again #1 with same password
generateWALFromFailedRotation(t, b, storage, mockDB, roleName)

// Ensure WAL is deleted since retrying password failed
requireWALs(t, storage, 0)

// Successfully rotate the role
mockDB.On("UpdateUser", mock.Anything, mock.Anything).
Return(v5.UpdateUserResponse{}, nil).
Once()
_, err = b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.UpdateOperation,
Path: "rotate-role/" + roleName,
Storage: storage,
})
if err != nil {
t.Fatal(err)
}

// Ensure WAL is flushed since request was successful
requireWALs(t, storage, 0)

// Read the credential
data := map[string]interface{}{}
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "static-creds/" + roleName,
Storage: storage,
Data: data,
}

resp, err := b.HandleRequest(namespace.RootContext(nil), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%s resp:%#v\n", err, resp)
}

// Confirm successful rotation used new credential
// Assert previous failing credential is not being used
if resp.Data["password"] == initialPassword {
t.Fatalf("expected password to be different after second retry")
}
})
}

func TestWALsStillTrackedAfterUpdate(t *testing.T) {
ctx := context.Background()
b, storage, mockDB := getBackend(t)
Expand Down
12 changes: 12 additions & 0 deletions builtin/logical/database/rotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag
// Use credential from input if available. This happens if we're restoring from
// a WAL item or processing the rotation queue with an item that has a WAL
// associated with it
var usedCredentialFromPreviousRotation bool
if output.WALID != "" {
wal, err := b.findStaticWAL(ctx, s, output.WALID)
if err != nil {
Expand Down Expand Up @@ -448,6 +449,7 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag
Statements: statements,
}
input.Role.StaticAccount.Password = wal.NewPassword
usedCredentialFromPreviousRotation = true
case wal.CredentialType == v5.CredentialTypeRSAPrivateKey:
// Roll forward by using the credential in the existing WAL entry
updateReq.CredentialType = v5.CredentialTypeRSAPrivateKey
Expand All @@ -456,6 +458,7 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag
Statements: statements,
}
input.Role.StaticAccount.PrivateKey = wal.NewPrivateKey
usedCredentialFromPreviousRotation = true
}
}

Expand Down Expand Up @@ -530,6 +533,15 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag
_, err = dbi.database.UpdateUser(ctx, updateReq, false)
if err != nil {
b.CloseIfShutdown(dbi, err)
if usedCredentialFromPreviousRotation {
b.Logger().Debug("credential stored in WAL failed, deleting WAL", "role", input.RoleName, "WAL ID", output.WALID)
if err := framework.DeleteWAL(ctx, s, output.WALID); err != nil {
b.Logger().Warn("failed to delete WAL", "error", err, "WAL ID", output.WALID)
}

// Generate a new WAL entry and credential for next attempt
output.WALID = ""
}
return output, fmt.Errorf("error setting credentials: %w", err)
}
modified = true
Expand Down
3 changes: 3 additions & 0 deletions changelog/28989.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
secret/db: Update static role rotation to generate a new password after 2 failed attempts.
```

0 comments on commit 9e87a1f

Please sign in to comment.