diff --git a/http/logical.go b/http/logical.go index 9ac7f98d67..99bd65813a 100644 --- a/http/logical.go +++ b/http/logical.go @@ -200,6 +200,67 @@ func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool) http.H // requests for these paths always go to the token NS case "auth/token/lookup-self", "auth/token/renew-self", "auth/token/revoke-self": r = r.WithContext(newCtx) + + // For the following operations, we can set the proper namespace context + // using the token's embedded nsID if a relative path was provided. Since + // this is done at the HTTP layer, the operation will still be gated by + // ACLs. + case "auth/token/lookup", "auth/token/renew", "auth/token/revoke", "auth/token/revoke-orphan": + token, ok := req.Data["token"] + // If the token is not present (e.g. a bad request), break out and let the backend + // handle the error + if !ok { + // If this is a token lookup request and if the token is not + // explicitly provided, it will use the client token so we simply set + // the context to the client token's context. + if req.Path == "auth/token/lookup" { + r = r.WithContext(newCtx) + } + break + } + _, nsID := namespace.SplitIDFromString(token.(string)) + if nsID != "" { + ns, err := vault.NamespaceByID(newCtx, nsID, core) + if err != nil { + core.Logger().Warn("error looking up namespace from the token's namespace ID", "error", err) + respondError(w, http.StatusInternalServerError, err) + return + } + if ns != nil { + newCtx = namespace.ContextWithNamespace(newCtx, ns) + r = r.WithContext(newCtx) + } + } + } + + // The following relative sys/leases/ paths handles re-routing requests + // to the proper namespace using the lease ID on applicable paths. + case strings.HasPrefix(req.Path, "sys/leases/"): + switch req.Path { + // For the following operations, we can set the proper namespace context + // using the lease's embedded nsID if a relative path was provided. Since + // this is done at the HTTP layer, the operation will still be gated by + // ACLs. + case "sys/leases/lookup", "sys/leases/renew", "sys/leases/revoke", "sys/leases/revoke-force": + leaseID, ok := req.Data["lease_id"] + // If lease ID is not present, break out and let the backend handle the error + if !ok { + break + } + _, nsID := namespace.SplitIDFromString(leaseID.(string)) + if nsID != "" { + newCtx := r.Context() + ns, err := vault.NamespaceByID(newCtx, nsID, core) + if err != nil { + core.Logger().Warn("error looking up namespace from the lease's namespace ID", "error", err) + respondError(w, http.StatusInternalServerError, err) + return + } + if ns != nil { + newCtx = namespace.ContextWithNamespace(newCtx, ns) + r = r.WithContext(newCtx) + } + } } } diff --git a/vault/expiration.go b/vault/expiration.go index 924fe6c969..f8346f6cd4 100644 --- a/vault/expiration.go +++ b/vault/expiration.go @@ -827,11 +827,11 @@ func (m *ExpirationManager) Renew(ctx context.Context, leaseID string, increment return logical.ErrorResponse("lease does not correspond to a secret"), nil } - reqNS, err := namespace.FromContext(ctx) + ns, err := namespace.FromContext(ctx) if err != nil { return nil, err } - if reqNS.ID != le.namespace.ID { + if ns.ID != le.namespace.ID { return nil, errors.New("cannot renew a lease across namespaces") }