diff --git a/pkg/apiserver/dispatch/dispatch.go b/pkg/apiserver/dispatch/dispatch.go index 8a365eb9fa2339802b79bccf406b728de96fa8c1..e764ccdaa4280fa11c9bdd9c760d8a096e12ad58 100644 --- a/pkg/apiserver/dispatch/dispatch.go +++ b/pkg/apiserver/dispatch/dispatch.go @@ -20,6 +20,7 @@ import ( "fmt" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/util/httpstream" "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/proxy" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" @@ -159,6 +160,14 @@ func (c *clusterDispatch) Dispatch(w http.ResponseWriter, req *http.Request, han if len(u.Query()["dryRun"]) != 0 { req.URL.RawQuery = strings.Replace(req.URL.RawQuery, "dryRun", "dryrun", 1) } + + // kube-apiserver lost query string when proxy websocket requests, there are several issues opened + // tracking this, like https://github.com/kubernetes/kubernetes/issues/89360. Also there is a promising + // PR aim to fix this, but it's unlikely it will get merged soon. So here we are again. Put raw query + // string in Header and extract it on member cluster. + if httpstream.IsUpgradeRequest(req) && len(req.URL.RawQuery) != 0 { + req.Header.Set("X-KubeSphere-Rawquery", req.URL.RawQuery) + } } else { // everything else goes to ks-apiserver, since our ks-apiserver has the ability to proxy kube-apiserver requests diff --git a/pkg/apiserver/filters/requestinfo.go b/pkg/apiserver/filters/requestinfo.go index a1848b05e0b848b1af14d2fa02c05a6b0cc22cae..1e2ac9c0eae4cca92271fb7fdfc3b2584dc820b3 100644 --- a/pkg/apiserver/filters/requestinfo.go +++ b/pkg/apiserver/filters/requestinfo.go @@ -26,13 +26,6 @@ import ( func WithRequestInfo(handler http.Handler, resolver request.RequestInfoResolver) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - ctx := req.Context() - info, err := resolver.NewRequestInfo(req) - if err != nil { - responsewriters.InternalError(w, req, fmt.Errorf("failed to crate RequestInfo: %v", err)) - return - } - // KubeSphere supports kube-apiserver proxy requests in multicluster mode. But kube-apiserver // stripped all authorization headers. Use custom header to carry token to avoid losing authentication token. // We may need a better way. See issue below. @@ -54,6 +47,22 @@ func WithRequestInfo(handler http.Handler, resolver request.RequestInfoResolver) req.URL.RawQuery = strings.Replace(req.URL.RawQuery, "dryrun", "dryRun", 1) } + // kube-apiserver lost query string when proxy websocket requests, there are several issues opened + // tracking this, like https://github.com/kubernetes/kubernetes/issues/89360. Also there is a promising + // PR aim to fix this, but it's unlikely it will get merged soon. So here we are again. Put raw query + // string in Header and extract it on member cluster. + if rawQuery := req.Header.Get("X-KubeSphere-Rawquery"); len(rawQuery) != 0 && len(req.URL.RawQuery) == 0 { + req.URL.RawQuery = rawQuery + req.Header.Del("X-KubeSphere-Rawquery") + } + + ctx := req.Context() + info, err := resolver.NewRequestInfo(req) + if err != nil { + responsewriters.InternalError(w, req, fmt.Errorf("failed to crate RequestInfo: %v", err)) + return + } + req = req.WithContext(request.WithRequestInfo(ctx, info)) handler.ServeHTTP(w, req) })