open-vault/builtin/credential/approle/backend.go
Christopher Swenson 2c8e88ab67
Check if plugin version matches running version (#17182)
Check if plugin version matches running version

When registering a plugin, we check if the request version matches the
self-reported version from the plugin. If these do not match, we log a
warning.

This uncovered a few missing pieces for getting the database version
code fully working.

We added an environment variable that helps us unit test the running
version behavior as well, but only for approle, postgresql, and consul
plugins.

Return 400 on plugin not found or version mismatch

Populate the running SHA256 of plugins in the mount and auth tables (#17217)
2022-09-21 12:25:04 -07:00

178 lines
5.3 KiB
Go

package approle
import (
"context"
"sync"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/locksutil"
"github.com/hashicorp/vault/sdk/helper/salt"
"github.com/hashicorp/vault/sdk/logical"
)
const (
secretIDPrefix = "secret_id/"
secretIDLocalPrefix = "secret_id_local/"
secretIDAccessorPrefix = "accessor/"
secretIDAccessorLocalPrefix = "accessor_local/"
)
// ReportedVersion is used to report a specific version to Vault.
var ReportedVersion = ""
type backend struct {
*framework.Backend
// The salt value to be used by the information to be accessed only
// by this backend.
salt *salt.Salt
saltMutex sync.RWMutex
// The view to use when creating the salt
view logical.Storage
// Guard to clean-up the expired SecretID entries
tidySecretIDCASGuard *uint32
// Locks to make changes to role entries. These will be initialized to a
// predefined number of locks when the backend is created, and will be
// indexed based on salted role names.
roleLocks []*locksutil.LockEntry
// Locks to make changes to the storage entries of RoleIDs generated. These
// will be initialized to a predefined number of locks when the backend is
// created, and will be indexed based on the salted RoleIDs.
roleIDLocks []*locksutil.LockEntry
// Locks to make changes to the storage entries of SecretIDs generated.
// These will be initialized to a predefined number of locks when the
// backend is created, and will be indexed based on the HMAC-ed SecretIDs.
secretIDLocks []*locksutil.LockEntry
// Locks to make changes to the storage entries of SecretIDAccessors
// generated. These will be initialized to a predefined number of locks
// when the backend is created, and will be indexed based on the
// SecretIDAccessors itself.
secretIDAccessorLocks []*locksutil.LockEntry
// secretIDListingLock is a dedicated lock for listing SecretIDAccessors
// for all the SecretIDs issued against an approle
secretIDListingLock sync.RWMutex
}
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
b, err := Backend(conf)
if err != nil {
return nil, err
}
if err := b.Setup(ctx, conf); err != nil {
return nil, err
}
return b, nil
}
func Backend(conf *logical.BackendConfig) (*backend, error) {
// Create a backend object
b := &backend{
view: conf.StorageView,
// Create locks to modify the registered roles
roleLocks: locksutil.CreateLocks(),
// Create locks to modify the generated RoleIDs
roleIDLocks: locksutil.CreateLocks(),
// Create locks to modify the generated SecretIDs
secretIDLocks: locksutil.CreateLocks(),
// Create locks to modify the generated SecretIDAccessors
secretIDAccessorLocks: locksutil.CreateLocks(),
tidySecretIDCASGuard: new(uint32),
}
// Attach the paths and secrets that are to be handled by the backend
b.Backend = &framework.Backend{
// Register a periodic function that deletes the expired SecretID entries
PeriodicFunc: b.periodicFunc,
Help: backendHelp,
AuthRenew: b.pathLoginRenew,
PathsSpecial: &logical.Paths{
Unauthenticated: []string{
"login",
},
LocalStorage: []string{
secretIDLocalPrefix,
secretIDAccessorLocalPrefix,
},
},
Paths: framework.PathAppend(
rolePaths(b),
[]*framework.Path{
pathLogin(b),
pathTidySecretID(b),
},
),
Invalidate: b.invalidate,
BackendType: logical.TypeCredential,
RunningVersion: ReportedVersion,
}
return b, nil
}
func (b *backend) Salt(ctx context.Context) (*salt.Salt, error) {
b.saltMutex.RLock()
if b.salt != nil {
defer b.saltMutex.RUnlock()
return b.salt, nil
}
b.saltMutex.RUnlock()
b.saltMutex.Lock()
defer b.saltMutex.Unlock()
if b.salt != nil {
return b.salt, nil
}
salt, err := salt.NewSalt(ctx, b.view, &salt.Config{
HashFunc: salt.SHA256Hash,
Location: salt.DefaultLocation,
})
if err != nil {
return nil, err
}
b.salt = salt
return salt, nil
}
func (b *backend) invalidate(_ context.Context, key string) {
switch key {
case salt.DefaultLocation:
b.saltMutex.Lock()
defer b.saltMutex.Unlock()
b.salt = nil
}
}
// periodicFunc of the backend will be invoked once a minute by the RollbackManager.
// RoleRole backend utilizes this function to delete expired SecretID entries.
// This could mean that the SecretID may live in the backend upto 1 min after its
// expiration. The deletion of SecretIDs are not security sensitive and it is okay
// to delay the removal of SecretIDs by a minute.
func (b *backend) periodicFunc(ctx context.Context, req *logical.Request) error {
// Initiate clean-up of expired SecretID entries
if b.System().LocalMount() || !b.System().ReplicationState().HasState(consts.ReplicationPerformanceSecondary|consts.ReplicationPerformanceStandby) {
b.tidySecretID(ctx, req)
}
return nil
}
const backendHelp = `
Any registered Role can authenticate itself with Vault. The credentials
depends on the constraints that are set on the Role. One common required
credential is the 'role_id' which is a unique identifier of the Role.
It can be retrieved from the 'role/<appname>/role-id' endpoint.
The default constraint configuration is 'bind_secret_id', which requires
the credential 'secret_id' to be presented during login. Refer to the
documentation for other types of constraints.`