mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-06 06:37:02 +02:00
VAULT-28255: Fix namespaced redirects (#27660)
* handle namespaced events redirects * full test: * changelog * lint
This commit is contained in:
parent
fc19a9ce9c
commit
9e299c2896
3
changelog/27660.txt
Normal file
3
changelog/27660.txt
Normal file
@ -0,0 +1,3 @@
|
||||
```release-note:bug
|
||||
core (enterprise): Fix HTTP redirects in namespaces to use the correct path and (in the case of event subscriptions) the correct URI scheme.
|
||||
```
|
@ -116,7 +116,7 @@ var (
|
||||
"/v1/sys/wrapping/wrap",
|
||||
}
|
||||
websocketRawPaths = []string{
|
||||
"/v1/sys/events/subscribe",
|
||||
"sys/events/subscribe",
|
||||
}
|
||||
oidcProtectedPathRegex = regexp.MustCompile(`^identity/oidc/provider/\w(([\w-.]+)?\w)?/userinfo$`)
|
||||
)
|
||||
@ -128,9 +128,7 @@ func init() {
|
||||
"!sys/storage/raft/snapshot-auto/config",
|
||||
})
|
||||
websocketPaths.AddPaths(websocketRawPaths)
|
||||
for _, path := range websocketRawPaths {
|
||||
alwaysRedirectPaths.AddPaths([]string{strings.TrimPrefix(path, "/v1/")})
|
||||
}
|
||||
alwaysRedirectPaths.AddPaths(websocketRawPaths)
|
||||
}
|
||||
|
||||
type HandlerAnchor struct{}
|
||||
@ -434,7 +432,7 @@ func wrapGenericHandler(core *vault.Core, h http.Handler, props *vault.HandlerPr
|
||||
} else if standby && !perfStandby {
|
||||
// Standby nodes, not performance standbys, don't start plugins
|
||||
// so registration can not happen, instead redirect to active
|
||||
respondStandby(core, w, r.URL)
|
||||
respondStandby(core, w, r)
|
||||
cancelFunc()
|
||||
return
|
||||
} else {
|
||||
@ -909,7 +907,7 @@ func handleRequestForwarding(core *vault.Core, handler http.Handler) http.Handle
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
|
||||
path := trimPath(ns, r.URL.Path)
|
||||
if !perfStandbyAlwaysForwardPaths.HasPath(path) && !alwaysRedirectPaths.HasPath(path) {
|
||||
handler.ServeHTTP(w, r)
|
||||
return
|
||||
@ -946,14 +944,14 @@ func handleRequestForwarding(core *vault.Core, handler http.Handler) http.Handle
|
||||
|
||||
func forwardRequest(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get(vault.IntNoForwardingHeaderName) != "" {
|
||||
respondStandby(core, w, r.URL)
|
||||
respondStandby(core, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Header.Get(NoRequestForwardingHeaderName) != "" {
|
||||
// Forwarding explicitly disabled, fall back to previous behavior
|
||||
core.Logger().Debug("handleRequestForwarding: forwarding disabled by client request")
|
||||
respondStandby(core, w, r.URL)
|
||||
respondStandby(core, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
@ -962,10 +960,25 @@ func forwardRequest(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
|
||||
if alwaysRedirectPaths.HasPath(path) {
|
||||
path := trimPath(ns, r.URL.Path)
|
||||
redirect := alwaysRedirectPaths.HasPath(path)
|
||||
// websocket paths are special, because they can contain a namespace
|
||||
// in front of them. This isn't an issue on perf standbys where the
|
||||
// namespace manager will know all the namespaces, so we will have
|
||||
// already extracted it from the path. But regular standbys don't have
|
||||
// knowledge of the namespaces, so we need
|
||||
// to add an extra check
|
||||
if !redirect && !core.PerfStandby() {
|
||||
for _, websocketPath := range websocketRawPaths {
|
||||
if strings.Contains(path, websocketPath) {
|
||||
redirect = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if redirect {
|
||||
core.Logger().Trace("cannot forward request (path included in always redirect paths), falling back to redirection to standby")
|
||||
respondStandby(core, w, r.URL)
|
||||
respondStandby(core, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
@ -981,7 +994,7 @@ func forwardRequest(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Fall back to redirection
|
||||
respondStandby(core, w, r.URL)
|
||||
respondStandby(core, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
@ -1045,7 +1058,7 @@ func request(core *vault.Core, w http.ResponseWriter, rawReq *http.Request, r *l
|
||||
return resp, false, false
|
||||
}
|
||||
if errwrap.Contains(err, consts.ErrStandby.Error()) {
|
||||
respondStandby(core, w, rawReq.URL)
|
||||
respondStandby(core, w, rawReq)
|
||||
return resp, false, false
|
||||
}
|
||||
if err != nil && errwrap.Contains(err, logical.ErrPerfStandbyPleaseForward.Error()) {
|
||||
@ -1094,7 +1107,8 @@ func request(core *vault.Core, w http.ResponseWriter, rawReq *http.Request, r *l
|
||||
}
|
||||
|
||||
// respondStandby is used to trigger a redirect in the case that this Vault is currently a hot standby
|
||||
func respondStandby(core *vault.Core, w http.ResponseWriter, reqURL *url.URL) {
|
||||
func respondStandby(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
reqURL := r.URL
|
||||
// Request the leader address
|
||||
_, redirectAddr, _, err := core.Leader()
|
||||
if err != nil {
|
||||
@ -1131,8 +1145,13 @@ func respondStandby(core *vault.Core, w http.ResponseWriter, reqURL *url.URL) {
|
||||
RawQuery: reqURL.RawQuery,
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ns, err := namespace.FromContext(ctx)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
}
|
||||
// WebSockets schemas are ws or wss
|
||||
if websocketPaths.HasPath(reqURL.Path) {
|
||||
if websocketPaths.HasPath(trimPath(ns, reqURL.Path)) {
|
||||
if finalURL.Scheme == "http" {
|
||||
finalURL.Scheme = "ws"
|
||||
} else {
|
||||
@ -1140,6 +1159,11 @@ func respondStandby(core *vault.Core, w http.ResponseWriter, reqURL *url.URL) {
|
||||
}
|
||||
}
|
||||
|
||||
originalPath, ok := logical.ContextOriginalRequestPathValue(ctx)
|
||||
if ok {
|
||||
finalURL.Path = originalPath
|
||||
}
|
||||
|
||||
// Ensure there is a scheme, default to https
|
||||
if finalURL.Scheme == "" {
|
||||
finalURL.Scheme = "https"
|
||||
@ -1391,3 +1415,8 @@ func respondOIDCPermissionDenied(w http.ResponseWriter) {
|
||||
enc := json.NewEncoder(w)
|
||||
enc.Encode(oidcResponse)
|
||||
}
|
||||
|
||||
// trimPath removes the /v1/ prefix and the namespace from the path
|
||||
func trimPath(ns *namespace.Namespace, path string) string {
|
||||
return ns.TrimmedPath(path[len("/v1/"):])
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ func handleHelp(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
respondError(w, http.StatusNotFound, errors.New("Missing /v1/ prefix in path. Use vault path-help command to retrieve API help for paths"))
|
||||
return
|
||||
}
|
||||
path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
|
||||
path := trimPath(ns, r.URL.Path)
|
||||
|
||||
req := &logical.Request{
|
||||
Operation: logical.HelpOperation,
|
||||
|
@ -50,8 +50,7 @@ func buildLogicalRequestNoAuth(perfStandby bool, ra *vault.RouterAccess, w http.
|
||||
if err != nil {
|
||||
return nil, nil, http.StatusBadRequest, nil
|
||||
}
|
||||
path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
|
||||
|
||||
path := trimPath(ns, r.URL.Path)
|
||||
var data map[string]interface{}
|
||||
var origBody io.ReadCloser
|
||||
var passHTTPReq bool
|
||||
@ -361,11 +360,13 @@ func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool, noForw
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
trimmedPath := trimPath(ns, r.URL.Path)
|
||||
|
||||
nsPath := ns.Path
|
||||
if ns.ID == namespace.RootNamespaceID {
|
||||
nsPath = ""
|
||||
}
|
||||
if strings.HasPrefix(r.URL.Path, fmt.Sprintf("/v1/%ssys/events/subscribe/", nsPath)) {
|
||||
if websocketPaths.HasPath(trimmedPath) {
|
||||
handler := entHandleEventsSubscribe(core, req)
|
||||
if handler != nil {
|
||||
handler.ServeHTTP(w, r)
|
||||
|
@ -20,7 +20,7 @@ func handleSysRekeyInit(core *vault.Core, recovery bool) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
standby, _ := core.Standby()
|
||||
if standby {
|
||||
respondStandby(core, w, r.URL)
|
||||
respondStandby(core, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
@ -155,7 +155,7 @@ func handleSysRekeyUpdate(core *vault.Core, recovery bool) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
standby, _ := core.Standby()
|
||||
if standby {
|
||||
respondStandby(core, w, r.URL)
|
||||
respondStandby(core, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
@ -228,7 +228,7 @@ func handleSysRekeyVerify(core *vault.Core, recovery bool) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
standby, _ := core.Standby()
|
||||
if standby {
|
||||
respondStandby(core, w, r.URL)
|
||||
respondStandby(core, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user