Calls to builtin plugins now go directly to the implementation instead of go-plugin

This commit is contained in:
Brian Kassouf
2017-04-20 18:46:41 -07:00
parent d9ce189b33
commit f1fa617e03
13 changed files with 94 additions and 179 deletions

View File

@@ -11,10 +11,10 @@ import (
"testing" "testing"
"github.com/hashicorp/vault/builtin/logical/database/dbplugin" "github.com/hashicorp/vault/builtin/logical/database/dbplugin"
"github.com/hashicorp/vault/helper/builtinplugins"
"github.com/hashicorp/vault/helper/pluginutil" "github.com/hashicorp/vault/helper/pluginutil"
"github.com/hashicorp/vault/http" "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/plugins/database/postgresql"
"github.com/hashicorp/vault/vault" "github.com/hashicorp/vault/vault"
"github.com/lib/pq" "github.com/lib/pq"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@@ -91,8 +91,7 @@ func TestBackend_PluginMain(t *testing.T) {
return return
} }
f, _ := builtinplugins.BuiltinPlugins.Get("postgresql-database-plugin") postgresql.Run()
f()
} }
func TestBackend_config_connection(t *testing.T) { func TestBackend_config_connection(t *testing.T) {

View File

@@ -33,15 +33,32 @@ type Statements struct {
// object in a logging and metrics middleware. // object in a logging and metrics middleware.
func PluginFactory(pluginName string, sys pluginutil.LookWrapper, logger log.Logger) (DatabaseType, error) { func PluginFactory(pluginName string, sys pluginutil.LookWrapper, logger log.Logger) (DatabaseType, error) {
// Look for plugin in the plugin catalog // Look for plugin in the plugin catalog
pluginMeta, err := sys.LookupPlugin(pluginName) pluginRunner, err := sys.LookupPlugin(pluginName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// create a DatabasePluginClient instance var db DatabaseType
db, err := newPluginClient(sys, pluginMeta) if pluginRunner.Builtin {
if err != nil { // Plugin is builtin so we can retrieve an instance of the interface
return nil, err // from the pluginRunner. Then cast it to a DatabaseType.
dbRaw, err := pluginRunner.BuiltinFactory()
if err != nil {
return nil, fmt.Errorf("error getting plugin type: %s", err)
}
var ok bool
db, ok = dbRaw.(DatabaseType)
if !ok {
return nil, fmt.Errorf("unsuported database type: %s", pluginName)
}
} else {
// create a DatabasePluginClient instance
db, err = newPluginClient(sys, pluginRunner)
if err != nil {
return nil, err
}
} }
typeStr, err := db.Type() typeStr, err := db.Type()

View File

@@ -331,11 +331,5 @@ func Commands(metaPtr *meta.Meta) map[string]cli.CommandFactory {
Ui: metaPtr.Ui, Ui: metaPtr.Ui,
}, nil }, nil
}, },
"plugin-exec": func() (cli.Command, error) {
return &command.PluginExec{
Meta: *metaPtr,
}, nil
},
} }
} }

View File

@@ -1,66 +0,0 @@
package command
import (
"fmt"
"strings"
"github.com/hashicorp/vault/helper/builtinplugins"
"github.com/hashicorp/vault/meta"
)
type PluginExec struct {
meta.Meta
}
func (c *PluginExec) Run(args []string) int {
flags := c.Meta.FlagSet("plugin-exec", meta.FlagSetDefault)
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
return 1
}
args = flags.Args()
if len(args) != 1 {
flags.Usage()
c.Ui.Error(fmt.Sprintf(
"\nplugin-exec expects one argument: the plugin to execute."))
return 1
}
pluginName := args[0]
runner, ok := builtinplugins.BuiltinPlugins.Get(pluginName)
if !ok {
c.Ui.Error(fmt.Sprintf(
"No plugin with the name %s found", pluginName))
return 1
}
err := runner()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error running plugin: %s", err))
return 1
}
return 0
}
func (c *PluginExec) Synopsis() string {
return "Runs a builtin plugin. Should only be called by vault."
}
func (c *PluginExec) Help() string {
helpText := `
Usage: vault plugin-exec type
Runs a builtin plugin. Should only be called by vault.
This will execute a plugin for use in a plugable location in vault. If run by
a cli user it will print a message indicating it can not be executed by anyone
other than vault. For supported plugin types see the vault documentation.
General Options:
` + meta.GeneralOptionsUsage()
return strings.TrimSpace(helpText)
}

View File

