diff --git a/.gitignore b/.gitignore index 359c01c..aa5b2fd 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ build/ # fake google application default credentials for testing fake-creds.json + +# IDE +.vscode diff --git a/README.md b/README.md index 08e51ff..3046bb5 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ The following plugin arguments are supported: * `-db-type`, defaults to `cloudsql-postgres`. This is currently the only supported database type. * `-log-level`, defaults to `info` +* `multiplex`, defaults to `true` ## Getting Started diff --git a/cloudsql.go b/cloudsql.go index df4a346..547de73 100644 --- a/cloudsql.go +++ b/cloudsql.go @@ -110,6 +110,7 @@ func newPostgresDatabase(dbType DBType, connProducer *connutil.SQLConnectionProd // // attribute 'sslmode=disable' is required. even though the sslmode parameter is set to disable, // the Cloud SQL Auth proxy does provide an encrypted connection. + // See: https://cloud.google.com/sql/docs/postgres/connect-admin-proxy#connect-to-proxy cleanup, err := pgxv4.RegisterDriver(dbType.String(), cloudsqlconn.WithIAMAuthN()) if err != nil { return nil, nil, nil, errors.Wrap(err, "failed to register 'postgres' driver with 'cloud-sql-go-connector'") diff --git a/cmd/vault-plugin-database-cloudsql/serve.go b/cmd/vault-plugin-database-cloudsql/serve.go index e32a12e..06c453e 100644 --- a/cmd/vault-plugin-database-cloudsql/serve.go +++ b/cmd/vault-plugin-database-cloudsql/serve.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/go-plugin" "github.com/hashicorp/vault/api" dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5" + "github.com/pkg/errors" ) // Serve launches the plugin @@ -22,8 +23,10 @@ func Serve(ctx context.Context, testServerChan chan *plugin.ReattachConfig) { flags := apiClientMeta.FlagSet() var flagDBType string var flagLogLevel string + var flagMultiplex bool flags.StringVar(&flagDBType, "db-type", cloudsql.Postgres.String(), "can be: 'cloudsql-postgres'") flags.StringVar(&flagLogLevel, "log-level", "info", "can be: 'trace', 'debug', 'info', 'warn', 'error', 'off'") + flags.BoolVar(&flagMultiplex, "multiplex", true, "Whether to enable plugin multiplexing. can be: 'true' or 'false'") err := flags.Parse(os.Args[1:]) if err != nil { fmt.Printf("unable to parse plugin arguments: %s", err) @@ -35,20 +38,22 @@ func Serve(ctx context.Context, testServerChan chan *plugin.ReattachConfig) { Level: hclog.LevelFromString(flagLogLevel), }) - // initialize "cloudsql" database plugin - cloudsqlDatabase, err := cloudsql.New(cloudsql.DBType(flagDBType)) + // provide factory to initialize a "cloudsql" database plugin + pluginFactory := func() (interface{}, error) { + cloudsqlDatabase, err := cloudsql.New(cloudsql.DBType(flagDBType)) + if err != nil { + return nil, errors.Wrap(err, "failed to get new instance of cloudsql database plugin") + } + return cloudsqlDatabase, nil + } + + serveConfig, err := initServeConfig(flagMultiplex, pluginFactory, logger) if err != nil { - logger.Error("failed to initialize cloudsql database plugin. aborting now.", err) + logger.Error("failed to initialize database plugin. aborting now.", err) os.Exit(1) } - - // Vault communicates to plugins over RPC - // start RPC server to which vault will connect to - // See: https://www.vaultproject.io/docs/secrets/databases/custom#serving-a-plugin - // TODO determine whether ServeMultiplex is required - // See: https://www.vaultproject.io/docs/plugins/plugin-architecture#plugin-multiplexing - serveConfig := dbplugin.ServeConfig(cloudsqlDatabase.(dbplugin.Database)) if testServerChan != nil { + // if running in test mode, use channel to pry into the plugin's lifecycle serveConfig.Test = &plugin.ServeTestConfig{ Context: ctx, ReattachConfigCh: testServerChan, @@ -58,6 +63,27 @@ func Serve(ctx context.Context, testServerChan chan *plugin.ReattachConfig) { } else { serveConfig.Logger = logger } - + // Vault communicates to plugins over RPC + // start RPC server to which vault will connect to + // See: https://www.vaultproject.io/docs/secrets/databases/custom#serving-a-plugin plugin.Serve(serveConfig) } + +func initServeConfig(multiplex bool, pluginFactory dbplugin.Factory, logger hclog.Logger) (*plugin.ServeConfig, error) { + logger.Debug("initializing cloudsql plugin with multiplexing=%t", multiplex) + var serveConfig *plugin.ServeConfig + if multiplex { + // See: https://www.vaultproject.io/docs/plugins/plugin-architecture#plugin-multiplexing + serveConfig = dbplugin.ServeConfigMultiplex(pluginFactory) + } else { + dbPlugin, err := pluginFactory() + if err != nil { + return nil, errors.Wrap(err, "failed to create new plugin instance") + } + serveConfig = dbplugin.ServeConfig(dbPlugin.(dbplugin.Database)) + } + if serveConfig == nil { + return nil, errors.New("failed to initialize server config for plugin") + } + return serveConfig, nil +} diff --git a/cmd/vault-plugin-database-cloudsql/serve_test.go b/cmd/vault-plugin-database-cloudsql/serve_test.go index 648a814..192a30e 100644 --- a/cmd/vault-plugin-database-cloudsql/serve_test.go +++ b/cmd/vault-plugin-database-cloudsql/serve_test.go @@ -12,7 +12,7 @@ import ( ) func TestServe(t *testing.T) { - os.Args = []string{"my-plugin"} + os.Args = []string{"my-plugin", "-multiplex=true"} ctx := context.Background() reattachConfigCh := make(chan *plugin.ReattachConfig)