mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 04:16:31 +02:00
Add DBv5 plugin serving & management functions (#9745)
This mirrors what DBv4 is doing, but with the updated interface
This commit is contained in:
parent
9afb87ab34
commit
75b2f42ef2
42
sdk/database/newdbplugin/grpc_database_plugin.go
Normal file
42
sdk/database/newdbplugin/grpc_database_plugin.go
Normal file
@ -0,0 +1,42 @@
|
||||
package newdbplugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/database/newdbplugin/proto"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// handshakeConfigs are used to just do a basic handshake between
|
||||
// a plugin and host. If the handshake fails, a user friendly error is shown.
|
||||
// This prevents users from executing bad plugins or executing a plugin
|
||||
// directory. It is a UX feature, not a security feature.
|
||||
var handshakeConfig = plugin.HandshakeConfig{
|
||||
ProtocolVersion: 5,
|
||||
MagicCookieKey: "VAULT_DATABASE_PLUGIN",
|
||||
MagicCookieValue: "926a0820-aea2-be28-51d6-83cdf00e8edb",
|
||||
}
|
||||
|
||||
type GRPCDatabasePlugin struct {
|
||||
Impl Database
|
||||
|
||||
// Embeding this will disable the netRPC protocol
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
}
|
||||
|
||||
var _ plugin.Plugin = &GRPCDatabasePlugin{}
|
||||
var _ plugin.GRPCPlugin = &GRPCDatabasePlugin{}
|
||||
|
||||
func (d GRPCDatabasePlugin) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) error {
|
||||
proto.RegisterDatabaseServer(s, gRPCServer{impl: d.Impl})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (GRPCDatabasePlugin) GRPCClient(doneCtx context.Context, _ *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
||||
client := gRPCClient{
|
||||
client: proto.NewDatabaseClient(c),
|
||||
doneCtx: doneCtx,
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
80
sdk/database/newdbplugin/plugin_client.go
Normal file
80
sdk/database/newdbplugin/plugin_client.go
Normal file
@ -0,0 +1,80 @@
|
||||
package newdbplugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
)
|
||||
|
||||
// DatabasePluginClient embeds a databasePluginRPCClient and wraps it's Close
|
||||
// method to also call Kill() on the plugin.Client.
|
||||
type DatabasePluginClient struct {
|
||||
client *plugin.Client
|
||||
sync.Mutex
|
||||
|
||||
Database
|
||||
}
|
||||
|
||||
// This wraps the Close call and ensures we both close the database connection
|
||||
// and kill the plugin.
|
||||
func (dc *DatabasePluginClient) Close() error {
|
||||
err := dc.Database.Close()
|
||||
dc.client.Kill()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewPluginClient returns a databaseRPCClient with a connection to a running
|
||||
// plugin. The client is wrapped in a DatabasePluginClient object to ensure the
|
||||
// plugin is killed on call of Close().
|
||||
func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner, logger log.Logger, isMetadataMode bool) (Database, error) {
|
||||
// pluginSets is the map of plugins we can dispense.
|
||||
pluginSets := map[int]plugin.PluginSet{
|
||||
5: plugin.PluginSet{
|
||||
"database": new(GRPCDatabasePlugin),
|
||||
},
|
||||
}
|
||||
|
||||
var client *plugin.Client
|
||||
var err error
|
||||
if isMetadataMode {
|
||||
client, err = pluginRunner.RunMetadataMode(ctx, sys, pluginSets, handshakeConfig, []string{}, logger)
|
||||
} else {
|
||||
client, err = pluginRunner.Run(ctx, sys, pluginSets, handshakeConfig, []string{}, logger)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Connect via RPC
|
||||
rpcClient, err := client.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Request the plugin
|
||||
raw, err := rpcClient.Dispense("database")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We should have a database type now. This feels like a normal interface
|
||||
// implementation but is in fact over an RPC connection.
|
||||
var db Database
|
||||
switch raw.(type) {
|
||||
case gRPCClient:
|
||||
db = raw.(gRPCClient)
|
||||
default:
|
||||
return nil, errors.New("unsupported client type")
|
||||
}
|
||||
|
||||
// Wrap RPC implementation in DatabasePluginClient
|
||||
return &DatabasePluginClient{
|
||||
client: client,
|
||||
Database: db,
|
||||
}, nil
|
||||
}
|
||||
78
sdk/database/newdbplugin/plugin_factory.go
Normal file
78
sdk/database/newdbplugin/plugin_factory.go
Normal file
@ -0,0 +1,78 @@
|
||||
package newdbplugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
)
|
||||
|
||||
// PluginFactory is used to build plugin database types. It wraps the database
|
||||
// object in a logging and metrics middleware.
|
||||
func PluginFactory(ctx context.Context, pluginName string, sys pluginutil.LookRunnerUtil, logger log.Logger) (Database, error) {
|
||||
// Look for plugin in the plugin catalog
|
||||
pluginRunner, err := sys.LookupPlugin(ctx, pluginName, consts.PluginTypeDatabase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namedLogger := logger.Named(pluginName)
|
||||
|
||||
var transport string
|
||||
var db Database
|
||||
if pluginRunner.Builtin {
|
||||
// Plugin is builtin so we can retrieve an instance of the interface
|
||||
// from the pluginRunner. Then cast it to a Database.
|
||||
dbRaw, err := pluginRunner.BuiltinFactory()
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("error initializing plugin: {{err}}", err)
|
||||
}
|
||||
|
||||
var ok bool
|
||||
db, ok = dbRaw.(Database)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported database type: %q", pluginName)
|
||||
}
|
||||
|
||||
transport = "builtin"
|
||||
|
||||
} else {
|
||||
// create a DatabasePluginClient instance
|
||||
db, err = NewPluginClient(ctx, sys, pluginRunner, namedLogger, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Switch on the underlying database client type to get the transport
|
||||
// method.
|
||||
switch db.(*DatabasePluginClient).Database.(type) {
|
||||
case *gRPCClient:
|
||||
transport = "gRPC"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
typeStr, err := db.Type()
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("error getting plugin type: {{err}}", err)
|
||||
}
|
||||
|
||||
// Wrap with metrics middleware
|
||||
db = &databaseMetricsMiddleware{
|
||||
next: db,
|
||||
typeStr: typeStr,
|
||||
}
|
||||
|
||||
// Wrap with tracing middleware
|
||||
if namedLogger.IsTrace() {
|
||||
db = &databaseTracingMiddleware{
|
||||
next: db,
|
||||
logger: namedLogger.With("transport", transport),
|
||||
}
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
42
sdk/database/newdbplugin/plugin_server.go
Normal file
42
sdk/database/newdbplugin/plugin_server.go
Normal file
@ -0,0 +1,42 @@
|
||||
package newdbplugin
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
)
|
||||
|
||||
// Serve is called from within a plugin and wraps the provided
|
||||
// Database implementation in a databasePluginRPCServer object and starts a
|
||||
// RPC server.
|
||||
func Serve(db Database, tlsProvider func() (*tls.Config, error)) {
|
||||
plugin.Serve(ServeConfig(db, tlsProvider))
|
||||
}
|
||||
|
||||
func ServeConfig(db Database, tlsProvider func() (*tls.Config, error)) *plugin.ServeConfig {
|
||||
err := pluginutil.OptionallyEnableMlock()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// pluginSets is the map of plugins we can dispense.
|
||||
pluginSets := map[int]plugin.PluginSet{
|
||||
5: plugin.PluginSet{
|
||||
"database": &GRPCDatabasePlugin{
|
||||
Impl: db,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
conf := &plugin.ServeConfig{
|
||||
HandshakeConfig: handshakeConfig,
|
||||
VersionedPlugins: pluginSets,
|
||||
TLSProvider: tlsProvider,
|
||||
GRPCServer: plugin.DefaultGRPCServer,
|
||||
}
|
||||
|
||||
return conf
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user