Fix plugin reload mounts (#15579)

* fix plugin reload mounts

* do not require sys/ prefix

* update plugin reload docs with examples

* fix unit test credential read path

* update docs to reflect correct cli usage

* allow sys/auth/foo or auth/foo

* append trailing slash if it doesn't exist in request

* add changelog

* use correct changelog number
This commit is contained in:
John-Michael Faircloth 2022-05-25 13:37:42 -05:00 committed by GitHub
parent d4f3fba56e
commit fc04699f57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 84 additions and 19 deletions

3
changelog/15579.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
plugin: Fix a bug where plugin reload would falsely report success in certain scenarios.
```

View File

@ -455,28 +455,62 @@ func TestSystemBackend_Plugin_SealUnseal(t *testing.T) {
}
func TestSystemBackend_Plugin_reload(t *testing.T) {
data := map[string]interface{}{
"plugin": "mock-plugin",
testCases := []struct {
name string
backendType logical.BackendType
data map[string]interface{}
}{
{
name: "test plugin reload for type credential",
backendType: logical.TypeCredential,
data: map[string]interface{}{
"plugin": "mock-plugin",
},
},
{
name: "test mount reload for type credential",
backendType: logical.TypeCredential,
data: map[string]interface{}{
"mounts": "sys/auth/mock-0/,auth/mock-1/",
},
},
{
name: "test plugin reload for type secret",
backendType: logical.TypeLogical,
data: map[string]interface{}{
"plugin": "mock-plugin",
},
},
{
name: "test mount reload for type secret",
backendType: logical.TypeLogical,
data: map[string]interface{}{
"mounts": "mock-0/,mock-1",
},
},
}
t.Run("plugin", func(t *testing.T) { testSystemBackend_PluginReload(t, data) })
data = map[string]interface{}{
"mounts": "mock-0/,mock-1/",
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testSystemBackend_PluginReload(t, tc.data, tc.backendType)
})
}
t.Run("mounts", func(t *testing.T) { testSystemBackend_PluginReload(t, data) })
}
// Helper func to test different reload methods on plugin reload endpoint
func testSystemBackend_PluginReload(t *testing.T, reqData map[string]interface{}) {
cluster := testSystemBackendMock(t, 1, 2, logical.TypeLogical)
func testSystemBackend_PluginReload(t *testing.T, reqData map[string]interface{}, backendType logical.BackendType) {
cluster := testSystemBackendMock(t, 1, 2, backendType)
defer cluster.Cleanup()
core := cluster.Cores[0]
client := core.Client
pathPrefix := "mock-"
if backendType == logical.TypeCredential {
pathPrefix = "auth/" + pathPrefix
}
for i := 0; i < 2; i++ {
// Update internal value in the backend
resp, err := client.Logical().Write(fmt.Sprintf("mock-%d/internal", i), map[string]interface{}{
resp, err := client.Logical().Write(fmt.Sprintf("%s%d/internal", pathPrefix, i), map[string]interface{}{
"value": "baz",
})
if err != nil {
@ -501,7 +535,7 @@ func testSystemBackend_PluginReload(t *testing.T, reqData map[string]interface{}
for i := 0; i < 2; i++ {
// Ensure internal backed value is reset
resp, err := client.Logical().Read(fmt.Sprintf("mock-%d/internal", i))
resp, err := client.Logical().Read(fmt.Sprintf("%s%d/internal", pathPrefix, i))
if err != nil {
t.Fatalf("err: %v", err)
}

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/vault/sdk/logical"
)
// reloadPluginMounts reloads provided mounts, regardless of
// reloadMatchingPluginMounts reloads provided mounts, regardless of
// plugin name, as long as the backend type is plugin.
func (c *Core) reloadMatchingPluginMounts(ctx context.Context, mounts []string) error {
c.mountsLock.RLock()
@ -27,18 +27,28 @@ func (c *Core) reloadMatchingPluginMounts(ctx context.Context, mounts []string)
var errors error
for _, mount := range mounts {
var isAuth bool
// allow any of
// - sys/auth/foo/
// - sys/auth/foo
// - auth/foo/
// - auth/foo
if strings.HasPrefix(mount, credentialRoutePrefix) {
isAuth = true
} else if strings.HasPrefix(mount, systemMountPath+credentialRoutePrefix) {
isAuth = true
mount = strings.TrimPrefix(mount, systemMountPath)
}
if !strings.HasSuffix(mount, "/") {
mount += "/"
}
entry := c.router.MatchingMountEntry(ctx, mount)
if entry == nil {
errors = multierror.Append(errors, fmt.Errorf("cannot fetch mount entry on %q", mount))
continue
}
var isAuth bool
fullPath := c.router.MatchingMount(ctx, mount)
if strings.HasPrefix(fullPath, credentialRoutePrefix) {
isAuth = true
}
// We dont reload mounts that are not in the same namespace
if ns.ID != entry.Namespace().ID {
continue

View File

@ -13,13 +13,31 @@ must be provided, but not both.
## Examples
Reload a plugin:
Reload a plugin by name:
```shell-session
$ vault plugin reload -plugin my-custom-plugin
Success! Reloaded plugin: my-custom-plugin
```
Reload an auth plugin by mount:
```shell-session
$ vault plugin reload \
-mounts auth/my-custom-plugin-1 \
-mounts auth/my-custom-plugin-2
Success! Reloaded mounts: [auth/my-custom-plugin-1/ auth/my-custom-plugin-2/]
```
Reload a secrets plugin by mount:
```shell-session
$ vault plugin reload \
-mounts my-custom-plugin-1 \
-mounts my-custom-plugin-2
Success! Reloaded mounts: [my-custom-plugin-1/ my-custom-plugin-2/]
```
## Usage
The following flags are available in addition to the [standard set of