diff --git a/helper/namespace/namespace.go b/helper/namespace/namespace.go index 1b59495cab845..6e042f84689c6 100644 --- a/helper/namespace/namespace.go +++ b/helper/namespace/namespace.go @@ -3,6 +3,7 @@ package namespace import ( "context" "errors" + "fmt" "strings" ) @@ -13,6 +14,10 @@ type Namespace struct { Path string `json:"path"` } +func (n *Namespace) String() string { + return fmt.Sprintf("ID: %s. Path: %s", n.ID, n.Path) +} + const ( RootNamespaceID = "root" ) diff --git a/vault/auth.go b/vault/auth.go index ef10a3786f0b6..ab1aeb8272f89 100644 --- a/vault/auth.go +++ b/vault/auth.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/hashicorp/go-secure-stdlib/strutil" - uuid "github.com/hashicorp/go-uuid" + "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/builtin/plugin" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/helper/consts" @@ -723,16 +723,19 @@ func (c *Core) setupCredentials(ctx context.Context) error { path := credentialRoutePrefix + entry.Path err = c.router.Mount(backend, path, entry, view) if err != nil { - c.logger.Error("failed to mount auth entry", "path", entry.Path, "error", err) + c.logger.Error("failed to mount auth entry", "path", entry.Path, "namespace", entry.Namespace(), "error", err) return errLoadAuthFailed } if c.logger.IsInfo() { - c.logger.Info("successfully enabled credential backend", "type", entry.Type, "path", entry.Path) + c.logger.Info("successfully enabled credential backend", "type", entry.Type, "path", entry.Path, "namespace", entry.Namespace()) } // Ensure the path is tainted if set in the mount table if entry.Tainted { + // Calculate any namespace prefixes here, because when Taint() is called, there won't be + // a namespace to pull from the context. This is similar to what we do above in c.router.Mount(). + path = entry.Namespace().Path + path c.router.Taint(ctx, path) } diff --git a/vault/router.go b/vault/router.go index 6426e4eb8f206..489cadf7e7cbe 100644 --- a/vault/router.go +++ b/vault/router.go @@ -9,9 +9,9 @@ import ( "sync/atomic" "time" - metrics "github.com/armon/go-metrics" - radix "github.com/armon/go-radix" - hclog "github.com/hashicorp/go-hclog" + "github.com/armon/go-metrics" + "github.com/armon/go-radix" + "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/strutil" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/helper/consts" @@ -522,15 +522,27 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc r.l.RLock() adjustedPath := req.Path mount, raw, ok := r.root.LongestPrefix(ns.Path + adjustedPath) + if r.logger.IsTrace() { + r.logger.Trace("trying to route to mount using adjusted path", "namespace", ns, "adjustedPath", adjustedPath) + if !ok { + r.logger.Trace("prefix search returned", "mount", mount, "raw", raw) + } + } if !ok && !strings.HasSuffix(adjustedPath, "/") { // Re-check for a backend by appending a slash. This lets "foo" mean // "foo/" at the root level which is almost always what we want. adjustedPath += "/" mount, raw, ok = r.root.LongestPrefix(ns.Path + adjustedPath) + if r.logger.IsTrace() { + r.logger.Trace("after appending / to adjustedPath, trying again to route to mount using adjusted path", "namespace", ns, "adjustedPath", adjustedPath) + if !ok { + r.logger.Trace("after appending / to adjustedPath, prefix search returned", "mount", mount, "raw", raw) + } + } } r.l.RUnlock() if !ok { - return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s'", req.Path)), false, false, logical.ErrUnsupportedPath + return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s', route entry not found", req.Path)), false, false, logical.ErrUnsupportedPath } req.Path = adjustedPath defer metrics.MeasureSince([]string{ @@ -551,7 +563,10 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc // Filtered mounts will have a nil backend if re.backend == nil { - return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s'", req.Path)), false, false, logical.ErrUnsupportedPath + if r.logger.IsTrace() { + r.logger.Trace("route entry found, but backend is nil") + } + return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s', nil backend", req.Path)), false, false, logical.ErrUnsupportedPath } // If the path is tainted, we reject any operation except for @@ -560,7 +575,10 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc switch req.Operation { case logical.RevokeOperation, logical.RollbackOperation: default: - return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s'", req.Path)), false, false, logical.ErrUnsupportedPath + if r.logger.IsTrace() { + r.logger.Trace("route entry is tainted, rejecting operations to it that aren't revoke or rollback") + } + return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s', entry tainted", req.Path)), false, false, logical.ErrUnsupportedPath } }