@@ -1,10 +1,8 @@
package command package command
import ( import (
"crypto/sha256"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@@ -133,33 +131,6 @@ func (c *ServerCommand) Run(args []string) int {
dev = true dev = true
} }
// Record the vault binary's location and SHA-256 checksum for use in
// builtin plugins.
ex, err := os.Executable()
if err != nil {
c.Ui.Output(fmt.Sprintf(
"Error looking up vault binary: %s", err))
return 1
}
file, err := os.Open(ex)
if err != nil {
c.Ui.Output(fmt.Sprintf(
"Error loading vault binary: %s", err))
return 1
}
defer file.Close()
hash := sha256.New()
_, err = io.Copy(hash, file)
if err != nil {
c.Ui.Output(fmt.Sprintf(
"Error checksumming vault binary: %s", err))
return 1
}
sha256Value := hash.Sum(nil)
// Validation // Validation
if !dev { if !dev {
switch { switch {
@@ -254,23 +225,21 @@ func (c *ServerCommand) Run(args []string) int {
} }
coreConfig := &vault.CoreConfig{ coreConfig := &vault.CoreConfig{
Physical: backend, Physical: backend,
RedirectAddr: config.Storage.RedirectAddr, RedirectAddr: config.Storage.RedirectAddr,
HAPhysical: nil, HAPhysical: nil,
Seal: seal, Seal: seal,
AuditBackends: c.AuditBackends, AuditBackends: c.AuditBackends,
CredentialBackends: c.CredentialBackends, CredentialBackends: c.CredentialBackends,
LogicalBackends: c.LogicalBackends, LogicalBackends: c.LogicalBackends,
Logger: c.logger, Logger: c.logger,
DisableCache: config.DisableCache, DisableCache: config.DisableCache,
DisableMlock: config.DisableMlock, DisableMlock: config.DisableMlock,
MaxLeaseTTL: config.MaxLeaseTTL, MaxLeaseTTL: config.MaxLeaseTTL,
DefaultLeaseTTL: config.DefaultLeaseTTL, DefaultLeaseTTL: config.DefaultLeaseTTL,
ClusterName: config.ClusterName, ClusterName: config.ClusterName,
CacheSize: config.CacheSize, CacheSize: config.CacheSize,
PluginDirectory: config.PluginDirectory, PluginDirectory: config.PluginDirectory,
VaultBinaryLocation: ex,
VaultBinarySHA256: sha256Value,
} }
if dev { if dev {
coreConfig.DevToken = devRootTokenID coreConfig.DevToken = devRootTokenID

View File

@@ -5,20 +5,22 @@ import (
"github.com/hashicorp/vault/plugins/database/postgresql" "github.com/hashicorp/vault/plugins/database/postgresql"
) )
type BuiltinFactory func() (interface{}, error)
var BuiltinPlugins *builtinPlugins = &builtinPlugins{ var BuiltinPlugins *builtinPlugins = &builtinPlugins{
plugins: map[string]func() error{ plugins: map[string]BuiltinFactory{
"mysql-database-plugin": mysql.Run, "mysql-database-plugin": mysql.New,
"postgresql-database-plugin": postgresql.Run, "postgresql-database-plugin": postgresql.New,
}, },
} }
// The list of builtin plugins should not be changed by any other package, so we // The list of builtin plugins should not be changed by any other package, so we
// store them in an unexported variable in this unexported struct. // store them in an unexported variable in this unexported struct.
type builtinPlugins struct { type builtinPlugins struct {
plugins map[string]func() error plugins map[string]BuiltinFactory
} }
func (b *builtinPlugins) Get(name string) (func() error, bool) { func (b *builtinPlugins) Get(name string) (BuiltinFactory, bool) {
f, ok := b.plugins[name] f, ok := b.plugins[name]
return f, ok return f, ok
} }

View File

@@ -40,11 +40,12 @@ type LookWrapper interface {
// PluginRunner defines the metadata needed to run a plugin securely with // PluginRunner defines the metadata needed to run a plugin securely with
// go-plugin. // go-plugin.
type PluginRunner struct { type PluginRunner struct {
Name string `json:"name"` Name string `json:"name"`
Command string `json:"command"` Command string `json:"command"`
Args []string `json:"args"` Args []string `json:"args"`
Sha256 []byte `json:"sha256"` Sha256 []byte `json:"sha256"`
Builtin bool `json:"builtin"` Builtin bool `json:"builtin"`
BuiltinFactory func() (interface{}, error) `json:"-"`
} }
// Run takes a wrapper instance, and the go-plugin paramaters and executes a // Run takes a wrapper instance, and the go-plugin paramaters and executes a

View File

@@ -23,7 +23,7 @@ type MySQL struct {
credsutil.CredentialsProducer credsutil.CredentialsProducer
} }
func New() *MySQL { func New() (interface{}, error) {
connProducer := &connutil.SQLConnectionProducer{} connProducer := &connutil.SQLConnectionProducer{}
connProducer.Type = mySQLTypeName connProducer.Type = mySQLTypeName
@@ -37,14 +37,17 @@ func New() *MySQL {
CredentialsProducer: credsProducer, CredentialsProducer: credsProducer,
} }
return dbType return dbType, nil
} }
// Run instantiates a MySQL object, and runs the RPC server for the plugin // Run instantiates a MySQL object, and runs the RPC server for the plugin
func Run() error { func Run() error {
dbType := New() dbType, err := New()
if err != nil {
return err
}
dbplugin.NewPluginServer(dbType) dbplugin.NewPluginServer(dbType.(*MySQL))
return nil return nil
} }

View File

@@ -66,7 +66,8 @@ func TestMySQL_Initialize(t *testing.T) {
"connection_url": connURL, "connection_url": connURL,
} }
db := New() dbRaw, _ := New()
db := dbRaw.(*MySQL)
connProducer := db.ConnectionProducer.(*connutil.SQLConnectionProducer) connProducer := db.ConnectionProducer.(*connutil.SQLConnectionProducer)
err := db.Initialize(connectionDetails, true) err := db.Initialize(connectionDetails, true)
@@ -92,7 +93,8 @@ func TestMySQL_CreateUser(t *testing.T) {
"connection_url": connURL, "connection_url": connURL,
} }
db := New() dbRaw, _ := New()
db := dbRaw.(*MySQL)
err := db.Initialize(connectionDetails, true) err := db.Initialize(connectionDetails, true)
if err != nil { if err != nil {
@@ -127,7 +129,8 @@ func TestMySQL_RevokeUser(t *testing.T) {
"connection_url": connURL, "connection_url": connURL,
} }
db := New() dbRaw, _ := New()
db := dbRaw.(*MySQL)
err := db.Initialize(connectionDetails, true) err := db.Initialize(connectionDetails, true)
if err != nil { if err != nil {

View File

@@ -16,7 +16,8 @@ import (
const postgreSQLTypeName string = "postgres" const postgreSQLTypeName string = "postgres"
func New() *PostgreSQL { // New implements builtinplugins.BuiltinFactory
func New() (interface{}, error) {
connProducer := &connutil.SQLConnectionProducer{} connProducer := &connutil.SQLConnectionProducer{}
connProducer.Type = postgreSQLTypeName connProducer.Type = postgreSQLTypeName
@@ -30,14 +31,17 @@ func New() *PostgreSQL {
CredentialsProducer: credsProducer, CredentialsProducer: credsProducer,
} }
return dbType return dbType, nil
} }
// Run instatiates a PostgreSQL object, and runs the RPC server for the plugin // Run instatiates a PostgreSQL object, and runs the RPC server for the plugin
func Run() error { func Run() error {
dbType := New() dbType, err := New()
if err != nil {
return err
}
dbplugin.NewPluginServer(dbType) dbplugin.NewPluginServer(dbType.(*PostgreSQL))
return nil return nil
} }

View File

@@ -66,7 +66,9 @@ func TestPostgreSQL_Initialize(t *testing.T) {
"connection_url": connURL, "connection_url": connURL,
} }
db := New() dbRaw, _ := New()
db := dbRaw.(*PostgreSQL)
connProducer := db.ConnectionProducer.(*connutil.SQLConnectionProducer) connProducer := db.ConnectionProducer.(*connutil.SQLConnectionProducer)
err := db.Initialize(connectionDetails, true) err := db.Initialize(connectionDetails, true)
@@ -92,7 +94,8 @@ func TestPostgreSQL_CreateUser(t *testing.T) {
"connection_url": connURL, "connection_url": connURL,
} }
db := New() dbRaw, _ := New()
db := dbRaw.(*PostgreSQL)
err := db.Initialize(connectionDetails, true) err := db.Initialize(connectionDetails, true)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@@ -136,7 +139,8 @@ func TestPostgreSQL_RenewUser(t *testing.T) {
"connection_url": connURL, "connection_url": connURL,
} }
db := New() dbRaw, _ := New()
db := dbRaw.(*PostgreSQL)
err := db.Initialize(connectionDetails, true) err := db.Initialize(connectionDetails, true)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@@ -176,7 +180,8 @@ func TestPostgreSQL_RevokeUser(t *testing.T) {
"connection_url": connURL, "connection_url": connURL,
} }
db := New() dbRaw, _ := New()
db := dbRaw.(*PostgreSQL)
err := db.Initialize(connectionDetails, true) err := db.Initialize(connectionDetails, true)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)

