mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-04 04:28:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			117 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) HashiCorp, Inc.
 | 
						|
// SPDX-License-Identifier: MPL-2.0
 | 
						|
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
// 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
 | 
						|
}
 |