Merge pull request #2230 from wansir/master

fix: multi-cluster proxy authentication
This commit is contained in:
KubeSphere CI Bot
2020-06-19 17:38:41 +08:00
committed by GitHub
7 changed files with 163 additions and 37 deletions

View File

@@ -38,6 +38,7 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory"
authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/path"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/proxy"
unionauthorizer "kubesphere.io/kubesphere/pkg/apiserver/authorization/union"
apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
"kubesphere.io/kubesphere/pkg/apiserver/dispatch"
@@ -267,7 +268,7 @@ func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) {
excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*", "/kapis/version"}
pathAuthorizer, _ := path.NewAuthorizer(excludedPaths)
amOperator := am.NewReadOnlyOperator(s.InformerFactory)
authorizers = unionauthorizer.New(pathAuthorizer, authorizerfactory.NewRBACAuthorizer(amOperator))
authorizers = unionauthorizer.New(pathAuthorizer, proxy.NewAuthorizer(s.Config.MultiClusterOptions.Enable), authorizerfactory.NewRBACAuthorizer(amOperator))
}
handler = filters.WithAuthorization(handler, authorizers)

View File

@@ -230,37 +230,6 @@ func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes,
}
}
if requestAttributes.GetResourceScope() == request.ClusterScope || requestAttributes.GetResourceScope() == request.NamespaceScope {
if clusterRoleBindings, err := r.am.ListClusterRoleBindings(""); err != nil {
if !visitor(nil, "", nil, err) {
return
}
} else {
sourceDescriber := &clusterRoleBindingDescriber{}
for _, clusterRoleBinding := range clusterRoleBindings {
subjectIndex, applies := appliesTo(requestAttributes.GetUser(), clusterRoleBinding.Subjects, "")
if !applies {
continue
}
regoPolicy, rules, err := r.am.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "")
if err != nil {
visitor(nil, "", nil, err)
continue
}
sourceDescriber.binding = clusterRoleBinding
sourceDescriber.subject = &clusterRoleBinding.Subjects[subjectIndex]
if !visitor(sourceDescriber, regoPolicy, nil, nil) {
return
}
for i := range rules {
if !visitor(sourceDescriber, "", &rules[i], nil) {
return
}
}
}
}
}
if requestAttributes.GetResourceScope() == request.WorkspaceScope || requestAttributes.GetResourceScope() == request.NamespaceScope {
var workspace string
@@ -338,6 +307,35 @@ func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes,
}
}
}
if clusterRoleBindings, err := r.am.ListClusterRoleBindings(""); err != nil {
if !visitor(nil, "", nil, err) {
return
}
} else {
sourceDescriber := &clusterRoleBindingDescriber{}
for _, clusterRoleBinding := range clusterRoleBindings {
subjectIndex, applies := appliesTo(requestAttributes.GetUser(), clusterRoleBinding.Subjects, "")
if !applies {
continue
}
regoPolicy, rules, err := r.am.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "")
if err != nil {
visitor(nil, "", nil, err)
continue
}
sourceDescriber.binding = clusterRoleBinding
sourceDescriber.subject = &clusterRoleBinding.Subjects[subjectIndex]
if !visitor(sourceDescriber, regoPolicy, nil, nil) {
return
}
for i := range rules {
if !visitor(sourceDescriber, "", &rules[i], nil) {
return
}
}
}
}
}
// appliesTo returns whether any of the bindingSubjects applies to the specified subject,

View File

@@ -0,0 +1,17 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package proxy

View File

@@ -0,0 +1,33 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package proxy
import (
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
)
// NewAuthorizer returns an authorizer which accepts cluster proxy request.
// If multi-cluster mode is enabled, request should authorize by target apiserver.
func NewAuthorizer(multiClusterEnabled bool) authorizer.Authorizer {
return authorizer.AuthorizerFunc(func(a authorizer.Attributes) (authorizer.Decision, string, error) {
// in multi cluster mode, the request will be dispatch.
if multiClusterEnabled && a.GetCluster() != "" {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionNoOpinion, "", nil
})
}

View File

@@ -0,0 +1,80 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package proxy
import (
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"testing"
)
func TestNewAuthorizer(t *testing.T) {
tests := []struct {
multiClusterEnabled bool
request authorizer.AttributesRecord
expectResult authorizer.Decision
}{
{
multiClusterEnabled: false,
request: authorizer.AttributesRecord{
Workspace: "ws",
Namespace: "ns",
KubernetesRequest: false,
ResourceRequest: false,
},
expectResult: authorizer.DecisionNoOpinion,
},
{
multiClusterEnabled: false,
request: authorizer.AttributesRecord{
Cluster: "cluster1",
Workspace: "ws",
Namespace: "ns",
KubernetesRequest: false,
ResourceRequest: false,
},
expectResult: authorizer.DecisionNoOpinion,
},
{
multiClusterEnabled: true,
request: authorizer.AttributesRecord{
Cluster: "cluster1",
Workspace: "ws",
Namespace: "ns",
KubernetesRequest: false,
ResourceRequest: false,
},
expectResult: authorizer.DecisionAllow,
},
{
multiClusterEnabled: true,
request: authorizer.AttributesRecord{
Workspace: "ws",
Namespace: "ns",
KubernetesRequest: false,
ResourceRequest: false,
},
expectResult: authorizer.DecisionNoOpinion,
},
}
for i, test := range tests {
a := NewAuthorizer(test.multiClusterEnabled)
result, _, _ := a.Authorize(test.request)
if result != test.expectResult {
t.Errorf("case %d, got %#v, expected %#v", i, result, test.expectResult)
}
}
}

View File

@@ -299,10 +299,6 @@ const (
func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string {
if request.Cluster != "" {
return ClusterScope
}
if request.Namespace != "" {
return NamespaceScope
}
@@ -311,5 +307,5 @@ func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string {
return WorkspaceScope
}
return GlobalScope
return ClusterScope
}

View File

@@ -447,6 +447,7 @@ func (r *Controller) initRoles(workspace *tenantv1alpha2.WorkspaceTemplate) erro
}
// make sure workspace label always exist
role.Labels[tenantv1alpha1.WorkspaceLabel] = workspace.Name
role.Name = roleName
old, err := r.workspaceRoleLister.Get(roleName)
if err != nil {
if errors.IsNotFound(err) {