Reorganize request handling code so that we don't touch storage until we have the stateLock. (#11835)

This commit is contained in:
Nick Cabatoff
2021-06-11 19:18:16 +02:00
committed by GitHub
parent 2168d91efb
commit 7679223da6
14 changed files with 239 additions and 252 deletions

View File

@@ -12,7 +12,6 @@ import (
"strings"
"time"
"github.com/hashicorp/errwrap"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/namespace"
"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
}
rawRequired := r.Header.Values(VaultIndexHeaderName)
if len(rawRequired) != 0 && core.MissingRequiredState(rawRequired) {
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.SetRequiredState(r.Header.Values(VaultIndexHeaderName))
requestAuth(r, req)
req, err = requestWrapInfo(r, req)
if err != nil {
@@ -313,129 +302,6 @@ func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool, noForw
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
// as well in case this is an authentication request that requires
// it. Vault core handles stripping this if we need to. This also