Vault Plugin New __hot__ -
Recent Vault versions (v1.21.3 and later) have improved the UI's support for recognizing and supporting HashiCorp-built external plugins, treating them with the same first-class support as built-in plugins.
# config.hcl plugin_directory = "/home/user/.vault/plugins"
This command compiles the plugin and places the binary in the vault/plugins/ directory. vault plugin new
This guide was written for Vault 1.15+. Check the official documentation for updates.
Then, copy your built plugin binary ( my-custom-vault-plugin ) into this directory. Recent Vault versions (v1
err := plugin.ServeMultiplex(&plugin.ServeOpts BackendFactoryFunc: myPlugin.Factory, TLSProviderFunc: tlsProviderFunc, )
vault write my-plugin/data/test value="Hello World" vault read my-plugin/data/test Check the official documentation for updates
package main import ( "os" "://github.com" "://github.com" ) func main() { logger := hclog.New(&hclog.LoggerOptions Name: "vault-plugin-secrets-custom", Level: hclog.Trace, ) apiClientMeta := &plugin.APIClientMeta{} flags := apiClientMeta.FlagSet() if err := flags.Parse(os.Args[1:]); err != nil logger.Error("failed to parse flags", "error", err) os.Exit(1) tlsConfig := apiClientMeta.GetTLSConfig() tlsProviderFunc := plugin.NewTLSConfigProvider(tlsConfig) err := plugin.Serve(&plugin.ServeOpts BackendFactoryFunc: Factory, TLSProviderFunc: tlsProviderFunc, ) if err != nil logger.Error("plugin shutting down", "error", err) os.Exit(1) } Use code with caution. backend.go : Defining the Backend Factory
command that allows operators to stage a new binary alongside the live one. How it works Traffic Shadowing
package main import ( "context" "://github.com" "://github.com" ) func pathSecrets(b *backend) *framework.Path return &framework.Path Pattern: "kv/" + framework.GenericNameRegex("name"), Fields: map[string]*framework.FieldSchema "name": Type: framework.TypeString, Description: "The key name for the secret.", , "value": Type: framework.TypeString, Description: "The sensitive value to store.", , , Operations: map[logical.Operation]framework.OperationHandler logical.UpdateOperation: &framework.PathOperationCallback: b.pathSecretWrite, logical.ReadOperation: &framework.PathOperationCallback: b.pathSecretRead, , func b.pathSecretWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) name := data.Get("name").(string) value := data.Get("value").(string) // Store data securely in Vault's encrypted storage barrier entry, err := logical.StorageEntryFromString("secret/"+name, value) if err != nil return nil, err if err := req.Storage.Put(ctx, entry); err != nil return nil, err return nil, nil func b.pathSecretRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { name := data.Get("name").(string) entry, err := req.Storage.Get(ctx, "secret/"+name) if err != nil return nil, err if entry == nil return nil, nil return &logical.Response{ Data: map[string]interface{} "value": string(entry.Value), , }, nil } Use code with caution. 4. Setting up the Main Entrypoint
We will build a new mock secrets engine called vault-plugin-secrets-custom . This plugin will expose a read/write path for static configuration and a dynamic path that returns a generated API token accompanied by a custom TTL. Project Structure Organize your workspace using the standard Go layout:

