-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More Mount Conflict Detection #2919
Conversation
Ensure that we are not creating a "submount" underneath an already existing mountpoint. This (hopefully) fixes hashicorp#2878
I'm missing something about the intersection between the router and logical backends... Doing some (manual) smoke testing, I noticed this repeating in the logs...
|
The issue is that what's in the router's The right thing to do would probably be to take the logic you wrote and the check above for a longest prefix and combine it into two functions inside the router, something like AllowedMount and allowedMountInternal. The latter takes that logic and returns an error or not, and can be used inside the router's Mount function. The former does the same thing but gets a read lock (see MatchingMount); this can be called from mount.go and auth.go instead of the existing MatchingMount call. |
Thanks for the guidance, those changes cleared up the rollback error. Any guidance on why (local) postgres tests are (consistently) failing? Basic test suite (
|
vault/auth.go
Outdated
@@ -73,7 +73,7 @@ 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 != "" { | |||
if match := c.router.MountConflict(credentialRoutePrefix + entry.Path); match != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For ease of understanding, can you change match
here to conflict
and in the error message say "conflicting mount at ..."?
vault/router.go
Outdated
// with an existing mountpoint | ||
if prefix != "" { | ||
if match := r.MatchingPrefix(prefix); match != "" { | ||
return fmt.Errorf("cannot mount under existing mount '%s'", match) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this checking for mounting over an existing mount?
vault/router.go
Outdated
func (r *Router) MatchingPrefix(path string) string { | ||
var existing string = "" | ||
fn := func(existing_path string, _v interface{}) bool { | ||
fmt.Println(fmt.Sprintf("comparing %v %v", path, existing_path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing this was a debug line you meant to take out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How embarrassing 😬
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly it was like, um, that's going to spam logs a LOT :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which is why I'm feeling a touch embarrassed 👼
New commit incorporates the feedback and I added a little blurb to the docs about mount conflicts.
vault/router.go
Outdated
@@ -174,6 +181,31 @@ func (r *Router) MatchingMount(path string) string { | |||
return mount | |||
} | |||
|
|||
//MatchingPrefix returns a mount prefix that a path may be a part of | |||
func (r *Router) MatchingPrefix(path string) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function doesn't get a read lock but should, as it can be called externally (via MountConflict from the mount code). However, it is called with a lock already held from the router's Mount function.
The way to deal with this would be to have MatchingPrefix grab a read lock, then call matchingPrefixInternal which has the actual logic. The router's Mount function would then just call matchingPrefixInternal directly.
@@ -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 point cannot conflict with each other in Vault. There are |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/point/points
vault/router.go
Outdated
//MatchingPrefix returns a mount prefix that a path may be a part of | ||
func (r *Router) MatchingPrefix(path string) string { | ||
var existing string = "" | ||
fn := func(existing_path string, _v interface{}) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this function executes at all wouldn't it mean that we've found a prefix? Is the if
check unnecessary here?
Added the read lock and fixed a typo but wasn't clear on the third statement. The |
Just noticed tests failed (they are passing locally). Will take a look this afternoon/evening... Unless someone can restart the build 👼 I see the build is passing now, but I'm seeing the following error consistently locally...
|
vault/router.go
Outdated
@@ -62,6 +62,13 @@ func (r *Router) Mount(backend logical.Backend, prefix string, mountEntry *Mount | |||
if existing, _, ok := r.root.LongestPrefix(prefix); ok && existing != "" { | |||
return fmt.Errorf("cannot mount under existing mount '%s'", existing) | |||
} | |||
// If this is a secret backend, check to see if the prefix conflicts | |||
// with an existing mountpoint | |||
if prefix != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure that we don't allow empty prefixes in the API calls or core mount function. Have you seen this? This if
seems unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this check appears unnecessary.
vault/router.go
Outdated
|
||
// MountConflict determines if there are potential path conflicts | ||
func (r *Router) MountConflict(path string) string { | ||
if exact_match := r.MatchingMount(path); exact_match != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really this function should grab a single read lock for the entire function and then call matchingMountInternal and matchingPrefixInternal. That way nothing can change between the two functions.
Regarding the |
Shuffled around the "internal" functions. When I removed the prefix matching code in
|
@otakup0pe Your build works fine -- can you explain what you mean about "when I removed the prefix matching code in Mount"? |
@otakup0pe Just wanted to check on on this and see if you were going to address the remaining review items. |
Hi @jefferai I was off grid for several weeks. Will pick wrap this up now that I'm back! |
Changed the tests around to better capture this now does. Existing paths paths are still supported from the perspective of the router, but not from the perspective of new mounts.
I think this addresses the feedback. I adjusted the tests to account for the new pattern. |
vault/router_test.go
Outdated
t.Fatalf("bad: prod/aws/") | ||
} | ||
|
||
err = r.Mount(n, "prod/", subMountEntry, view) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a comment here as to why this should pass?
@@ -118,6 +118,11 @@ func TestRouter_Mount(t *testing.T) { | |||
t.Fatalf("err: %v", err) | |||
} | |||
|
|||
meUUID, err = uuid.GenerateUUID() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move this to be placed just above the definition of subMountEntry?
vault/router.go
Outdated
@@ -201,14 +200,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() | |||
mount, _, ok := r.root.LongestPrefix(path) | |||
var mount = r.matchingMountInternal(path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Nitpick][Optional] We could do a defer r.l.RUnlock()
and return r.matchingMountInternal(path)
and avoid the local var mount
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor comments. This is looking good to me.
* oss/master: (30 commits) Handle 'not supplied' case for field type TypeNameString (#3546) Fix deprecated cassandra backend tests (#3543) changelog++ auth/aws: Make disallow_reauthentication and allow_instance_migration mutually exclusive (#3291) changelog++ More Mount Conflict Detection (#2919) Fix swallowed errors in TestRollbackManager_Join() (#3327) changelog++ added AWS enpoint handling (#3416) Seal wrap all root tokens and their leases (#3540) Return group memberships of entity during read (#3526) Add note on support for using rec keys on /sys/rekey (#3517) Add third party tools list to website (#3488) Minor client refactoring (#3539) changelog++ Add PKCS8 marshaling to PKI (#3518) Update SSH list roles docs (#3536) Update gocql dep changelog++ Return role info for each role on pathRoleList (#3532) ...
Ensure that we are not creating a "submount" underneath an already
existing mountpoint. This (hopefully) fixes #2878
Note that (locally) I'm seeing intermittent (~25% of the time on the full test suite) build failures 😿