Skip to content

Commit

Permalink
Fix plugin reload mounts (#15579)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
fairclothjm committed May 25, 2022
1 parent 9fd8a97 commit 6d2a218
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 19 deletions.
3 changes: 3 additions & 0 deletions changelog/15579.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
plugin: Fix a bug where plugin reload would falsely report success in certain scenarios.
```
56 changes: 45 additions & 11 deletions vault/logical_system_integ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
}
Expand Down
24 changes: 17 additions & 7 deletions vault/plugin_reload.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down
20 changes: 19 additions & 1 deletion website/content/docs/commands/plugin/reload.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 6d2a218

Please sign in to comment.