Plugin Development
Advanced topic! Plugin development is a highly advanced topic in OpenBao, and is not required knowledge for day-to-day usage. If you don't plan on writing any plugins, we recommend not reading this section of the documentation.
Because OpenBao communicates to plugins over a gRPC interface, you can build and distribute a plugin for OpenBao without having to rebuild OpenBao itself. This makes it easy for you to build an OpenBao plugin for your organization's internal use, for a proprietary API that you don't want to open source, or to prototype something before contributing it back to the main project.
Developing a plugin is simple. The only knowledge necessary to write a plugin is basic command-line skills and basic knowledge of the Go programming language.
Your plugin implementation needs to satisfy the interface for the plugin type you want to build. You can find these definitions in the docs for the backend running the plugin.
Plugins should be prepared to handle multiple concurrent requests from OpenBao.
Serving a plugin
Serving a plugin requires calling into the respective plugin entrypoint function exposed by the appropriate plugin server SDK for type of plugin developed:
- Auth method and secrets engine plugins: Use
ServeMultiplexfromgithub.com/openbao/openbao/sdk/v2/plugin. - Database provider plugins: Use
ServeMultiplexfromgithub.com/openbao/openbao/sdk/v2/database/dbplugin. - KMS provider plugins: Use
Servefromgithub.com/openbao/go-kms-wrapping/plugin/v2.
Plenty of real-world plugin examples are available in the official
openbao-plugins plugin
collection.
The following code exhibits an example main package for an OpenBao plugin using the OpenBao SDK for a secrets engine or auth method:
package main
import (
"os"
myPlugin "your/plugin/import/path"
"github.com/openbao/openbao/api/v2"
"github.com/openbao/openbao/sdk/v2/plugin"
)
func main() {
apiClientMeta := &api.PluginAPIClientMeta{}
flags := apiClientMeta.FlagSet()
flags.Parse(os.Args[1:])
tlsConfig := apiClientMeta.GetTLSConfig()
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
err := plugin.ServeMultiplex(&plugin.ServeOpts{
BackendFactoryFunc: myPlugin.Factory,
TLSProviderFunc: tlsProviderFunc,
})
if err != nil {
logger := hclog.New(&hclog.LoggerOptions{})
logger.Error("plugin shutting down", "error", err)
os.Exit(1)
}
}
And that's basically it! You would just need to change myPlugin to your actual
plugin.
Plugin backwards compatibility with OpenBao
Let's take a closer look at a snippet from the above main package.
err := plugin.ServeMultiplex(&plugin.ServeOpts{
BackendFactoryFunc: myPlugin.Factory,
TLSProviderFunc: tlsProviderFunc,
})
The call to plugin.ServeMultiplex ensures that the plugin will use
OpenBao's plugin multiplexing
feature. However, this plugin will not be multiplexed if it is run by a
version of OpenBao that does not support multiplexing. OpenBao will simply
fall back to a plugin version that it can run. Additionally, we set the
TLSProviderFunc to ensure that our plugin is backwards compatible with
versions of OpenBao that do not support automatic mutual TLS for secure plugin
communication. If you are certain
your plugin does not need backwards compatibility, this field can be omitted.
Leveraging plugin versioning
Plugins can optionally self-report their own semantic version. For plugins that
do so, OpenBao will automatically populate the plugin's version in the catalog
without requiring the user to provide it. If users do provide a version during
registration, OpenBao will error if the version provided does not match what the
plugin reports. Plugins that report a non-empty version must report a valid
Semantic Version with a leading 'v' added or registration
will fail, e.g. v1.0.0 or v2.3.2-beta.
Plugins that want to opt into this behavior can implement the version interface. However, it is not a prerequisite; users can still provide a version during registration if the plugin does not implement the version interface.
Auth and secrets plugins based on framework.Backend from the SDK should set the
RunningVersion
variable, and the framework will implement the version interface.
Database plugins have a smaller API than framework.Backend exposes, and should
instead implement the
PluginVersioner
interface directly.
Building a plugin from source
To build a plugin from source, first navigate to the location holding the
desired plugin version. Next, run go build to obtain a new binary for the
plugin. Finally, register the
plugin and enable it.