diff --git a/builtin/logical/database/backend_test.go b/builtin/logical/database/backend_test.go index 2615577fdb..2ece767fcd 100644 --- a/builtin/logical/database/backend_test.go +++ b/builtin/logical/database/backend_test.go @@ -11,10 +11,10 @@ import ( "testing" "github.com/hashicorp/vault/builtin/logical/database/dbplugin" - "github.com/hashicorp/vault/helper/builtinplugins" "github.com/hashicorp/vault/helper/pluginutil" "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/plugins/database/postgresql" "github.com/hashicorp/vault/vault" "github.com/lib/pq" "github.com/mitchellh/mapstructure" @@ -91,8 +91,7 @@ func TestBackend_PluginMain(t *testing.T) { return } - f, _ := builtinplugins.BuiltinPlugins.Get("postgresql-database-plugin") - f() + postgresql.Run() } func TestBackend_config_connection(t *testing.T) { diff --git a/builtin/logical/database/dbplugin/plugin.go b/builtin/logical/database/dbplugin/plugin.go index 61de0fe8ce..9a6691fbab 100644 --- a/builtin/logical/database/dbplugin/plugin.go +++ b/builtin/logical/database/dbplugin/plugin.go @@ -33,15 +33,32 @@ type Statements struct { // object in a logging and metrics middleware. func PluginFactory(pluginName string, sys pluginutil.LookWrapper, logger log.Logger) (DatabaseType, error) { // Look for plugin in the plugin catalog - pluginMeta, err := sys.LookupPlugin(pluginName) + pluginRunner, err := sys.LookupPlugin(pluginName) if err != nil { return nil, err } - // create a DatabasePluginClient instance - db, err := newPluginClient(sys, pluginMeta) - if err != nil { - return nil, err + var db DatabaseType + if pluginRunner.Builtin { + // Plugin is builtin so we can retrieve an instance of the interface + // 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() diff --git a/cli/commands.go b/cli/commands.go index e7545ca906..13f7c8b25a 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -331,11 +331,5 @@ func Commands(metaPtr *meta.Meta) map[string]cli.CommandFactory { Ui: metaPtr.Ui, }, nil }, - - "plugin-exec": func() (cli.Command, error) { - return &command.PluginExec{ - Meta: *metaPtr, - }, nil - }, } } diff --git a/command/plugin_exec.go b/command/plugin_exec.go deleted file mode 100644 index 575be14b7d..0000000000 --- a/command/plugin_exec.go +++ /dev/null @@ -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) -} diff --git a/command/server.go b/command/server.go index 6548aa58ce..ef9e3e3a0c 100644 --- a/command/server.go +++ b/command/server.go @@ -1,10 +1,8 @@ package command import ( - "crypto/sha256" "encoding/base64" "fmt" - "io" "net" "net/http" "net/url" @@ -133,33 +131,6 @@ func (c *ServerCommand) Run(args []string) int { 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 if !dev { switch { @@ -254,23 +225,21 @@ func (c *ServerCommand) Run(args []string) int { } coreConfig := &vault.CoreConfig{ - Physical: backend, - RedirectAddr: config.Storage.RedirectAddr, - HAPhysical: nil, - Seal: seal, - AuditBackends: c.AuditBackends, - CredentialBackends: c.CredentialBackends, - LogicalBackends: c.LogicalBackends, - Logger: c.logger, - DisableCache: config.DisableCache, - DisableMlock: config.DisableMlock, - MaxLeaseTTL: config.MaxLeaseTTL, - DefaultLeaseTTL: config.DefaultLeaseTTL, - ClusterName: config.ClusterName, - CacheSize: config.CacheSize, - PluginDirectory: config.PluginDirectory, - VaultBinaryLocation: ex, - VaultBinarySHA256: sha256Value, + Physical: backend, + RedirectAddr: config.Storage.RedirectAddr, + HAPhysical: nil, + Seal: seal, + AuditBackends: c.AuditBackends, + CredentialBackends: c.CredentialBackends, + LogicalBackends: c.LogicalBackends, + Logger: c.logger, + DisableCache: config.DisableCache, + DisableMlock: config.DisableMlock, + MaxLeaseTTL: config.MaxLeaseTTL, + DefaultLeaseTTL: config.DefaultLeaseTTL, + ClusterName: config.ClusterName, + CacheSize: config.CacheSize, + PluginDirectory: config.PluginDirectory, } if dev { coreConfig.DevToken = devRootTokenID diff --git a/helper/builtinplugins/builtin.go b/helper/builtinplugins/builtin.go index beedbb15b8..9c51ae4789 100644 --- a/helper/builtinplugins/builtin.go +++ b/helper/builtinplugins/builtin.go @@ -5,20 +5,22 @@ import ( "github.com/hashicorp/vault/plugins/database/postgresql" ) +type BuiltinFactory func() (interface{}, error) + var BuiltinPlugins *builtinPlugins = &builtinPlugins{ - plugins: map[string]func() error{ - "mysql-database-plugin": mysql.Run, - "postgresql-database-plugin": postgresql.Run, + plugins: map[string]BuiltinFactory{ + "mysql-database-plugin": mysql.New, + "postgresql-database-plugin": postgresql.New, }, } // 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. 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] return f, ok } diff --git a/helper/pluginutil/runner.go b/helper/pluginutil/runner.go index bbc5ab99b7..95de96a5a8 100644 --- a/helper/pluginutil/runner.go +++ b/helper/pluginutil/runner.go @@ -40,11 +40,12 @@ type LookWrapper interface { // PluginRunner defines the metadata needed to run a plugin securely with // go-plugin. type PluginRunner struct { - Name string `json:"name"` - Command string `json:"command"` - Args []string `json:"args"` - Sha256 []byte `json:"sha256"` - Builtin bool `json:"builtin"` + Name string `json:"name"` + Command string `json:"command"` + Args []string `json:"args"` + Sha256 []byte `json:"sha256"` + Builtin bool `json:"builtin"` + BuiltinFactory func() (interface{}, error) `json:"-"` } // Run takes a wrapper instance, and the go-plugin paramaters and executes a diff --git a/plugins/database/mysql/mysql.go b/plugins/database/mysql/mysql.go index ea14a6782b..e7e2a8aea9 100644 --- a/plugins/database/mysql/mysql.go +++ b/plugins/database/mysql/mysql.go @@ -23,7 +23,7 @@ type MySQL struct { credsutil.CredentialsProducer } -func New() *MySQL { +func New() (interface{}, error) { connProducer := &connutil.SQLConnectionProducer{} connProducer.Type = mySQLTypeName @@ -37,14 +37,17 @@ func New() *MySQL { CredentialsProducer: credsProducer, } - return dbType + return dbType, nil } // Run instantiates a MySQL object, and runs the RPC server for the plugin func Run() error { - dbType := New() + dbType, err := New() + if err != nil { + return err + } - dbplugin.NewPluginServer(dbType) + dbplugin.NewPluginServer(dbType.(*MySQL)) return nil } diff --git a/plugins/database/mysql/mysql_test.go b/plugins/database/mysql/mysql_test.go index 2b1f272918..c86f9c2f6b 100644 --- a/plugins/database/mysql/mysql_test.go +++ b/plugins/database/mysql/mysql_test.go @@ -66,7 +66,8 @@ func TestMySQL_Initialize(t *testing.T) { "connection_url": connURL, } - db := New() + dbRaw, _ := New() + db := dbRaw.(*MySQL) connProducer := db.ConnectionProducer.(*connutil.SQLConnectionProducer) err := db.Initialize(connectionDetails, true) @@ -92,7 +93,8 @@ func TestMySQL_CreateUser(t *testing.T) { "connection_url": connURL, } - db := New() + dbRaw, _ := New() + db := dbRaw.(*MySQL) err := db.Initialize(connectionDetails, true) if err != nil { @@ -127,7 +129,8 @@ func TestMySQL_RevokeUser(t *testing.T) { "connection_url": connURL, } - db := New() + dbRaw, _ := New() + db := dbRaw.(*MySQL) err := db.Initialize(connectionDetails, true) if err != nil { diff --git a/plugins/database/postgresql/postgresql.go b/plugins/database/postgresql/postgresql.go index b8449f5498..5781b6c3d1 100644 --- a/plugins/database/postgresql/postgresql.go +++ b/plugins/database/postgresql/postgresql.go @@ -16,7 +16,8 @@ import ( const postgreSQLTypeName string = "postgres" -func New() *PostgreSQL { +// New implements builtinplugins.BuiltinFactory +func New() (interface{}, error) { connProducer := &connutil.SQLConnectionProducer{} connProducer.Type = postgreSQLTypeName @@ -30,14 +31,17 @@ func New() *PostgreSQL { CredentialsProducer: credsProducer, } - return dbType + return dbType, nil } // Run instatiates a PostgreSQL object, and runs the RPC server for the plugin func Run() error { - dbType := New() + dbType, err := New() + if err != nil { + return err + } - dbplugin.NewPluginServer(dbType) + dbplugin.NewPluginServer(dbType.(*PostgreSQL)) return nil } diff --git a/plugins/database/postgresql/postgresql_test.go b/plugins/database/postgresql/postgresql_test.go index c7ccc8ee8f..79391dc56e 100644 --- a/plugins/database/postgresql/postgresql_test.go +++ b/plugins/database/postgresql/postgresql_test.go @@ -66,7 +66,9 @@ func TestPostgreSQL_Initialize(t *testing.T) { "connection_url": connURL, } - db := New() + dbRaw, _ := New() + db := dbRaw.(*PostgreSQL) + connProducer := db.ConnectionProducer.(*connutil.SQLConnectionProducer) err := db.Initialize(connectionDetails, true) @@ -92,7 +94,8 @@ func TestPostgreSQL_CreateUser(t *testing.T) { "connection_url": connURL, } - db := New() + dbRaw, _ := New() + db := dbRaw.(*PostgreSQL) err := db.Initialize(connectionDetails, true) if err != nil { t.Fatalf("err: %s", err) @@ -136,7 +139,8 @@ func TestPostgreSQL_RenewUser(t *testing.T) { "connection_url": connURL, } - db := New() + dbRaw, _ := New() + db := dbRaw.(*PostgreSQL) err := db.Initialize(connectionDetails, true) if err != nil { t.Fatalf("err: %s", err) @@ -176,7 +180,8 @@ func TestPostgreSQL_RevokeUser(t *testing.T) { "connection_url": connURL, } - db := New() + dbRaw, _ := New() + db := dbRaw.(*PostgreSQL) err := db.Initialize(connectionDetails, true) if err != nil { t.Fatalf("err: %s", err) diff --git a/vault/core.go b/vault/core.go index ef99741bfd..01ab49f752 100644 --- a/vault/core.go +++ b/vault/core.go @@ -335,12 +335,6 @@ type Core struct { // pluginDirectory is the location vault will look for plugin binaries 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 *PluginCatalog @@ -389,9 +383,7 @@ type CoreConfig struct { EnableUI bool `json:"ui" structs:"ui" mapstructure:"ui"` - 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"` + PluginDirectory string `json:"plugin_directory" structs:"plugin_directory" mapstructure:"plugin_directory"` ReloadFuncs *map[string][]ReloadFunc ReloadFuncsLock *sync.RWMutex @@ -449,8 +441,6 @@ func NewCore(conf *CoreConfig) (*Core, error) { clusterName: conf.ClusterName, clusterListenerShutdownCh: make(chan struct{}), clusterListenerShutdownSuccessCh: make(chan struct{}), - vaultBinaryLocation: conf.VaultBinaryLocation, - vaultBinarySHA256: conf.VaultBinarySHA256, disableMlock: conf.DisableMlock, } diff --git a/vault/plugin_catalog.go b/vault/plugin_catalog.go index b89224780c..598a16fac5 100644 --- a/vault/plugin_catalog.go +++ b/vault/plugin_catalog.go @@ -23,20 +23,16 @@ var ( // to be registered to the catalog before they can be used in backends. Builtin // plugins are automatically detected and included in the catalog. type PluginCatalog struct { - catalogView *BarrierView - directory string - vaultCommand string - vaultSHA256 []byte + catalogView *BarrierView + directory string lock sync.RWMutex } func (c *Core) setupPluginCatalog() error { c.pluginCatalog = &PluginCatalog{ - catalogView: c.systemBarrierView.SubView(pluginCatalogPrefix), - directory: c.pluginDirectory, - vaultCommand: c.vaultBinaryLocation, - vaultSHA256: c.vaultBinarySHA256, + catalogView: c.systemBarrierView.SubView(pluginCatalogPrefix), + directory: c.pluginDirectory, } return nil @@ -64,17 +60,15 @@ func (c *PluginCatalog) Get(name string) (*pluginutil.PluginRunner, error) { } // Look for builtin plugins - if _, ok := builtinplugins.BuiltinPlugins.Get(name); !ok { - return nil, fmt.Errorf("no plugin found with name: %s", name) + if factory, ok := builtinplugins.BuiltinPlugins.Get(name); ok { + return &pluginutil.PluginRunner{ + Name: name, + Builtin: true, + BuiltinFactory: factory, + }, nil } - return &pluginutil.PluginRunner{ - Name: name, - Command: c.vaultCommand, - Args: []string{"plugin-exec", name}, - Sha256: c.vaultSHA256, - Builtin: true, - }, nil + return nil, fmt.Errorf("no plugin found with name: %s", name) } // Set registers a new external plugin with the catalog, or updates an existing