mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
adv ttl mgmt: define schedule interface (#22590)
This commit is contained in:
committed by
GitHub
parent
e941d444a9
commit
aa05ba6105
@@ -15,6 +15,7 @@ import (
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/builtin/logical/database/schedule"
|
||||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
"github.com/hashicorp/vault/helper/syncmap"
|
||||
"github.com/hashicorp/vault/internalshared/configutil"
|
||||
@@ -25,7 +26,6 @@ import (
|
||||
"github.com/hashicorp/vault/sdk/helper/locksutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/queue"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,7 +34,6 @@ const (
|
||||
databaseRolePath = "role/"
|
||||
databaseStaticRolePath = "static-role/"
|
||||
minRootCredRollbackAge = 1 * time.Minute
|
||||
scheduleOptionsDefault = cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow
|
||||
)
|
||||
|
||||
type dbPluginInstance struct {
|
||||
@@ -129,6 +128,7 @@ func Backend(conf *logical.BackendConfig) *databaseBackend {
|
||||
b.connections = syncmap.NewSyncMap[string, *dbPluginInstance]()
|
||||
b.queueCtx, b.cancelQueueCtx = context.WithCancel(context.Background())
|
||||
b.roleLocks = locksutil.CreateLocks()
|
||||
b.schedule = &schedule.DefaultSchedule{}
|
||||
|
||||
return &b
|
||||
}
|
||||
@@ -180,25 +180,7 @@ type databaseBackend struct {
|
||||
gaugeCollectionProcess *metricsutil.GaugeCollectionProcess
|
||||
gaugeCollectionProcessStop sync.Once
|
||||
|
||||
// scheduleOptionsOverride is used by tests to set a custom ParseOption with seconds enabled
|
||||
scheduleOptionsOverride cron.ParseOption
|
||||
}
|
||||
|
||||
func (b *databaseBackend) ParseSchedule(rotationSchedule string) (*cron.SpecSchedule, error) {
|
||||
scheduleOptions := scheduleOptionsDefault
|
||||
if b.scheduleOptionsOverride != 0 {
|
||||
scheduleOptions = b.scheduleOptionsOverride
|
||||
}
|
||||
parser := cron.NewParser(scheduleOptions)
|
||||
schedule, err := parser.Parse(rotationSchedule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sched, ok := schedule.(*cron.SpecSchedule)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid rotation schedule")
|
||||
}
|
||||
return sched, nil
|
||||
schedule schedule.Scheduler
|
||||
}
|
||||
|
||||
func (b *databaseBackend) DatabaseConfig(ctx context.Context, s logical.Storage, name string) (*DatabaseConfig, error) {
|
||||
|
||||
@@ -591,7 +591,7 @@ func (b *databaseBackend) pathStaticRoleCreateUpdate(ctx context.Context, req *l
|
||||
}
|
||||
|
||||
if rotationScheduleOk {
|
||||
parsedSchedule, err := b.ParseSchedule(rotationSchedule)
|
||||
parsedSchedule, err := b.schedule.Parse(rotationSchedule)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("could not parse rotation_schedule", "error", err), nil
|
||||
}
|
||||
@@ -600,8 +600,9 @@ func (b *databaseBackend) pathStaticRoleCreateUpdate(ctx context.Context, req *l
|
||||
|
||||
if rotationWindowOk {
|
||||
rotationWindowSeconds := rotationWindowSecondsRaw.(int)
|
||||
if rotationWindowSeconds < minRotationWindowSeconds {
|
||||
return logical.ErrorResponse(fmt.Sprintf("rotation_window must be %d seconds or more", minRotationWindowSeconds)), nil
|
||||
err := b.schedule.ValidateRotationWindow(rotationWindowSeconds)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("rotation_window is invalid", "error", err), nil
|
||||
}
|
||||
role.StaticAccount.RotationWindow = time.Duration(rotationWindowSeconds) * time.Second
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -332,8 +331,8 @@ func TestBackend_StaticRole_Config(t *testing.T) {
|
||||
"rotation_schedule": "* * * * *",
|
||||
"rotation_window": "59s",
|
||||
},
|
||||
path: "plugin-role-test",
|
||||
err: errors.New(fmt.Sprintf("rotation_window must be %d seconds or more", minRotationWindowSeconds)),
|
||||
path: "plugin-role-test",
|
||||
errContains: "rotation_window is invalid",
|
||||
},
|
||||
"disallowed role config": {
|
||||
account: map[string]interface{}{
|
||||
@@ -1152,7 +1151,7 @@ func TestIsInsideRotationWindow(t *testing.T) {
|
||||
testTime := tc.now
|
||||
if tc.data["rotation_schedule"] != nil && tc.timeModifier != nil {
|
||||
rotationSchedule := tc.data["rotation_schedule"].(string)
|
||||
schedule, err := b.ParseSchedule(rotationSchedule)
|
||||
schedule, err := b.schedule.Parse(rotationSchedule)
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse rotation_schedule: %s", err)
|
||||
}
|
||||
|
||||
@@ -22,9 +22,6 @@ const (
|
||||
// Default interval to check the queue for items needing rotation
|
||||
defaultQueueTickSeconds = 5
|
||||
|
||||
// Minimum allowed value for rotation_window
|
||||
minRotationWindowSeconds = 3600
|
||||
|
||||
// Config key to set an alternate interval
|
||||
queueTickIntervalKey = "rotation_queue_tick_interval"
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Sectorbob/mlab-ns2/gae/ns/digest"
|
||||
"github.com/hashicorp/vault/builtin/logical/database/schedule"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/helper/testhelpers/mongodb"
|
||||
postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql"
|
||||
@@ -33,9 +34,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
dbUser = "vaultstatictest"
|
||||
dbUserDefaultPassword = "password"
|
||||
testScheduleOptionsSeconds = cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow
|
||||
dbUser = "vaultstatictest"
|
||||
dbUserDefaultPassword = "password"
|
||||
testMinRotationWindowSeconds = 10
|
||||
testScheduleParseOptions = cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow
|
||||
)
|
||||
|
||||
func TestBackend_StaticRole_Rotation_basic(t *testing.T) {
|
||||
@@ -56,7 +58,7 @@ func TestBackend_StaticRole_Rotation_basic(t *testing.T) {
|
||||
}
|
||||
defer b.Cleanup(context.Background())
|
||||
|
||||
b.scheduleOptionsOverride = testScheduleOptionsSeconds
|
||||
b.schedule = &TestSchedule{}
|
||||
|
||||
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||
defer cleanup()
|
||||
@@ -1297,7 +1299,7 @@ func TestStoredWALsCorrectlyProcessed(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b.credRotationQueue = queue.New()
|
||||
b.scheduleOptionsOverride = testScheduleOptionsSeconds
|
||||
b.schedule = &TestSchedule{}
|
||||
configureDBMount(t, config.StorageView)
|
||||
createRoleWithData(t, b, config.StorageView, mockDB, tc.wal.RoleName, tc.data)
|
||||
role, err := b.StaticRole(ctx, config.StorageView, "hashicorp")
|
||||
@@ -1458,7 +1460,7 @@ func getBackend(t *testing.T) (*databaseBackend, logical.Storage, *mockNewDataba
|
||||
if err := b.Setup(context.Background(), config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b.scheduleOptionsOverride = testScheduleOptionsSeconds
|
||||
b.schedule = &TestSchedule{}
|
||||
b.credRotationQueue = queue.New()
|
||||
b.populateQueue(context.Background(), config.StorageView)
|
||||
|
||||
@@ -1548,3 +1550,27 @@ func assertPriorityUnchanged(t *testing.T, priority int64, nextRotationTime time
|
||||
t.Fatalf("expected next rotation at %s, but got %s", nextRotationTime, time.Unix(priority, 0).String())
|
||||
}
|
||||
}
|
||||
|
||||
var _ schedule.Scheduler = &TestSchedule{}
|
||||
|
||||
type TestSchedule struct{}
|
||||
|
||||
func (d *TestSchedule) Parse(rotationSchedule string) (*cron.SpecSchedule, error) {
|
||||
parser := cron.NewParser(testScheduleParseOptions)
|
||||
schedule, err := parser.Parse(rotationSchedule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sched, ok := schedule.(*cron.SpecSchedule)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid rotation schedule")
|
||||
}
|
||||
return sched, nil
|
||||
}
|
||||
|
||||
func (d *TestSchedule) ValidateRotationWindow(s int) error {
|
||||
if s < testMinRotationWindowSeconds {
|
||||
return fmt.Errorf("rotation_window must be %d seconds or more", testMinRotationWindowSeconds)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
42
builtin/logical/database/schedule/schedule.go
Normal file
42
builtin/logical/database/schedule/schedule.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package schedule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
// Minimum allowed value for rotation_window
|
||||
minRotationWindowSeconds = 3600
|
||||
parseOptions = cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow
|
||||
)
|
||||
|
||||
type Scheduler interface {
|
||||
Parse(string) (*cron.SpecSchedule, error)
|
||||
ValidateRotationWindow(int) error
|
||||
}
|
||||
|
||||
var _ Scheduler = &DefaultSchedule{}
|
||||
|
||||
type DefaultSchedule struct{}
|
||||
|
||||
func (d *DefaultSchedule) Parse(rotationSchedule string) (*cron.SpecSchedule, error) {
|
||||
parser := cron.NewParser(parseOptions)
|
||||
schedule, err := parser.Parse(rotationSchedule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sched, ok := schedule.(*cron.SpecSchedule)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid rotation schedule")
|
||||
}
|
||||
return sched, nil
|
||||
}
|
||||
|
||||
func (d *DefaultSchedule) ValidateRotationWindow(s int) error {
|
||||
if s < minRotationWindowSeconds {
|
||||
return fmt.Errorf("rotation_window must be %d seconds or more", minRotationWindowSeconds)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user