View File

@@ -335,12 +335,6 @@ type Core struct {
// pluginDirectory is the location vault will look for plugin binaries // pluginDirectory is the location vault will look for plugin binaries
pluginDirectory string pluginDirectory string
// vaultBinaryLocation is used to run builtin plugins in secure mode
vaultBinaryLocation string
// vaultBinarySHA256 is used to run builtin plugins in secure mode
vaultBinarySHA256 []byte
// pluginCatalog is used to manage plugin configurations // pluginCatalog is used to manage plugin configurations
pluginCatalog *PluginCatalog pluginCatalog *PluginCatalog
@@ -389,9 +383,7 @@ type CoreConfig struct {
EnableUI bool `json:"ui" structs:"ui" mapstructure:"ui"` EnableUI bool `json:"ui" structs:"ui" mapstructure:"ui"`
PluginDirectory string `json:"plugin_directory" structs:"plugin_directory" mapstructure:"plugin_directory"` PluginDirectory string `json:"plugin_directory" structs:"plugin_directory" mapstructure:"plugin_directory"`
VaultBinaryLocation string `json:"vault_binary_location" structs:"vault_binary_location" mapstructure:"vault_binary_location"`
VaultBinarySHA256 []byte `json:"vault_binary_sha256" structs:"vault_binary_sha256" mapstructure:"vault_binary_sha256"`
ReloadFuncs *map[string][]ReloadFunc ReloadFuncs *map[string][]ReloadFunc
ReloadFuncsLock *sync.RWMutex ReloadFuncsLock *sync.RWMutex
@@ -449,8 +441,6 @@ func NewCore(conf *CoreConfig) (*Core, error) {
clusterName: conf.ClusterName, clusterName: conf.ClusterName,
clusterListenerShutdownCh: make(chan struct{}), clusterListenerShutdownCh: make(chan struct{}),
clusterListenerShutdownSuccessCh: make(chan struct{}), clusterListenerShutdownSuccessCh: make(chan struct{}),
vaultBinaryLocation: conf.VaultBinaryLocation,
vaultBinarySHA256: conf.VaultBinarySHA256,
disableMlock: conf.DisableMlock, disableMlock: conf.DisableMlock,
} }

View File

@@ -23,20 +23,16 @@ var (
// to be registered to the catalog before they can be used in backends. Builtin // to be registered to the catalog before they can be used in backends. Builtin
// plugins are automatically detected and included in the catalog. // plugins are automatically detected and included in the catalog.
type PluginCatalog struct { type PluginCatalog struct {
catalogView *BarrierView catalogView *BarrierView
directory string directory string
vaultCommand string
vaultSHA256 []byte
lock sync.RWMutex lock sync.RWMutex
} }
func (c *Core) setupPluginCatalog() error { func (c *Core) setupPluginCatalog() error {
c.pluginCatalog = &PluginCatalog{ c.pluginCatalog = &PluginCatalog{
catalogView: c.systemBarrierView.SubView(pluginCatalogPrefix), catalogView: c.systemBarrierView.SubView(pluginCatalogPrefix),
directory: c.pluginDirectory, directory: c.pluginDirectory,
vaultCommand: c.vaultBinaryLocation,
vaultSHA256: c.vaultBinarySHA256,
} }
return nil return nil
@@ -64,17 +60,15 @@ func (c *PluginCatalog) Get(name string) (*pluginutil.PluginRunner, error) {
} }
// Look for builtin plugins // Look for builtin plugins
if _, ok := builtinplugins.BuiltinPlugins.Get(name); !ok { if factory, ok := builtinplugins.BuiltinPlugins.Get(name); ok {
return nil, fmt.Errorf("no plugin found with name: %s", name) return &pluginutil.PluginRunner{
Name: name,
Builtin: true,
BuiltinFactory: factory,
}, nil
} }
return &pluginutil.PluginRunner{ return nil, fmt.Errorf("no plugin found with name: %s", name)
Name: name,
Command: c.vaultCommand,
Args: []string{"plugin-exec", name},
Sha256: c.vaultSHA256,
Builtin: true,
}, nil
} }
// Set registers a new external plugin with the catalog, or updates an existing // Set registers a new external plugin with the catalog, or updates an existing