diff --git a/pkg/apiserver/dispatch/dispatch.go b/pkg/apiserver/dispatch/dispatch.go index 44e17162bbed255a67afd2f6e5eaf96bedeb0ec9..fc8c87444f6225ba2a3e5f46baefff4690b717af 100644 --- a/pkg/apiserver/dispatch/dispatch.go +++ b/pkg/apiserver/dispatch/dispatch.go @@ -130,14 +130,16 @@ func (c *clusterDispatch) Dispatch(w http.ResponseWriter, req *http.Request, han u := *req.URL u.Path = strings.Replace(u.Path, fmt.Sprintf("/clusters/%s", info.Cluster), "", 1) + // change request host to actually cluster hosts if info.IsKubernetesRequest { u.Host = innCluster.kubernetesURL.Host u.Scheme = innCluster.kubernetesURL.Scheme } else { u.Host = innCluster.kubesphereURL.Host + u.Scheme = innCluster.kubesphereURL.Scheme // if cluster connection is direct and kubesphere apiserver endpoint is empty - // we use kube-apiserver proxy + // we use kube-apiserver proxy way if cluster.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeDirect && len(cluster.Spec.Connection.KubeSphereAPIEndpoint) == 0 { @@ -145,6 +147,14 @@ func (c *clusterDispatch) Dispatch(w http.ResponseWriter, req *http.Request, han u.Host = innCluster.kubernetesURL.Host u.Path = fmt.Sprintf(proxyURLFormat, u.Path) transport = innCluster.transport + + // The reason we need this is kube-apiserver doesn't behave like a standard proxy, it will strip + // authorization header of proxy requests. Use custom header to avoid stripping by kube-apiserver. + // https://github.com/kubernetes/kubernetes/issues/38775#issuecomment-277915961 + // We first copy req.Header['Authorization'] to req.Header['X-KubeSphere-Authorization'] before sending + // designated cluster kube-apiserver, then copy req.Header['X-KubeSphere-Authorization'] to + // req.Header['Authorization'] before authentication. + req.Header.Set("X-KubeSphere-Authorization", req.Header.Get("Authorization")) } } diff --git a/pkg/apiserver/dispatch/dispatch_test.go b/pkg/apiserver/dispatch/dispatch_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2ac8529a43791c9a660a888bb777bff9e2a746cc --- /dev/null +++ b/pkg/apiserver/dispatch/dispatch_test.go @@ -0,0 +1 @@ +package dispatch diff --git a/pkg/apiserver/filters/requestinfo.go b/pkg/apiserver/filters/requestinfo.go index c2e4dcbfaab130dcc97833f0c5af6b561b7aa538..6f2211cda10cb9d603ab663107f5933e66cb5234 100644 --- a/pkg/apiserver/filters/requestinfo.go +++ b/pkg/apiserver/filters/requestinfo.go @@ -32,6 +32,19 @@ func WithRequestInfo(handler http.Handler, resolver request.RequestInfoResolver) 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. + // https://github.com/kubernetes/kubernetes/issues/38775#issuecomment-277915961 + authorization := req.Header.Get("Authorization") + if len(authorization) == 0 { + xAuthorization := req.Header.Get("X-KubeSphere-Authorization") + if len(xAuthorization) != 0 { + req.Header.Set("Authorization", xAuthorization) + req.Header.Del("X-KubeSphere-Authorization") + } + } + req = req.WithContext(request.WithRequestInfo(ctx, info)) handler.ServeHTTP(w, req) })