Skip to content

Commit

Permalink
Add plugin version to GRPC interface (#17088)
Browse files Browse the repository at this point in the history
Add plugin version to GRPC interface

Added a version interface in the sdk/logical so that it can be shared between all plugin types, and then wired it up to RunningVersion in the mounts, auth list, and database systems.

I've tested that this works with auth, database, and secrets plugin types, with the following logic to populate RunningVersion:

If a plugin has a PluginVersion() method implemented, then that is used
If not, and the plugin is built into the Vault binary, then the go.mod version is used
Otherwise, the it will be the empty string.
My apologies for the length of this PR.

* Placeholder backend should be external

We use a placeholder backend (previously a framework.Backend) before a
GRPC plugin is lazy-loaded. This makes us later think the plugin is a
builtin plugin.

So we added a `placeholderBackend` type that overrides the
`IsExternal()` method so that later we know that the plugin is external,
and don't give it a default builtin version.
  • Loading branch information
Christopher Swenson authored Sep 15, 2022
1 parent b4e9ee8 commit 70278c2
Show file tree
Hide file tree
Showing 40 changed files with 954 additions and 178 deletions.
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ EXTERNAL_TOOLS_CI=\
EXTERNAL_TOOLS=\
github.com/client9/misspell/cmd/misspell
GOFMT_FILES?=$$(find . -name '*.go' | grep -v pb.go | grep -v vendor)
SED?=$(shell command -v gsed || command -v sed)


GO_VERSION_MIN=1.19.1
Expand Down Expand Up @@ -190,8 +191,8 @@ proto: bootstrap
# No additional sed expressions should be added to this list. Going forward
# we should just use the variable names choosen by protobuf. These are left
# here for backwards compatability, namely for SDK compilation.
sed -i -e 's/Id/ID/' vault/request_forwarding_service.pb.go
sed -i -e 's/Idp/IDP/' -e 's/Url/URL/' -e 's/Id/ID/' -e 's/IDentity/Identity/' -e 's/EntityId/EntityID/' -e 's/Api/API/' -e 's/Qr/QR/' -e 's/Totp/TOTP/' -e 's/Mfa/MFA/' -e 's/Pingid/PingID/' -e 's/namespaceId/namespaceID/' -e 's/Ttl/TTL/' -e 's/BoundCidrs/BoundCIDRs/' helper/identity/types.pb.go helper/identity/mfa/types.pb.go helper/storagepacker/types.pb.go sdk/plugin/pb/backend.pb.go sdk/logical/identity.pb.go vault/activity/activity_log.pb.go
$(SED) -i -e 's/Id/ID/' vault/request_forwarding_service.pb.go
$(SED) -i -e 's/Idp/IDP/' -e 's/Url/URL/' -e 's/Id/ID/' -e 's/IDentity/Identity/' -e 's/EntityId/EntityID/' -e 's/Api/API/' -e 's/Qr/QR/' -e 's/Totp/TOTP/' -e 's/Mfa/MFA/' -e 's/Pingid/PingID/' -e 's/namespaceId/namespaceID/' -e 's/Ttl/TTL/' -e 's/BoundCidrs/BoundCIDRs/' helper/identity/types.pb.go helper/identity/mfa/types.pb.go helper/storagepacker/types.pb.go sdk/plugin/pb/backend.pb.go sdk/logical/identity.pb.go vault/activity/activity_log.pb.go

# This will inject the sentinel struct tags as decorated in the proto files.
protoc-go-inject-tag -input=./helper/identity/types.pb.go
Expand Down
18 changes: 18 additions & 0 deletions builtin/logical/database/version_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
v4 "github.com/hashicorp/vault/sdk/database/dbplugin"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
"github.com/hashicorp/vault/sdk/logical"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
Expand All @@ -18,6 +19,8 @@ type databaseVersionWrapper struct {
v5 v5.Database
}

var _ logical.PluginVersioner = databaseVersionWrapper{}

// newDatabaseWrapper figures out which version of the database the pluginName is referring to and returns a wrapper object
// that can be used to make operations on the underlying database plugin.
func newDatabaseWrapper(ctx context.Context, pluginName string, pluginVersion string, sys pluginutil.LookRunnerUtil, logger log.Logger) (dbw databaseVersionWrapper, err error) {
Expand Down Expand Up @@ -227,6 +230,21 @@ func (d databaseVersionWrapper) Close() error {
return d.v4.Close()
}

func (d databaseVersionWrapper) PluginVersion() logical.PluginVersion {
// v5 Database
if d.isV5() {
if versioner, ok := d.v5.(logical.PluginVersioner); ok {
return versioner.PluginVersion()
}
}

// v4 Database
if versioner, ok := d.v4.(logical.PluginVersioner); ok {
return versioner.PluginVersion()
}
return logical.EmptyPluginVersion
}

func (d databaseVersionWrapper) isV5() bool {
return d.v5 != nil
}
Expand Down
59 changes: 54 additions & 5 deletions builtin/plugin/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
log "github.com/hashicorp/go-hclog"

"github.com/hashicorp/go-multierror"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/go-uuid"
v5 "github.com/hashicorp/vault/builtin/plugin/v5"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/consts"
Expand Down Expand Up @@ -79,22 +79,52 @@ func Backend(ctx context.Context, conf *logical.BackendConfig) (*PluginBackend,
// Get SpecialPaths and BackendType
paths := raw.SpecialPaths()
btype := raw.Type()
runningVersion := ""
if versioner, ok := raw.(logical.PluginVersioner); ok {
runningVersion = versioner.PluginVersion().Version
}

external := false
if externaler, ok := raw.(logical.Externaler); ok {
external = externaler.IsExternal()
}

// Cleanup meta plugin backend
raw.Cleanup(ctx)

// Initialize b.Backend with dummy backend since plugin
// Initialize b.Backend with placeholder backend since plugin
// backends will need to be lazy loaded.
b.Backend = &framework.Backend{
PathsSpecial: paths,
BackendType: btype,
b.Backend = &placeholderBackend{
Backend: framework.Backend{
PathsSpecial: paths,
BackendType: btype,
RunningVersion: runningVersion,
},
external: external,
}

b.config = conf

return &b, nil
}

// placeholderBackend is used a placeholder before a backend is lazy-loaded.
// It is mostly used to mark that the backend is an external backend.
type placeholderBackend struct {
framework.Backend

external bool
}

func (p *placeholderBackend) IsExternal() bool {
return p.external
}

var (
_ logical.Externaler = (*placeholderBackend)(nil)
_ logical.PluginVersioner = (*placeholderBackend)(nil)
)

// PluginBackend is a thin wrapper around plugin.BackendPluginClient
type PluginBackend struct {
Backend logical.Backend
Expand Down Expand Up @@ -286,3 +316,22 @@ func (b *PluginBackend) Type() logical.BackendType {
defer b.RUnlock()
return b.Backend.Type()
}

func (b *PluginBackend) PluginVersion() logical.PluginVersion {
if versioner, ok := b.Backend.(logical.PluginVersioner); ok {
return versioner.PluginVersion()
}
return logical.EmptyPluginVersion
}

func (b *PluginBackend) IsExternal() bool {
if externaler, ok := b.Backend.(logical.Externaler); ok {
return externaler.IsExternal()
}
return false
}

var (
_ logical.PluginVersioner = (*PluginBackend)(nil)
_ logical.Externaler = (*PluginBackend)(nil)
)
12 changes: 10 additions & 2 deletions builtin/plugin/v5/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"net/rpc"
"sync"

uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/plugin"
Expand Down Expand Up @@ -35,7 +35,7 @@ func Backend(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,
return &b, nil
}

// backend is a thin wrapper around plugin.BackendPluginClientV5
// backend is a thin wrapper around a builtin plugin or a plugin.BackendPluginClientV5
type backend struct {
logical.Backend
mu sync.RWMutex
Expand Down Expand Up @@ -147,3 +147,11 @@ func (b *backend) InvalidateKey(ctx context.Context, key string) {
defer b.mu.RUnlock()
b.Backend.InvalidateKey(ctx, key)
}

func (b *backend) IsExternal() bool {
switch b.Backend.(type) {
case *plugin.BackendPluginClientV5:
return true
}
return false
}
3 changes: 3 additions & 0 deletions changelog/17088.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
plugins: Adding version to plugin GRPC interface
```
54 changes: 54 additions & 0 deletions helper/versions/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package versions

import (
"fmt"
"runtime/debug"
"strings"
"sync"

"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/version"
)

var (
buildInfoOnce sync.Once // once is used to ensure we only parse build info once.
buildInfo *debug.BuildInfo
DefaultBuiltinVersion = "v" + version.GetVersion().Version + "+builtin.vault"
)

func GetBuiltinVersion(pluginType consts.PluginType, pluginName string) string {
buildInfoOnce.Do(func() {
buildInfo, _ = debug.ReadBuildInfo()
})

// Should never happen, means the binary was built without Go modules.
// Fall back to just the Vault version.
if buildInfo == nil {
return DefaultBuiltinVersion
}

// Vault builtin plugins are all either:
// a) An external repo within the hashicorp org - return external repo version with +builtin
// b) Within the Vault repo itself - return Vault version with +builtin.vault
//
// The repo names are predictable, but follow slightly different patterns
// for each plugin type.
t := pluginType.String()
switch pluginType {
case consts.PluginTypeDatabase:
// Database plugin built-ins are registered as e.g. "postgresql-database-plugin"
pluginName = strings.TrimSuffix(pluginName, "-database-plugin")
case consts.PluginTypeSecrets:
// Repos use "secrets", pluginType.String() is "secret".
t = "secrets"
}
pluginModulePath := fmt.Sprintf("github.com/hashicorp/vault-plugin-%s-%s", t, pluginName)

for _, dep := range buildInfo.Deps {
if dep.Path == pluginModulePath {
return dep.Version + "+builtin"
}
}

return DefaultBuiltinVersion
}
17 changes: 9 additions & 8 deletions http/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/go-test/deep"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/helper/versions"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault"
Expand Down Expand Up @@ -415,7 +416,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
"version": "",
},
"sys/": map[string]interface{}{
Expand All @@ -433,7 +434,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.DefaultBuiltinVersion,
"version": "",
},
"cubbyhole/": map[string]interface{}{
Expand All @@ -450,7 +451,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
"version": "",
},
"identity/": map[string]interface{}{
Expand All @@ -468,7 +469,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
"version": "",
},
},
Expand All @@ -486,7 +487,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"options": map[string]interface{}{"version": "1"},
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
"version": "",
},
"sys/": map[string]interface{}{
Expand All @@ -504,7 +505,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.DefaultBuiltinVersion,
"version": "",
},
"cubbyhole/": map[string]interface{}{
Expand All @@ -521,7 +522,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
"version": "",
},
"identity/": map[string]interface{}{
Expand All @@ -539,7 +540,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"options": interface{}(nil),
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
"version": "",
},
}
Expand Down
10 changes: 6 additions & 4 deletions http/sys_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"time"

"github.com/go-test/deep"
"github.com/hashicorp/vault/helper/versions"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
)

Expand Down Expand Up @@ -128,7 +130,7 @@ func TestSysEnableAuth(t *testing.T) {
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeCredential, "approle"),
"version": "",
},
"token/": map[string]interface{}{
Expand Down Expand Up @@ -166,7 +168,7 @@ func TestSysEnableAuth(t *testing.T) {
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeCredential, "approle"),
"version": "",
},
"token/": map[string]interface{}{
Expand Down Expand Up @@ -531,7 +533,7 @@ func TestSysRemountAuth(t *testing.T) {
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
"version": "",
},
"token/": map[string]interface{}{
Expand Down Expand Up @@ -568,7 +570,7 @@ func TestSysRemountAuth(t *testing.T) {
"options": map[string]interface{}{},
"sha": "",
"running_sha": "",
"running_version": "",
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
"version": "",
},
"token/": map[string]interface{}{
Expand Down
Loading

0 comments on commit 70278c2

Please sign in to comment.