More Mount Conflict Detection (#2919)
This commit is contained in:
parent
8ccdaa1860
commit
4109473134
|
@ -75,8 +75,8 @@ func (c *Core) enableCredential(entry *MountEntry) error {
|
|||
return fmt.Errorf("token credential backend cannot be instantiated")
|
||||
}
|
||||
|
||||
if match := c.router.MatchingMount(credentialRoutePrefix + entry.Path); match != "" {
|
||||
return logical.CodedError(409, fmt.Sprintf("existing mount at %s", match))
|
||||
if conflict := c.router.MountConflict(credentialRoutePrefix + entry.Path); conflict != "" {
|
||||
return logical.CodedError(409, fmt.Sprintf("existing mount at %s", conflict))
|
||||
}
|
||||
|
||||
// Generate a new UUID and view
|
||||
|
|
|
@ -238,8 +238,8 @@ func (c *Core) mountInternal(entry *MountEntry) error {
|
|||
c.mountsLock.Lock()
|
||||
defer c.mountsLock.Unlock()
|
||||
|
||||
// Verify there is no conflicting mount
|
||||
if match := c.router.MatchingMount(entry.Path); match != "" {
|
||||
// Verify there are no conflicting mounts
|
||||
if match := c.router.MountConflict(entry.Path); match != "" {
|
||||
return logical.CodedError(409, fmt.Sprintf("existing mount at %s", match))
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ type Router struct {
|
|||
mountUUIDCache *radix.Tree
|
||||
mountAccessorCache *radix.Tree
|
||||
tokenStoreSaltFunc func() (*salt.Salt, error)
|
||||
|
||||
// storagePrefix maps the prefix used for storage (ala the BarrierView)
|
||||
// to the backend. This is used to map a key back into the backend that owns it.
|
||||
// For example, logical/uuid1/foobar -> secrets/ (kv backend) + foobar
|
||||
|
@ -231,14 +230,46 @@ func (r *Router) MatchingMountByAccessor(mountAccessor string) *MountEntry {
|
|||
// MatchingMount returns the mount prefix that would be used for a path
|
||||
func (r *Router) MatchingMount(path string) string {
|
||||
r.l.RLock()
|
||||
defer r.l.RUnlock()
|
||||
var mount = r.matchingMountInternal(path)
|
||||
return mount
|
||||
}
|
||||
|
||||
func (r *Router) matchingMountInternal(path string) string {
|
||||
mount, _, ok := r.root.LongestPrefix(path)
|
||||
r.l.RUnlock()
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return mount
|
||||
}
|
||||
|
||||
// matchingPrefixInternal returns a mount prefix that a path may be a part of
|
||||
func (r *Router) matchingPrefixInternal(path string) string {
|
||||
var existing string = ""
|
||||
fn := func(existing_path string, _v interface{}) bool {
|
||||
if strings.HasPrefix(existing_path, path) {
|
||||
existing = existing_path
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
r.root.WalkPrefix(path, fn)
|
||||
return existing
|
||||
}
|
||||
|
||||
// MountConflict determines if there are potential path conflicts
|
||||
func (r *Router) MountConflict(path string) string {
|
||||
r.l.RLock()
|
||||
defer r.l.RUnlock()
|
||||
if exact_match := r.matchingMountInternal(path); exact_match != "" {
|
||||
return exact_match
|
||||
}
|
||||
if prefix_match := r.matchingPrefixInternal(path); prefix_match != "" {
|
||||
return prefix_match
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// MatchingStorageByAPIPath/StoragePath returns the storage used for
|
||||
// API/Storage paths respectively
|
||||
func (r *Router) MatchingStorageByAPIPath(path string) logical.Storage {
|
||||
|
|
|
@ -118,6 +118,11 @@ func TestRouter_Mount(t *testing.T) {
|
|||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
meUUID, err = uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if path := r.MatchingMount("prod/aws/foo"); path != "prod/aws/" {
|
||||
t.Fatalf("bad: %s", path)
|
||||
}
|
||||
|
@ -162,6 +167,25 @@ func TestRouter_Mount(t *testing.T) {
|
|||
if len(n.Paths) != 1 || n.Paths[0] != "foo" {
|
||||
t.Fatalf("bad: %v", n.Paths)
|
||||
}
|
||||
|
||||
subMountEntry := &MountEntry{
|
||||
Path: "prod/",
|
||||
UUID: meUUID,
|
||||
Accessor: "prodaccessor",
|
||||
}
|
||||
|
||||
if r.MountConflict("prod/aws/") == "" {
|
||||
t.Fatalf("bad: prod/aws/")
|
||||
}
|
||||
|
||||
// No error is shown here because MountConflict is checked before Mount
|
||||
err = r.Mount(n, "prod/", subMountEntry, view)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if r.MountConflict("prod/test") == "" {
|
||||
t.Fatalf("bad: prod/test/")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouter_MountCredential(t *testing.T) {
|
||||
|
|
|
@ -59,6 +59,13 @@ Once a secret backend is mounted, you can interact with it directly
|
|||
at its mount point according to its own API. You can use the `vault path-help`
|
||||
system to determine the paths it responds to.
|
||||
|
||||
Note that mount points cannot conflict with each other in Vault. There are
|
||||
two broad implications of this fact. The first is that you cannot have
|
||||
a mount which is prefixed with an existing mount. The second is that you
|
||||
cannot create a mount point that is named as a prefix of an existing mount.
|
||||
As an example, the mounts `foo/bar` and `foo/baz` can peacefully coexist
|
||||
with each other whereas `foo` and `foo/baz` cannot
|
||||
|
||||
## Barrier View
|
||||
|
||||
An important concept around secret backends is that they receive a
|
||||
|
|
Loading…
Reference in New Issue