Vault Automation 0c6c13dd38
license: update headers to IBM Corp. (#10229) (#10233)
* license: update headers to IBM Corp.
* `make proto`
* update offset because source file changed

Signed-off-by: Ryan Cragun <me@ryan.ec>
Co-authored-by: Ryan Cragun <me@ryan.ec>
2025-10-21 15:20:20 -06:00

141 lines
4.6 KiB
Go

// Copyright IBM Corp. 2016, 2025
// SPDX-License-Identifier: BUSL-1.1
package database
import (
"context"
"errors"
"github.com/hashicorp/vault/sdk/database/dbplugin"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/logical"
"github.com/mitchellh/mapstructure"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// WAL storage key used for the rollback of root database credentials
const rotateRootWALKey = "rotateRootWALKey"
// WAL entry used for the rollback of root database credentials
type rotateRootCredentialsWAL struct {
ConnectionName string
UserName string
NewPassword string
OldPassword string
NewPublicKey string
NewPrivateKey string
OldPrivateKey string
}
func NewRotateRootCredentialsWALPasswordEntry(connectionName, userName, newPassword, oldPassword string) *rotateRootCredentialsWAL {
return &rotateRootCredentialsWAL{
ConnectionName: connectionName,
UserName: userName,
NewPassword: newPassword,
OldPassword: oldPassword,
}
}
func NewRotateRootCredentialsWALPrivateKeyEntry(connectionName, userName, newPublicKey, newPrivateKey, oldPrivateKey string) *rotateRootCredentialsWAL {
return &rotateRootCredentialsWAL{
ConnectionName: connectionName,
UserName: userName,
NewPublicKey: newPublicKey,
NewPrivateKey: newPrivateKey,
OldPrivateKey: oldPrivateKey,
}
}
// walRollback handles WAL entries that result from partial failures
// to rotate the root credentials of a database. It is responsible
// for rolling back root database credentials when doing so would
// reconcile the credentials with Vault storage.
func (b *databaseBackend) walRollback(ctx context.Context, req *logical.Request, kind string, data interface{}) error {
if kind != rotateRootWALKey {
return errors.New("unknown type to rollback")
}
// Decode the WAL data
var entry rotateRootCredentialsWAL
if err := mapstructure.Decode(data, &entry); err != nil {
return err
}
// Get the current database configuration from storage
config, err := b.DatabaseConfig(ctx, req.Storage, entry.ConnectionName)
if err != nil {
return err
}
// The password in storage doesn't match the new password
// in the WAL entry. This means there was a partial failure
// to update either the database or storage.
if config.ConnectionDetails["password"] != entry.NewPassword {
// Clear any cached connection to inform the rollback decision
err := b.ClearConnection(entry.ConnectionName)
if err != nil {
return err
}
// Attempt to get a connection with the current configuration.
// If successful, the WAL entry can be deleted. This means
// the root credentials are the same according to the database
// and storage.
_, err = b.GetConnection(ctx, req.Storage, entry.ConnectionName)
if err == nil {
return nil
}
return b.rollbackDatabaseCredentials(ctx, config, entry)
}
// The password in storage matches the new password in
// the WAL entry, so there is nothing to roll back. This
// means the new password was successfully updated in the
// database and storage, but the WAL wasn't deleted.
return nil
}
// rollbackDatabaseCredentials rolls back root database credentials for
// the connection associated with the passed WAL entry. It will create
// a connection to the database using the WAL entry new password in
// order to alter the password to be the WAL entry old password.
func (b *databaseBackend) rollbackDatabaseCredentials(ctx context.Context, config *DatabaseConfig, entry rotateRootCredentialsWAL) error {
// Attempt to get a connection with the WAL entry new password.
config.ConnectionDetails["password"] = entry.NewPassword
dbi, err := b.GetConnectionWithConfig(ctx, entry.ConnectionName, config)
if err != nil {
return err
}
// Ensure the connection used to roll back the database password is not cached
defer func() {
if err := b.ClearConnection(entry.ConnectionName); err != nil {
b.Logger().Error("error closing database plugin connection", "err", err)
}
}()
updateReq := v5.UpdateUserRequest{
Username: entry.UserName,
CredentialType: v5.CredentialTypePassword,
Password: &v5.ChangePassword{
NewPassword: entry.OldPassword,
Statements: v5.Statements{
Commands: config.RootCredentialsRotateStatements,
},
},
}
// It actually is the root user here, but we only want to use SetCredentials since
// RotateRootCredentials doesn't give any control over what password is used
_, err = dbi.database.UpdateUser(ctx, updateReq, false)
if status.Code(err) == codes.Unimplemented || err == dbplugin.ErrPluginStaticUnsupported {
return nil
}
return err
}