Documentation
¶
Overview ¶
Package dbrotate rotates a managed database's owner password in the engine itself, then switches the stored credential every app reads.
Index ¶
- Constants
- Variables
- type Apps
- type Auditor
- type Databases
- type Driver
- type Exec
- type Preview
- type Result
- type Secrets
- type Service
- func (s *Service) Heal(ctx context.Context, name string) error
- func (s *Service) Preview(ctx context.Context, name string) (Preview, error)
- func (s *Service) Rotate(ctx context.Context, name, actor string) (Result, error)
- func (s *Service) Run(ctx context.Context, interval time.Duration) error
- func (s *Service) SetInterval(ctx context.Context, name string, days int, actor string) error
- func (s *Service) Sweep(ctx context.Context) int
- type Store
- type Target
Constants ¶
const ( ReasonVerifyFailed = "verify_failed" ReasonApplyFailed = "apply_failed" ReasonRefUpdate = "reference_update_failed" ReasonUnresolved = "unresolved" ReasonInterrupted = "interrupted" ActionStart = "database.password.rotate.start" ActionComplete = "database.password.rotate" ActionRollback = "database.password.rotate.rollback" ActionPolicy = "database.password.policy" SystemActor = "database-password-rotation" )
Reasons stored on a rotation row. Fixed strings, never engine output.
Variables ¶
var ( ErrUnsupported = errors.New("this engine has no owner password to rotate") ErrNoSecrets = errors.New("secrets are not configured on this control plane") ErrNotRunning = errors.New("database has no running container") ErrPending = errors.New("a previous rotation is not settled yet") ErrVerifyFailed = errors.New("new password was refused by the engine, the old one was restored") ErrBadInterval = errors.New("interval must be 0 to 3650 days") )
Typed failures the API maps to status codes.
var ErrEngine = errors.New("database engine refused the statement")
ErrEngine wraps every engine-side failure. It never carries engine output.
Functions ¶
This section is empty.
Types ¶
type Apps ¶
type Apps interface {
// Dependents splits linked apps into running ones, which restart, and
// suspended ones, which pick the new credential up when started.
Dependents(ctx context.Context, database string) (running, suspended []string, err error)
Restart(ctx context.Context, database string) []string
}
Apps lists and restarts the apps that read a database credential.
type Databases ¶
type Databases interface {
GetDesiredDatabase(ctx context.Context, name string) (*store.DesiredDatabase, error)
}
Databases reads desired database records.
type Driver ¶
type Driver interface {
// Set makes newPassword the owner password. adminPassword is the one in
// force now, for engines that authenticate the admin session.
Set(ctx context.Context, ex Exec, containerID, database, adminPassword, newPassword string) error
// Verify logs in as the owner with password and fails if it is refused.
Verify(ctx context.Context, ex Exec, containerID, database, password string) error
}
Driver changes and checks one engine's owner password. Passwords travel on stdin only, never in a command line or an error.
type Exec ¶
type Exec interface {
ExecWithInput(ctx context.Context, containerID string, cmd []string, stdin io.Reader) (io.ReadCloser, error)
}
Exec is the slice of the container runtime a driver needs.
type Preview ¶
type Preview struct {
Database string
Engine string
Supported bool
Reason string
RestartRequired bool
Apps []string
SuspendedApps []string
State string
LastError string
LastChangedAt time.Time
IntervalDays int
NextDue time.Time
}
Preview is what an operator sees before confirming a rotation.
type Secrets ¶
type Secrets interface {
Resolve(ctx context.Context, serviceName, envKey string) (string, error)
Exists(ctx context.Context, serviceName, envKey string) (bool, error)
SetValueGuarded(ctx context.Context, serviceName, envKey, plaintext string, overwriteLocked bool) error
ListKeys(ctx context.Context, serviceName string) ([]store.SecretKeyInfo, error)
DeleteValue(ctx context.Context, serviceName, envKey string) error
}
Secrets is the encrypted credential store.
type Service ¶
type Service struct {
Store Store
Databases Databases
Secrets Secrets
Target Target
Apps Apps
Auditor Auditor
Logger *slog.Logger
Now func() time.Time
// contains filtered or unexported fields
}
Service rotates database owner passwords.
func (*Service) Heal ¶
Heal settles name's unsettled rotation. A row that is not pending is left alone.
func (*Service) Rotate ¶
Rotate performs one staged rotation: stage the new password, apply it in the engine, verify a real login, switch the stored credential, then restart the linked apps. A failed verification restores the old password.
func (*Service) SetInterval ¶
SetInterval sets how often name rotates on its own, in days (0 is off).
type Store ¶
type Store interface {
GetDatabasePasswordRotation(ctx context.Context, name string) (store.DatabasePasswordRotation, error)
ListDatabasePasswordRotations(ctx context.Context) ([]store.DatabasePasswordRotation, error)
SetDatabasePasswordRotationInterval(ctx context.Context, name string, days int, now time.Time) error
SaveDatabasePasswordRotationState(ctx context.Context, name, state, reason string, lastRotated, pendingSince, now time.Time) error
DeleteDatabasePasswordRotation(ctx context.Context, name string) error
}
Store is the rotation persistence. *store.DB satisfies it.