未验证 提交 845887a3 编写于 作者: Z zryfish 提交者: GitHub

fix proxy bug (#2146)

上级 68029de6
......@@ -39,6 +39,8 @@ import (
const proxyURLFormat = "/api/v1/namespaces/kubesphere-system/services/:ks-apiserver:/proxy%s"
// Dispatcher defines how to forward request to designated cluster based on cluster name
// This should only be used in host cluster when multicluster mode enabled, use in any other cases may cause
// unexpected behavior
type Dispatcher interface {
Dispatch(w http.ResponseWriter, req *http.Request, handler http.Handler)
}
......@@ -127,35 +129,32 @@ func (c *clusterDispatch) Dispatch(w http.ResponseWriter, req *http.Request, han
transport := http.DefaultTransport
// change request host to actually cluster hosts
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
// if cluster connection is direct and kubesphere apiserver endpoint is empty
// we use kube-apiserver proxy way
if cluster.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeDirect &&
len(cluster.Spec.Connection.KubeSphereAPIEndpoint) == 0 {
u.Scheme = innCluster.kubernetesURL.Scheme
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"))
} else {
// everything else goes to ks-apiserver, since our ks-apiserver has the ability to proxy kube-apiserver requests
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 way
if cluster.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeDirect &&
len(cluster.Spec.Connection.KubeSphereAPIEndpoint) == 0 {
u.Scheme = innCluster.kubernetesURL.Scheme
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"))
}
}
httpProxy := proxy.NewUpgradeAwareHandler(&u, transport, false, false, c)
......
......@@ -243,7 +243,7 @@ func (h *handler) ValidateCluster(request *restful.Request, response *restful.Re
return
}
_, err = validateKubeSphereAPIServer(cluster.Spec.Connection.KubeSphereAPIEndpoint)
_, err = validateKubeSphereAPIServer(cluster.Spec.Connection.KubeSphereAPIEndpoint, cluster.Spec.Connection.KubeConfig)
if err != nil {
api.HandleBadRequest(response, request, fmt.Errorf("unable validate kubesphere endpoint, %v", err))
return
......@@ -286,18 +286,38 @@ func loadKubeConfigFromBytes(kubeconfig []byte) (*rest.Config, error) {
}
// validateKubeSphereAPIServer uses version api to check the accessibility
func validateKubeSphereAPIServer(ksEndpoint string) (*version.Info, error) {
_, err := url.Parse(ksEndpoint)
if err != nil {
return nil, err
// If ksEndpoint is empty, use
func validateKubeSphereAPIServer(ksEndpoint string, kubeconfig []byte) (*version.Info, error) {
if len(ksEndpoint) == 0 && len(kubeconfig) == 0 {
return nil, fmt.Errorf("neither kubesphere api endpoint nor kubeconfig was provided")
}
path := fmt.Sprintf("%s/kapis/version", ksEndpoint)
client := http.Client{
Timeout: defaultTimeout,
}
path := fmt.Sprintf("%s/kapis/version", ksEndpoint)
if len(ksEndpoint) != 0 {
_, err := url.Parse(ksEndpoint)
if err != nil {
return nil, err
}
} else {
config, err := loadKubeConfigFromBytes(kubeconfig)
if err != nil {
return nil, err
}
transport, err := rest.TransportFor(config)
if err != nil {
return nil, err
}
client.Transport = transport
path = fmt.Sprintf("%s/api/v1/namespaces/kubesphere-system/services/:ks-apiserver:/proxy/kapis/version", config.Host)
}
response, err := client.Get(path)
if err != nil {
return nil, err
......
......@@ -263,7 +263,7 @@ func TestValidateKubeSphereEndpoint(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(endpoint))
defer svr.Close()
got, err := validateKubeSphereAPIServer(svr.URL)
got, err := validateKubeSphereAPIServer(svr.URL, nil)
if err != nil {
t.Fatal(err)
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册