mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-04 04:28:08 +00:00 
			
		
		
		
	Reorganize request handling code so that we don't touch storage until we have the stateLock. (#11835)
This commit is contained in:
		@@ -449,25 +449,6 @@ func WrapForwardedForHandler(h http.Handler, l *configutil.Listener) http.Handle
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A lookup on a token that is about to expire returns nil, which means by the
 | 
					 | 
				
			||||||
// time we can validate a wrapping token lookup will return nil since it will
 | 
					 | 
				
			||||||
// be revoked after the call. So we have to do the validation here.
 | 
					 | 
				
			||||||
func wrappingVerificationFunc(ctx context.Context, core *vault.Core, req *logical.Request) error {
 | 
					 | 
				
			||||||
	if req == nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("invalid request")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	valid, err := core.ValidateWrappingToken(ctx, req)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("error validating wrapping token: %w", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !valid {
 | 
					 | 
				
			||||||
		return consts.ErrInvalidWrappingToken
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// stripPrefix is a helper to strip a prefix from the path. It will
 | 
					// stripPrefix is a helper to strip a prefix from the path. It will
 | 
				
			||||||
// return false from the second return value if it the prefix doesn't exist.
 | 
					// return false from the second return value if it the prefix doesn't exist.
 | 
				
			||||||
func stripPrefix(prefix, path string) (string, bool) {
 | 
					func stripPrefix(prefix, path string) (string, bool) {
 | 
				
			||||||
@@ -731,7 +712,7 @@ func forwardBasedOnHeaders(core *vault.Core, r *http.Request) (bool, error) {
 | 
				
			|||||||
		return false, nil
 | 
							return false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return core.MissingRequiredState(r.Header.Values(VaultIndexHeaderName)), nil
 | 
						return core.MissingRequiredState(r.Header.Values(VaultIndexHeaderName), core.PerfStandby()), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// handleRequestForwarding determines whether to forward a request or not,
 | 
					// handleRequestForwarding determines whether to forward a request or not,
 | 
				
			||||||
@@ -977,7 +958,7 @@ func getTokenFromReq(r *http.Request) (string, bool) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// requestAuth adds the token to the logical.Request if it exists.
 | 
					// requestAuth adds the token to the logical.Request if it exists.
 | 
				
			||||||
func requestAuth(core *vault.Core, r *http.Request, req *logical.Request) (*logical.Request, error) {
 | 
					func requestAuth(r *http.Request, req *logical.Request) {
 | 
				
			||||||
	// Attach the header value if we have it
 | 
						// Attach the header value if we have it
 | 
				
			||||||
	token, fromAuthzHeader := getTokenFromReq(r)
 | 
						token, fromAuthzHeader := getTokenFromReq(r)
 | 
				
			||||||
	if token != "" {
 | 
						if token != "" {
 | 
				
			||||||
@@ -987,29 +968,7 @@ func requestAuth(core *vault.Core, r *http.Request, req *logical.Request) (*logi
 | 
				
			|||||||
			req.ClientTokenSource = logical.ClientTokenFromAuthzHeader
 | 
								req.ClientTokenSource = logical.ClientTokenFromAuthzHeader
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Also attach the accessor if we have it. This doesn't fail if it
 | 
					 | 
				
			||||||
		// doesn't exist because the request may be to an unauthenticated
 | 
					 | 
				
			||||||
		// endpoint/login endpoint where a bad current token doesn't matter, or
 | 
					 | 
				
			||||||
		// a token from a Vault version pre-accessors. We ignore errors for
 | 
					 | 
				
			||||||
		// JWTs.
 | 
					 | 
				
			||||||
		te, err := core.LookupToken(r.Context(), token)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			dotCount := strings.Count(token, ".")
 | 
					 | 
				
			||||||
			// If we have two dots but the second char is a dot it's a vault
 | 
					 | 
				
			||||||
			// token of the form s.SOMETHING.nsid, not a JWT
 | 
					 | 
				
			||||||
			if dotCount != 2 ||
 | 
					 | 
				
			||||||
				dotCount == 2 && token[1] == '.' {
 | 
					 | 
				
			||||||
				return req, err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err == nil && te != nil {
 | 
					 | 
				
			||||||
			req.ClientTokenAccessor = te.Accessor
 | 
					 | 
				
			||||||
			req.ClientTokenRemainingUses = te.NumUses
 | 
					 | 
				
			||||||
			req.SetTokenEntry(te)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return req, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func requestPolicyOverride(r *http.Request, req *logical.Request) error {
 | 
					func requestPolicyOverride(r *http.Request, req *logical.Request) error {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -650,7 +650,8 @@ func TestHandler_requestAuth(t *testing.T) {
 | 
				
			|||||||
	for _, r := range []*http.Request{rWithVault, rWithAuthorization} {
 | 
						for _, r := range []*http.Request{rWithVault, rWithAuthorization} {
 | 
				
			||||||
		req := logical.TestRequest(t, logical.ReadOperation, "test/path")
 | 
							req := logical.TestRequest(t, logical.ReadOperation, "test/path")
 | 
				
			||||||
		r = r.WithContext(rootCtx)
 | 
							r = r.WithContext(rootCtx)
 | 
				
			||||||
		req, err = requestAuth(core, r, req)
 | 
							requestAuth(r, req)
 | 
				
			||||||
 | 
							err = core.PopulateTokenEntry(rootCtx, req)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatalf("err: %s", err)
 | 
								t.Fatalf("err: %s", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -675,25 +676,14 @@ func TestHandler_requestAuth(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	req := logical.TestRequest(t, logical.ReadOperation, "test/path")
 | 
						req := logical.TestRequest(t, logical.ReadOperation, "test/path")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req, err = requestAuth(core, rNothing, req)
 | 
						requestAuth(rNothing, req)
 | 
				
			||||||
 | 
						err = core.PopulateTokenEntry(rootCtx, req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("expected no error, got %s", err)
 | 
							t.Fatalf("expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if req.ClientToken != "" {
 | 
						if req.ClientToken != "" {
 | 
				
			||||||
		t.Fatalf("client token should not be filled, got %s", req.ClientToken)
 | 
							t.Fatalf("client token should not be filled, got %s", req.ClientToken)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	rFragmentedHeader, err := http.NewRequest("GET", "v1/test/path", nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("err: %s", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	rFragmentedHeader.Header.Set("Authorization", "Bearer something somewhat")
 | 
					 | 
				
			||||||
	req = logical.TestRequest(t, logical.ReadOperation, "test/path")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, err = requestAuth(core, rFragmentedHeader, req)
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		t.Fatalf("expected an error, got none")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestHandler_getTokenFromReq(t *testing.T) {
 | 
					func TestHandler_getTokenFromReq(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								http/help.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								http/help.go
									
									
									
									
									
								
							@@ -1,10 +1,8 @@
 | 
				
			|||||||
package http
 | 
					package http
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/hashicorp/errwrap"
 | 
					 | 
				
			||||||
	"github.com/hashicorp/vault/helper/namespace"
 | 
						"github.com/hashicorp/vault/helper/namespace"
 | 
				
			||||||
	"github.com/hashicorp/vault/sdk/logical"
 | 
						"github.com/hashicorp/vault/sdk/logical"
 | 
				
			||||||
	"github.com/hashicorp/vault/vault"
 | 
						"github.com/hashicorp/vault/vault"
 | 
				
			||||||
@@ -35,19 +33,12 @@ func handleHelp(core *vault.Core, w http.ResponseWriter, r *http.Request) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
 | 
						path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req, err := requestAuth(core, r, &logical.Request{
 | 
						req := &logical.Request{
 | 
				
			||||||
		Operation:  logical.HelpOperation,
 | 
							Operation:  logical.HelpOperation,
 | 
				
			||||||
		Path:       path,
 | 
							Path:       path,
 | 
				
			||||||
		Connection: getConnection(r),
 | 
							Connection: getConnection(r),
 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) {
 | 
					 | 
				
			||||||
			respondError(w, http.StatusForbidden, nil)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		respondError(w, http.StatusBadRequest, fmt.Errorf("error performing token check: %w", err))
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						requestAuth(r, req)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp, err := core.HandleRequest(r.Context(), req)
 | 
						resp, err := core.HandleRequest(r.Context(), req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package http
 | 
					package http
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/hashicorp/vault/vault"
 | 
						"github.com/hashicorp/vault/vault"
 | 
				
			||||||
@@ -12,7 +13,12 @@ func TestHelp(t *testing.T) {
 | 
				
			|||||||
	defer ln.Close()
 | 
						defer ln.Close()
 | 
				
			||||||
	TestServerAuth(t, addr, token)
 | 
						TestServerAuth(t, addr, token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := testHttpGet(t, token, addr+"/v1/sys/mounts?help=1")
 | 
						resp := testHttpGet(t, "", addr+"/v1/sys/mounts?help=1")
 | 
				
			||||||
 | 
						if resp.StatusCode != http.StatusBadRequest {
 | 
				
			||||||
 | 
							t.Fatal("expected bad request with no token")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp = testHttpGet(t, token, addr+"/v1/sys/mounts?help=1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var actual map[string]interface{}
 | 
						var actual map[string]interface{}
 | 
				
			||||||
	testResponseStatus(t, resp, 200)
 | 
						testResponseStatus(t, resp, 200)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										138
									
								
								http/logical.go
									
									
									
									
									
								
							
							
						
						
									
										138
									
								
								http/logical.go
									
									
									
									
									
								
							@@ -12,7 +12,6 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/hashicorp/errwrap"
 | 
					 | 
				
			||||||
	uuid "github.com/hashicorp/go-uuid"
 | 
						uuid "github.com/hashicorp/go-uuid"
 | 
				
			||||||
	"github.com/hashicorp/vault/helper/namespace"
 | 
						"github.com/hashicorp/vault/helper/namespace"
 | 
				
			||||||
	"github.com/hashicorp/vault/sdk/helper/consts"
 | 
						"github.com/hashicorp/vault/sdk/helper/consts"
 | 
				
			||||||
@@ -220,18 +219,8 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques
 | 
				
			|||||||
		return nil, nil, status, err
 | 
							return nil, nil, status, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rawRequired := r.Header.Values(VaultIndexHeaderName)
 | 
						req.SetRequiredState(r.Header.Values(VaultIndexHeaderName))
 | 
				
			||||||
	if len(rawRequired) != 0 && core.MissingRequiredState(rawRequired) {
 | 
						requestAuth(r, req)
 | 
				
			||||||
		return nil, nil, http.StatusPreconditionFailed, fmt.Errorf("required index state not present")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	req, err = requestAuth(core, r, req)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) {
 | 
					 | 
				
			||||||
			return nil, nil, http.StatusForbidden, nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return nil, nil, http.StatusBadRequest, fmt.Errorf("error performing token check: %w", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req, err = requestWrapInfo(r, req)
 | 
						req, err = requestWrapInfo(r, req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -313,129 +302,6 @@ func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool, noForw
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Always forward requests that are using a limited use count token.
 | 
					 | 
				
			||||||
		if core.PerfStandby() && req.ClientTokenRemainingUses > 0 {
 | 
					 | 
				
			||||||
			// Prevent forwarding on local-only requests.
 | 
					 | 
				
			||||||
			if noForward {
 | 
					 | 
				
			||||||
				respondError(w, http.StatusBadRequest, vault.ErrCannotForwardLocalOnly)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if origBody != nil {
 | 
					 | 
				
			||||||
				r.Body = origBody
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			forwardRequest(core, w, r)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// req.Path will be relative by this point. The prefix check is first
 | 
					 | 
				
			||||||
		// to fail faster if we're not in this situation since it's a hot path
 | 
					 | 
				
			||||||
		switch {
 | 
					 | 
				
			||||||
		case strings.HasPrefix(req.Path, "sys/wrapping/"), strings.HasPrefix(req.Path, "auth/token/"):
 | 
					 | 
				
			||||||
			// Get the token ns info; if we match the paths below we want to
 | 
					 | 
				
			||||||
			// swap in the token context (but keep the relative path)
 | 
					 | 
				
			||||||
			te := req.TokenEntry()
 | 
					 | 
				
			||||||
			newCtx := r.Context()
 | 
					 | 
				
			||||||
			if te != nil {
 | 
					 | 
				
			||||||
				ns, err := vault.NamespaceByID(newCtx, te.NamespaceID, 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)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			switch req.Path {
 | 
					 | 
				
			||||||
			// Route the token wrapping request to its respective sys NS
 | 
					 | 
				
			||||||
			case "sys/wrapping/lookup", "sys/wrapping/rewrap", "sys/wrapping/unwrap":
 | 
					 | 
				
			||||||
				r = r.WithContext(newCtx)
 | 
					 | 
				
			||||||
				if err := wrappingVerificationFunc(r.Context(), core, req); err != nil {
 | 
					 | 
				
			||||||
					if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) {
 | 
					 | 
				
			||||||
						respondError(w, http.StatusForbidden, err)
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						respondError(w, http.StatusBadRequest, err)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// The -self paths have no meaning outside of the token NS, so
 | 
					 | 
				
			||||||
			// 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)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Prevent any metrics requests to be forwarded from a standby node.
 | 
					 | 
				
			||||||
		// Instead, we return an error since we cannot be sure if we have an
 | 
					 | 
				
			||||||
		// active token store to validate the provided token.
 | 
					 | 
				
			||||||
		case strings.HasPrefix(req.Path, "sys/metrics"):
 | 
					 | 
				
			||||||
			if isStandby, _ := core.Standby(); isStandby {
 | 
					 | 
				
			||||||
				respondError(w, http.StatusBadRequest, vault.ErrCannotForwardLocalOnly)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Make the internal request. We attach the connection info
 | 
							// Make the internal request. We attach the connection info
 | 
				
			||||||
		// as well in case this is an authentication request that requires
 | 
							// as well in case this is an authentication request that requires
 | 
				
			||||||
		// it. Vault core handles stripping this if we need to. This also
 | 
							// it. Vault core handles stripping this if we need to. This also
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,6 +42,11 @@ var (
 | 
				
			|||||||
	// unrecoverable error.
 | 
						// unrecoverable error.
 | 
				
			||||||
	// e.g.: misconfigured or disconnected storage backend.
 | 
						// e.g.: misconfigured or disconnected storage backend.
 | 
				
			||||||
	ErrUnrecoverable = errors.New("unrecoverable error")
 | 
						ErrUnrecoverable = errors.New("unrecoverable error")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ErrMissingRequiredState is returned when a request can't be satisfied
 | 
				
			||||||
 | 
						// with the data in the local node's storage, based on the provided
 | 
				
			||||||
 | 
						// X-Vault-Index request header.
 | 
				
			||||||
 | 
						ErrMissingRequiredState = errors.New("required index state not present")
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type HTTPCodedError interface {
 | 
					type HTTPCodedError interface {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -205,6 +205,11 @@ type Request struct {
 | 
				
			|||||||
	// request that generated this logical.Request object.
 | 
						// request that generated this logical.Request object.
 | 
				
			||||||
	ResponseWriter *HTTPResponseWriter `json:"-" sentinel:""`
 | 
						ResponseWriter *HTTPResponseWriter `json:"-" sentinel:""`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// requiredState is used internally to propagate the X-Vault-Index request
 | 
				
			||||||
 | 
						// header to later levels of request processing that operate only on
 | 
				
			||||||
 | 
						// logical.Request.
 | 
				
			||||||
 | 
						requiredState []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// responseState is used internally to propagate the state that should appear
 | 
						// responseState is used internally to propagate the state that should appear
 | 
				
			||||||
	// in response headers; it's attached to the request rather than the response
 | 
						// in response headers; it's attached to the request rather than the response
 | 
				
			||||||
	// because not all requests yields non-nil responses.
 | 
						// because not all requests yields non-nil responses.
 | 
				
			||||||
@@ -273,6 +278,14 @@ func (r *Request) SetLastRemoteWAL(last uint64) {
 | 
				
			|||||||
	r.lastRemoteWAL = last
 | 
						r.lastRemoteWAL = last
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Request) RequiredState() []string {
 | 
				
			||||||
 | 
						return r.requiredState
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Request) SetRequiredState(state []string) {
 | 
				
			||||||
 | 
						r.requiredState = state
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *Request) ResponseState() *WALState {
 | 
					func (r *Request) ResponseState() *WALState {
 | 
				
			||||||
	return r.responseState
 | 
						return r.responseState
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,6 +102,8 @@ func RespondErrorCommon(req *Request, resp *Response, err error) (int, error) {
 | 
				
			|||||||
			statusCode = http.StatusBadRequest
 | 
								statusCode = http.StatusBadRequest
 | 
				
			||||||
		case errwrap.Contains(err, ErrPermissionDenied.Error()):
 | 
							case errwrap.Contains(err, ErrPermissionDenied.Error()):
 | 
				
			||||||
			statusCode = http.StatusForbidden
 | 
								statusCode = http.StatusForbidden
 | 
				
			||||||
 | 
							case errwrap.Contains(err, consts.ErrInvalidWrappingToken.Error()):
 | 
				
			||||||
 | 
								statusCode = http.StatusBadRequest
 | 
				
			||||||
		case errwrap.Contains(err, ErrUnsupportedOperation.Error()):
 | 
							case errwrap.Contains(err, ErrUnsupportedOperation.Error()):
 | 
				
			||||||
			statusCode = http.StatusMethodNotAllowed
 | 
								statusCode = http.StatusMethodNotAllowed
 | 
				
			||||||
		case errwrap.Contains(err, ErrUnsupportedPath.Error()):
 | 
							case errwrap.Contains(err, ErrUnsupportedPath.Error()):
 | 
				
			||||||
@@ -114,6 +116,8 @@ func RespondErrorCommon(req *Request, resp *Response, err error) (int, error) {
 | 
				
			|||||||
			statusCode = http.StatusTooManyRequests
 | 
								statusCode = http.StatusTooManyRequests
 | 
				
			||||||
		case errwrap.Contains(err, ErrLeaseCountQuotaExceeded.Error()):
 | 
							case errwrap.Contains(err, ErrLeaseCountQuotaExceeded.Error()):
 | 
				
			||||||
			statusCode = http.StatusTooManyRequests
 | 
								statusCode = http.StatusTooManyRequests
 | 
				
			||||||
 | 
							case errwrap.Contains(err, ErrMissingRequiredState.Error()):
 | 
				
			||||||
 | 
								statusCode = http.StatusPreconditionFailed
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1673,8 +1673,7 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
 | 
				
			|||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if req == nil {
 | 
						if req == nil {
 | 
				
			||||||
		retErr = multierror.Append(retErr, errors.New("nil request to seal"))
 | 
							return errors.New("nil request to seal")
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Since there is no token store in standby nodes, sealing cannot be done.
 | 
						// Since there is no token store in standby nodes, sealing cannot be done.
 | 
				
			||||||
@@ -1684,14 +1683,19 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
 | 
				
			|||||||
	// same thing.
 | 
						// same thing.
 | 
				
			||||||
	if c.standby {
 | 
						if c.standby {
 | 
				
			||||||
		c.logger.Error("vault cannot seal when in standby mode; please restart instead")
 | 
							c.logger.Error("vault cannot seal when in standby mode; please restart instead")
 | 
				
			||||||
		retErr = multierror.Append(retErr, errors.New("vault cannot seal when in standby mode; please restart instead"))
 | 
							return errors.New("vault cannot seal when in standby mode; please restart instead")
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := c.PopulateTokenEntry(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) {
 | 
				
			||||||
 | 
								return logical.ErrPermissionDenied
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return logical.ErrInvalidRequest
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	acl, te, entity, identityPolicies, err := c.fetchACLTokenEntryAndEntity(ctx, req)
 | 
						acl, te, entity, identityPolicies, err := c.fetchACLTokenEntryAndEntity(ctx, req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		retErr = multierror.Append(retErr, err)
 | 
							return err
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Audit-log the request before going any further
 | 
						// Audit-log the request before going any further
 | 
				
			||||||
@@ -1717,19 +1721,16 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	if err := c.auditBroker.LogRequest(ctx, logInput, c.auditedHeaders); err != nil {
 | 
						if err := c.auditBroker.LogRequest(ctx, logInput, c.auditedHeaders); err != nil {
 | 
				
			||||||
		c.logger.Error("failed to audit request", "request_path", req.Path, "error", err)
 | 
							c.logger.Error("failed to audit request", "request_path", req.Path, "error", err)
 | 
				
			||||||
		retErr = multierror.Append(retErr, errors.New("failed to audit request, cannot continue"))
 | 
							return errors.New("failed to audit request, cannot continue")
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if entity != nil && entity.Disabled {
 | 
						if entity != nil && entity.Disabled {
 | 
				
			||||||
		c.logger.Warn("permission denied as the entity on the token is disabled")
 | 
							c.logger.Warn("permission denied as the entity on the token is disabled")
 | 
				
			||||||
		retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
 | 
							return logical.ErrPermissionDenied
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if te != nil && te.EntityID != "" && entity == nil {
 | 
						if te != nil && te.EntityID != "" && entity == nil {
 | 
				
			||||||
		c.logger.Warn("permission denied as the entity on the token is invalid")
 | 
							c.logger.Warn("permission denied as the entity on the token is invalid")
 | 
				
			||||||
		retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
 | 
							return logical.ErrPermissionDenied
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Attempt to use the token (decrement num_uses)
 | 
						// Attempt to use the token (decrement num_uses)
 | 
				
			||||||
@@ -1738,13 +1739,11 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
 | 
				
			|||||||
		te, err = c.tokenStore.UseToken(ctx, te)
 | 
							te, err = c.tokenStore.UseToken(ctx, te)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			c.logger.Error("failed to use token", "error", err)
 | 
								c.logger.Error("failed to use token", "error", err)
 | 
				
			||||||
			retErr = multierror.Append(retErr, ErrInternalError)
 | 
								return ErrInternalError
 | 
				
			||||||
			return retErr
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if te == nil {
 | 
							if te == nil {
 | 
				
			||||||
			// Token is no longer valid
 | 
								// Token is no longer valid
 | 
				
			||||||
			retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
 | 
								return logical.ErrPermissionDenied
 | 
				
			||||||
			return retErr
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -177,6 +177,6 @@ func (c *Core) AllowForwardingViaHeader() bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Core) MissingRequiredState(raw []string) bool {
 | 
					func (c *Core) MissingRequiredState(raw []string, perfStandby bool) bool {
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								vault/ha.go
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								vault/ha.go
									
									
									
									
									
								
							@@ -11,17 +11,17 @@ import (
 | 
				
			|||||||
	"sync/atomic"
 | 
						"sync/atomic"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/armon/go-metrics"
 | 
				
			||||||
 | 
						"github.com/hashicorp/errwrap"
 | 
				
			||||||
 | 
						aeadwrapper "github.com/hashicorp/go-kms-wrapping/wrappers/aead"
 | 
				
			||||||
 | 
						"github.com/hashicorp/go-multierror"
 | 
				
			||||||
 | 
						"github.com/hashicorp/go-uuid"
 | 
				
			||||||
 | 
						"github.com/hashicorp/vault/helper/namespace"
 | 
				
			||||||
	"github.com/hashicorp/vault/sdk/helper/certutil"
 | 
						"github.com/hashicorp/vault/sdk/helper/certutil"
 | 
				
			||||||
	"github.com/hashicorp/vault/sdk/helper/consts"
 | 
						"github.com/hashicorp/vault/sdk/helper/consts"
 | 
				
			||||||
	"github.com/hashicorp/vault/sdk/helper/jsonutil"
 | 
						"github.com/hashicorp/vault/sdk/helper/jsonutil"
 | 
				
			||||||
	"github.com/hashicorp/vault/sdk/logical"
 | 
						"github.com/hashicorp/vault/sdk/logical"
 | 
				
			||||||
	"github.com/hashicorp/vault/sdk/physical"
 | 
						"github.com/hashicorp/vault/sdk/physical"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/armon/go-metrics"
 | 
					 | 
				
			||||||
	aeadwrapper "github.com/hashicorp/go-kms-wrapping/wrappers/aead"
 | 
					 | 
				
			||||||
	"github.com/hashicorp/go-multierror"
 | 
					 | 
				
			||||||
	"github.com/hashicorp/go-uuid"
 | 
					 | 
				
			||||||
	"github.com/hashicorp/vault/helper/namespace"
 | 
					 | 
				
			||||||
	"github.com/hashicorp/vault/vault/seal"
 | 
						"github.com/hashicorp/vault/vault/seal"
 | 
				
			||||||
	"github.com/oklog/run"
 | 
						"github.com/oklog/run"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -218,8 +218,7 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e
 | 
				
			|||||||
	defer metrics.MeasureSince([]string{"core", "step_down"}, time.Now())
 | 
						defer metrics.MeasureSince([]string{"core", "step_down"}, time.Now())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if req == nil {
 | 
						if req == nil {
 | 
				
			||||||
		retErr = multierror.Append(retErr, errors.New("nil request to step-down"))
 | 
							return errors.New("nil request to step-down")
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.stateLock.RLock()
 | 
						c.stateLock.RLock()
 | 
				
			||||||
@@ -243,10 +242,16 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := c.PopulateTokenEntry(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) {
 | 
				
			||||||
 | 
								return logical.ErrPermissionDenied
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return logical.ErrInvalidRequest
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	acl, te, entity, identityPolicies, err := c.fetchACLTokenEntryAndEntity(ctx, req)
 | 
						acl, te, entity, identityPolicies, err := c.fetchACLTokenEntryAndEntity(ctx, req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		retErr = multierror.Append(retErr, err)
 | 
							return err
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Audit-log the request before going any further
 | 
						// Audit-log the request before going any further
 | 
				
			||||||
@@ -272,20 +277,17 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	if err := c.auditBroker.LogRequest(ctx, logInput, c.auditedHeaders); err != nil {
 | 
						if err := c.auditBroker.LogRequest(ctx, logInput, c.auditedHeaders); err != nil {
 | 
				
			||||||
		c.logger.Error("failed to audit request", "request_path", req.Path, "error", err)
 | 
							c.logger.Error("failed to audit request", "request_path", req.Path, "error", err)
 | 
				
			||||||
		retErr = multierror.Append(retErr, errors.New("failed to audit request, cannot continue"))
 | 
							return errors.New("failed to audit request, cannot continue")
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if entity != nil && entity.Disabled {
 | 
						if entity != nil && entity.Disabled {
 | 
				
			||||||
		c.logger.Warn("permission denied as the entity on the token is disabled")
 | 
							c.logger.Warn("permission denied as the entity on the token is disabled")
 | 
				
			||||||
		retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
 | 
							return logical.ErrPermissionDenied
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if te != nil && te.EntityID != "" && entity == nil {
 | 
						if te != nil && te.EntityID != "" && entity == nil {
 | 
				
			||||||
		c.logger.Warn("permission denied as the entity on the token is invalid")
 | 
							c.logger.Warn("permission denied as the entity on the token is invalid")
 | 
				
			||||||
		retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
 | 
							return logical.ErrPermissionDenied
 | 
				
			||||||
		return retErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Attempt to use the token (decrement num_uses)
 | 
						// Attempt to use the token (decrement num_uses)
 | 
				
			||||||
@@ -293,13 +295,11 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e
 | 
				
			|||||||
		te, err = c.tokenStore.UseToken(ctx, te)
 | 
							te, err = c.tokenStore.UseToken(ctx, te)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			c.logger.Error("failed to use token", "error", err)
 | 
								c.logger.Error("failed to use token", "error", err)
 | 
				
			||||||
			retErr = multierror.Append(retErr, ErrInternalError)
 | 
								return ErrInternalError
 | 
				
			||||||
			return retErr
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if te == nil {
 | 
							if te == nil {
 | 
				
			||||||
			// Token has been revoked
 | 
								// Token has been revoked
 | 
				
			||||||
			retErr = multierror.Append(retErr, logical.ErrPermissionDenied)
 | 
								return logical.ErrPermissionDenied
 | 
				
			||||||
			return retErr
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -421,16 +421,15 @@ func (c *Core) switchedLockHandleRequest(httpCtx context.Context, req *logical.R
 | 
				
			|||||||
		cancel()
 | 
							cancel()
 | 
				
			||||||
		return nil, fmt.Errorf("could not parse namespace from http context: %w", err)
 | 
							return nil, fmt.Errorf("could not parse namespace from http context: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx = namespace.ContextWithNamespace(ctx, ns)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp, err = c.handleCancelableRequest(ctx, ns, req)
 | 
						resp, err = c.handleCancelableRequest(namespace.ContextWithNamespace(ctx, ns), req)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req.SetTokenEntry(nil)
 | 
						req.SetTokenEntry(nil)
 | 
				
			||||||
	cancel()
 | 
						cancel()
 | 
				
			||||||
	return resp, err
 | 
						return resp, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Core) handleCancelableRequest(ctx context.Context, ns *namespace.Namespace, req *logical.Request) (resp *logical.Response, err error) {
 | 
					func (c *Core) handleCancelableRequest(ctx context.Context, req *logical.Request) (resp *logical.Response, err error) {
 | 
				
			||||||
	// Allowing writing to a path ending in / makes it extremely difficult to
 | 
						// Allowing writing to a path ending in / makes it extremely difficult to
 | 
				
			||||||
	// understand user intent for the filesystem-like backends (kv,
 | 
						// understand user intent for the filesystem-like backends (kv,
 | 
				
			||||||
	// cubbyhole) -- did they want a key named foo/ or did they want to write
 | 
						// cubbyhole) -- did they want a key named foo/ or did they want to write
 | 
				
			||||||
@@ -453,6 +452,133 @@ func (c *Core) handleCancelableRequest(ctx context.Context, ns *namespace.Namesp
 | 
				
			|||||||
		defer waitGroup.Done()
 | 
							defer waitGroup.Done()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c.MissingRequiredState(req.RequiredState(), c.perfStandby) {
 | 
				
			||||||
 | 
							return nil, logical.ErrMissingRequiredState
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = c.PopulateTokenEntry(ctx, req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Always forward requests that are using a limited use count token.
 | 
				
			||||||
 | 
						if c.perfStandby && req.ClientTokenRemainingUses > 0 {
 | 
				
			||||||
 | 
							// Prevent forwarding on local-only requests.
 | 
				
			||||||
 | 
							return nil, logical.ErrPerfStandbyPleaseForward
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ns, err := namespace.FromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("could not parse namespace from http context: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// req.Path will be relative by this point. The prefix check is first
 | 
				
			||||||
 | 
						// to fail faster if we're not in this situation since it's a hot path
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case strings.HasPrefix(req.Path, "sys/wrapping/"), strings.HasPrefix(req.Path, "auth/token/"):
 | 
				
			||||||
 | 
							// Get the token ns info; if we match the paths below we want to
 | 
				
			||||||
 | 
							// swap in the token context (but keep the relative path)
 | 
				
			||||||
 | 
							te := req.TokenEntry()
 | 
				
			||||||
 | 
							newCtx := ctx
 | 
				
			||||||
 | 
							if te != nil {
 | 
				
			||||||
 | 
								ns, err := NamespaceByID(ctx, te.NamespaceID, c)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									c.Logger().Warn("error looking up namespace from the token's namespace ID", "error", err)
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if ns != nil {
 | 
				
			||||||
 | 
									newCtx = namespace.ContextWithNamespace(ctx, ns)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							switch req.Path {
 | 
				
			||||||
 | 
							// Route the token wrapping request to its respective sys NS
 | 
				
			||||||
 | 
							case "sys/wrapping/lookup", "sys/wrapping/rewrap", "sys/wrapping/unwrap":
 | 
				
			||||||
 | 
								ctx = newCtx
 | 
				
			||||||
 | 
								// A lookup on a token that is about to expire returns nil, which means by the
 | 
				
			||||||
 | 
								// time we can validate a wrapping token lookup will return nil since it will
 | 
				
			||||||
 | 
								// be revoked after the call. So we have to do the validation here.
 | 
				
			||||||
 | 
								valid, err := c.validateWrappingToken(ctx, req)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, fmt.Errorf("error validating wrapping token: %w", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if !valid {
 | 
				
			||||||
 | 
									return nil, consts.ErrInvalidWrappingToken
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// The -self paths have no meaning outside of the token NS, so
 | 
				
			||||||
 | 
							// requests for these paths always go to the token NS
 | 
				
			||||||
 | 
							case "auth/token/lookup-self", "auth/token/renew-self", "auth/token/revoke-self":
 | 
				
			||||||
 | 
								ctx = newCtx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// For the following operations, we can set the proper namespace context
 | 
				
			||||||
 | 
							// using the token's embedded nsID if a relative path was provided.
 | 
				
			||||||
 | 
							// The operation will still be gated by ACLs, which are checked later.
 | 
				
			||||||
 | 
							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" {
 | 
				
			||||||
 | 
										ctx = newCtx
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								_, nsID := namespace.SplitIDFromString(token.(string))
 | 
				
			||||||
 | 
								if nsID != "" {
 | 
				
			||||||
 | 
									ns, err := NamespaceByID(ctx, nsID, c)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										c.Logger().Warn("error looking up namespace from the token's namespace ID", "error", err)
 | 
				
			||||||
 | 
										return nil, err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if ns != nil {
 | 
				
			||||||
 | 
										ctx = namespace.ContextWithNamespace(ctx, ns)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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.
 | 
				
			||||||
 | 
							// The operation will still be gated by ACLs, which are checked later.
 | 
				
			||||||
 | 
							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 != "" {
 | 
				
			||||||
 | 
									ns, err := NamespaceByID(ctx, nsID, c)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										c.Logger().Warn("error looking up namespace from the lease's namespace ID", "error", err)
 | 
				
			||||||
 | 
										return nil, err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if ns != nil {
 | 
				
			||||||
 | 
										ctx = namespace.ContextWithNamespace(ctx, ns)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Prevent any metrics requests to be forwarded from a standby node.
 | 
				
			||||||
 | 
						// Instead, we return an error since we cannot be sure if we have an
 | 
				
			||||||
 | 
						// active token store to validate the provided token.
 | 
				
			||||||
 | 
						case strings.HasPrefix(req.Path, "sys/metrics"):
 | 
				
			||||||
 | 
							if c.standby && !c.perfStandby {
 | 
				
			||||||
 | 
								return nil, ErrCannotForwardLocalOnly
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ns, err = namespace.FromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, errwrap.Wrapf("could not parse namespace from http context: {{err}}", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !hasNamespaces(c) && ns.Path != "" {
 | 
						if !hasNamespaces(c) && ns.Path != "" {
 | 
				
			||||||
		return nil, logical.CodedError(403, "namespaces feature not enabled")
 | 
							return nil, logical.CodedError(403, "namespaces feature not enabled")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1368,3 +1494,35 @@ func (c *Core) RegisterAuth(ctx context.Context, tokenTTL time.Duration, path st
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PopulateTokenEntry looks up req.ClientToken in the token store and uses
 | 
				
			||||||
 | 
					// it to set other fields in req.  Does nothing if ClientToken is empty
 | 
				
			||||||
 | 
					// or a JWT token, or for service tokens that don't exist in the token store.
 | 
				
			||||||
 | 
					// Should be called with read stateLock held.
 | 
				
			||||||
 | 
					func (c *Core) PopulateTokenEntry(ctx context.Context, req *logical.Request) error {
 | 
				
			||||||
 | 
						if req.ClientToken == "" {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Also attach the accessor if we have it. This doesn't fail if it
 | 
				
			||||||
 | 
						// doesn't exist because the request may be to an unauthenticated
 | 
				
			||||||
 | 
						// endpoint/login endpoint where a bad current token doesn't matter, or
 | 
				
			||||||
 | 
						// a token from a Vault version pre-accessors. We ignore errors for
 | 
				
			||||||
 | 
						// JWTs.
 | 
				
			||||||
 | 
						token := req.ClientToken
 | 
				
			||||||
 | 
						te, err := c.LookupToken(ctx, token)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							dotCount := strings.Count(token, ".")
 | 
				
			||||||
 | 
							// If we have two dots but the second char is a dot it's a vault
 | 
				
			||||||
 | 
							// token of the form s.SOMETHING.nsid, not a JWT
 | 
				
			||||||
 | 
							if dotCount != 2 || (dotCount == 2 && token[1] == '.') {
 | 
				
			||||||
 | 
								return fmt.Errorf("error performing token check: %w", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err == nil && te != nil {
 | 
				
			||||||
 | 
							req.ClientTokenAccessor = te.Accessor
 | 
				
			||||||
 | 
							req.ClientTokenRemainingUses = te.NumUses
 | 
				
			||||||
 | 
							req.SetTokenEntry(te)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -452,14 +452,13 @@ func (ts *TokenStore) paths() []*framework.Path {
 | 
				
			|||||||
// is particularly useful to fetch the accessor of the client token and get it
 | 
					// is particularly useful to fetch the accessor of the client token and get it
 | 
				
			||||||
// populated in the logical request along with the client token. The accessor
 | 
					// populated in the logical request along with the client token. The accessor
 | 
				
			||||||
// of the client token can get audit logged.
 | 
					// of the client token can get audit logged.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Should be called with read stateLock held.
 | 
				
			||||||
func (c *Core) LookupToken(ctx context.Context, token string) (*logical.TokenEntry, error) {
 | 
					func (c *Core) LookupToken(ctx context.Context, token string) (*logical.TokenEntry, error) {
 | 
				
			||||||
	if c.Sealed() {
 | 
						if c.Sealed() {
 | 
				
			||||||
		return nil, consts.ErrSealed
 | 
							return nil, consts.ErrSealed
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.stateLock.RLock()
 | 
					 | 
				
			||||||
	defer c.stateLock.RUnlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if c.standby && !c.perfStandby {
 | 
						if c.standby && !c.perfStandby {
 | 
				
			||||||
		return nil, consts.ErrStandby
 | 
							return nil, consts.ErrStandby
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -334,7 +334,7 @@ DONELISTHANDLING:
 | 
				
			|||||||
// validateWrappingToken checks whether a token is a wrapping token. The passed
 | 
					// validateWrappingToken checks whether a token is a wrapping token. The passed
 | 
				
			||||||
// in logical request will be updated if the wrapping token was provided within
 | 
					// in logical request will be updated if the wrapping token was provided within
 | 
				
			||||||
// a JWT token.
 | 
					// a JWT token.
 | 
				
			||||||
func (c *Core) ValidateWrappingToken(ctx context.Context, req *logical.Request) (valid bool, err error) {
 | 
					func (c *Core) validateWrappingToken(ctx context.Context, req *logical.Request) (valid bool, err error) {
 | 
				
			||||||
	if req == nil {
 | 
						if req == nil {
 | 
				
			||||||
		return false, fmt.Errorf("invalid request")
 | 
							return false, fmt.Errorf("invalid request")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -343,9 +343,6 @@ func (c *Core) ValidateWrappingToken(ctx context.Context, req *logical.Request)
 | 
				
			|||||||
		return false, consts.ErrSealed
 | 
							return false, consts.ErrSealed
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.stateLock.RLock()
 | 
					 | 
				
			||||||
	defer c.stateLock.RUnlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if c.standby && !c.perfStandby {
 | 
						if c.standby && !c.perfStandby {
 | 
				
			||||||
		return false, consts.ErrStandby
 | 
							return false, consts.ErrStandby
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user