mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
db: honor static role TTL across restarts when skip import rotation i… (#29537)
* db: honor static role TTL across restarts when skip import rotation is enabled * changelog
This commit is contained in:
committed by
GitHub
parent
49ecdad1ad
commit
8d0443fd48
@@ -258,7 +258,9 @@ func (b *databaseBackend) pathStaticCredsRead() framework.OperationFunc {
|
|||||||
respData := map[string]interface{}{
|
respData := map[string]interface{}{
|
||||||
"username": role.StaticAccount.Username,
|
"username": role.StaticAccount.Username,
|
||||||
"ttl": role.StaticAccount.CredentialTTL().Seconds(),
|
"ttl": role.StaticAccount.CredentialTTL().Seconds(),
|
||||||
"last_vault_rotation": role.StaticAccount.LastVaultRotation,
|
}
|
||||||
|
if !role.StaticAccount.LastVaultRotation.IsZero() {
|
||||||
|
respData["last_vault_rotation"] = role.StaticAccount.LastVaultRotation
|
||||||
}
|
}
|
||||||
|
|
||||||
if role.StaticAccount.UsesRotationPeriod() {
|
if role.StaticAccount.UsesRotationPeriod() {
|
||||||
|
|||||||
@@ -713,14 +713,22 @@ func (b *databaseBackend) pathStaticRoleCreateUpdate(ctx context.Context, req *l
|
|||||||
switch req.Operation {
|
switch req.Operation {
|
||||||
case logical.CreateOperation:
|
case logical.CreateOperation:
|
||||||
if role.SkipImportRotation {
|
if role.SkipImportRotation {
|
||||||
b.Logger().Trace("skipping static role import rotation", "role", name)
|
b.Logger().Debug("skipping static role import rotation", "role", name)
|
||||||
// synthetically set lastVaultRotation to now, so that it gets
|
|
||||||
// queued correctly
|
// Synthetically set lastVaultRotation to now, so that it gets
|
||||||
lastVaultRotation = time.Now()
|
// queued correctly.
|
||||||
// we intentionally do not set role.StaticAccount.LastVaultRotation
|
// NOTE: We intentionally do not set role.StaticAccount.LastVaultRotation
|
||||||
// because the zero value indicates Vault has not rotated the
|
// because the zero value indicates Vault has not rotated the
|
||||||
// password yet
|
// password yet
|
||||||
|
lastVaultRotation = time.Now()
|
||||||
|
|
||||||
|
// NextVaultRotation allows calculating the TTL on GET /static-creds
|
||||||
|
// requests and to calculate the queue priority in populateQueue()
|
||||||
|
// across restarts. We can't rely on LastVaultRotation in these
|
||||||
|
// cases bacause, when import rotation is skipped, LastVaultRotation
|
||||||
|
// is set to a zero value in storage.
|
||||||
role.StaticAccount.SetNextVaultRotation(lastVaultRotation)
|
role.StaticAccount.SetNextVaultRotation(lastVaultRotation)
|
||||||
|
|
||||||
// we were told to not rotate, just add the entry
|
// we were told to not rotate, just add the entry
|
||||||
err := b.StoreStaticRole(ctx, req.Storage, role)
|
err := b.StoreStaticRole(ctx, req.Storage, role)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -936,7 +944,7 @@ type staticAccount struct {
|
|||||||
// querying for the next schedule expiry since the last known vault rotation.
|
// querying for the next schedule expiry since the last known vault rotation.
|
||||||
func (s *staticAccount) NextRotationTime() time.Time {
|
func (s *staticAccount) NextRotationTime() time.Time {
|
||||||
if s.UsesRotationPeriod() {
|
if s.UsesRotationPeriod() {
|
||||||
return s.LastVaultRotation.Add(s.RotationPeriod)
|
return s.NextVaultRotation
|
||||||
}
|
}
|
||||||
return s.Schedule.Next(time.Now())
|
return s.Schedule.Next(time.Now())
|
||||||
}
|
}
|
||||||
@@ -985,7 +993,8 @@ func (s *staticAccount) ShouldRotate(priority int64, t time.Time) bool {
|
|||||||
return priority <= t.Unix() && s.IsInsideRotationWindow(t)
|
return priority <= t.Unix() && s.IsInsideRotationWindow(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNextVaultRotation
|
// SetNextVaultRotation sets the next vault rotation to time t plus the role's
|
||||||
|
// rotation period or to the next schedule.
|
||||||
func (s *staticAccount) SetNextVaultRotation(t time.Time) {
|
func (s *staticAccount) SetNextVaultRotation(t time.Time) {
|
||||||
if s.UsesRotationPeriod() {
|
if s.UsesRotationPeriod() {
|
||||||
s.NextVaultRotation = t.Add(s.RotationPeriod)
|
s.NextVaultRotation = t.Add(s.RotationPeriod)
|
||||||
|
|||||||
@@ -1388,6 +1388,22 @@ func createRoleWithData(t *testing.T, b *databaseBackend, s logical.Storage, moc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readStaticCred(t *testing.T, b *databaseBackend, s logical.Storage, mockDB *mockNewDatabase, roleName string) *logical.Response {
|
||||||
|
t.Helper()
|
||||||
|
mockDB.On("UpdateUser", mock.Anything, mock.Anything).
|
||||||
|
Return(v5.UpdateUserResponse{}, nil).
|
||||||
|
Once()
|
||||||
|
resp, err := b.HandleRequest(context.Background(), &logical.Request{
|
||||||
|
Operation: logical.ReadOperation,
|
||||||
|
Path: "static-creds/" + roleName,
|
||||||
|
Storage: s,
|
||||||
|
})
|
||||||
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
t.Fatal(resp, err)
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
const testRoleStaticCreate = `
|
const testRoleStaticCreate = `
|
||||||
CREATE ROLE "{{name}}" WITH
|
CREATE ROLE "{{name}}" WITH
|
||||||
LOGIN
|
LOGIN
|
||||||
|
|||||||
@@ -1664,6 +1664,29 @@ func requireWALs(t *testing.T, storage logical.Storage, expectedCount int) []str
|
|||||||
return wals
|
return wals
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getBackendWithConfig returns an initialized test backend for the given
|
||||||
|
// BackendConfig
|
||||||
|
func getBackendInitQueue(t *testing.T, c *logical.BackendConfig, tickInterval string) (*databaseBackend, *logical.BackendConfig, *mockNewDatabase) {
|
||||||
|
t.Helper()
|
||||||
|
// make queue ticks more frequent for tests
|
||||||
|
c.Config[queueTickIntervalKey] = tickInterval
|
||||||
|
c.StorageView = &logical.InmemStorage{}
|
||||||
|
// Create and init the backend ourselves instead of using a Factory because
|
||||||
|
// the factory function kicks off threads that cause racy tests.
|
||||||
|
b := Backend(c)
|
||||||
|
ctx := context.Background()
|
||||||
|
if err := b.Setup(ctx, c); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
b.schedule = &TestSchedule{}
|
||||||
|
b.credRotationQueue = queue.New()
|
||||||
|
b.initQueue(ctx, c)
|
||||||
|
|
||||||
|
mockDB := setupMockDB(b)
|
||||||
|
|
||||||
|
return b, c, mockDB
|
||||||
|
}
|
||||||
|
|
||||||
func getBackend(t *testing.T) (*databaseBackend, logical.Storage, *mockNewDatabase) {
|
func getBackend(t *testing.T) (*databaseBackend, logical.Storage, *mockNewDatabase) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
config := logical.TestBackendConfig()
|
config := logical.TestBackendConfig()
|
||||||
@@ -1671,7 +1694,8 @@ func getBackend(t *testing.T) (*databaseBackend, logical.Storage, *mockNewDataba
|
|||||||
// Create and init the backend ourselves instead of using a Factory because
|
// Create and init the backend ourselves instead of using a Factory because
|
||||||
// the factory function kicks off threads that cause racy tests.
|
// the factory function kicks off threads that cause racy tests.
|
||||||
b := Backend(config)
|
b := Backend(config)
|
||||||
if err := b.Setup(context.Background(), config); err != nil {
|
ctx := context.Background()
|
||||||
|
if err := b.Setup(ctx, config); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
b.schedule = &TestSchedule{}
|
b.schedule = &TestSchedule{}
|
||||||
|
|||||||
3
changelog/29537.txt
Normal file
3
changelog/29537.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:bug
|
||||||
|
database: Fix a bug where static role passwords are erroneously rotated across backend restarts when using skip import rotation.
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user