mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	add admin,edit,view roles
This commit is contained in:
		@@ -58,6 +58,7 @@ func init() {
 | 
			
		||||
	addControllerRole(rbac.ClusterRole{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "replication-controller"},
 | 
			
		||||
		Rules: []rbac.PolicyRule{
 | 
			
		||||
			// 1.0 controllers needed get, update, so without these old controllers break on new servers
 | 
			
		||||
			rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(),
 | 
			
		||||
			rbac.NewRule("update").Groups(legacyGroup).Resources("replicationcontrollers/status").RuleOrDie(),
 | 
			
		||||
			rbac.NewRule("list", "watch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
 | 
			
		||||
 
 | 
			
		||||
@@ -23,10 +23,22 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	readWrite = []string{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"}
 | 
			
		||||
	read      = []string{"get", "list", "watch"}
 | 
			
		||||
	ReadWrite = []string{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"}
 | 
			
		||||
	Read      = []string{"get", "list", "watch"}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
	legacyGroup = ""
 | 
			
		||||
const (
 | 
			
		||||
	legacyGroup         = ""
 | 
			
		||||
	appsGroup           = "apps"
 | 
			
		||||
	authenticationGroup = "authentication.k8s.io"
 | 
			
		||||
	authorizationGroup  = "authorization.k8s.io"
 | 
			
		||||
	autoscalingGroup    = "autoscaling"
 | 
			
		||||
	batchGroup          = "batch"
 | 
			
		||||
	certificatesGroup   = "certificates.k8s.io"
 | 
			
		||||
	extensionsGroup     = "extensions"
 | 
			
		||||
	policyGroup         = "policy"
 | 
			
		||||
	rbacGroup           = "rbac.authorization.k8s.io"
 | 
			
		||||
	storageGroup        = "storage.k8s.io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ClusterRoles returns the cluster roles to bootstrap an API server with
 | 
			
		||||
@@ -47,6 +59,92 @@ func ClusterRoles() []rbac.ClusterRole {
 | 
			
		||||
				rbac.NewRule("get").URLs("/version", "/api", "/api/*", "/apis", "/apis/*").RuleOrDie(),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// a role which provides minimal resource access to allow a "normal" user to learn information about themselves
 | 
			
		||||
			ObjectMeta: api.ObjectMeta{Name: "system:basic-user"},
 | 
			
		||||
			Rules: []rbac.PolicyRule{
 | 
			
		||||
				// TODO add future selfsubjectrulesreview, project request APIs, project listing APIs
 | 
			
		||||
				rbac.NewRule("create").Groups(authorizationGroup).Resources("selfsubjectaccessreviews").RuleOrDie(),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			// a role for a namespace level admin.  It is `edit` plus the power to grant permissions to other users.
 | 
			
		||||
			ObjectMeta: api.ObjectMeta{Name: "admin"},
 | 
			
		||||
			Rules: []rbac.PolicyRule{
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
 | 
			
		||||
					"services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(),
 | 
			
		||||
				rbac.NewRule(Read...).Groups(legacyGroup).Resources("limitranges", "resourcequotas", "bindings", "events",
 | 
			
		||||
					"pods/status", "resourcequotas/status", "namespaces/status", "replicationcontrollers/status", "pods/log").RuleOrDie(),
 | 
			
		||||
				// read access to namespaces at the namespace scope means you can read *this* namespace.  This can be used as an
 | 
			
		||||
				// indicator of which namespaces you have access to.
 | 
			
		||||
				rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(),
 | 
			
		||||
				rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("petsets").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "scheduledjobs").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("jobs", "daemonsets", "horizontalpodautoscalers",
 | 
			
		||||
					"replicationcontrollers/scale", "replicasets", "replicasets/scale", "deployments", "deployments/scale").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				// additional admin powers
 | 
			
		||||
				rbac.NewRule("create").Groups(authorizationGroup).Resources("localsubjectaccessreviews").RuleOrDie(),
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(rbacGroup).Resources("roles", "rolebindings").RuleOrDie(),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// a role for a namespace level editor.  It grants access to all user level actions in a namespace.
 | 
			
		||||
			// It does not grant powers for "privileged" resources which are domain of the system: `/status`
 | 
			
		||||
			// subresources or `quota`/`limits` which are used to control namespaces
 | 
			
		||||
			ObjectMeta: api.ObjectMeta{Name: "edit"},
 | 
			
		||||
			Rules: []rbac.PolicyRule{
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
 | 
			
		||||
					"services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(),
 | 
			
		||||
				rbac.NewRule(Read...).Groups(legacyGroup).Resources("limitranges", "resourcequotas", "bindings", "events",
 | 
			
		||||
					"pods/status", "resourcequotas/status", "namespaces/status", "replicationcontrollers/status", "pods/log").RuleOrDie(),
 | 
			
		||||
				// read access to namespaces at the namespace scope means you can read *this* namespace.  This can be used as an
 | 
			
		||||
				// indicator of which namespaces you have access to.
 | 
			
		||||
				rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(),
 | 
			
		||||
				rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("petsets").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "scheduledjobs").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("jobs", "daemonsets", "horizontalpodautoscalers",
 | 
			
		||||
					"replicationcontrollers/scale", "replicasets", "replicasets/scale", "deployments", "deployments/scale").RuleOrDie(),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// a role for namespace level viewing.  It grants Read-only access to non-escalating resources in
 | 
			
		||||
			// a namespace.
 | 
			
		||||
			ObjectMeta: api.ObjectMeta{Name: "view"},
 | 
			
		||||
			Rules: []rbac.PolicyRule{
 | 
			
		||||
				rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
 | 
			
		||||
					"services", "endpoints", "persistentvolumeclaims", "configmaps").RuleOrDie(),
 | 
			
		||||
				rbac.NewRule(Read...).Groups(legacyGroup).Resources("limitranges", "resourcequotas", "bindings", "events",
 | 
			
		||||
					"pods/status", "resourcequotas/status", "namespaces/status", "replicationcontrollers/status", "pods/log").RuleOrDie(),
 | 
			
		||||
				// read access to namespaces at the namespace scope means you can read *this* namespace.  This can be used as an
 | 
			
		||||
				// indicator of which namespaces you have access to.
 | 
			
		||||
				rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(Read...).Groups(appsGroup).Resources("petsets").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(Read...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(Read...).Groups(batchGroup).Resources("jobs", "scheduledjobs").RuleOrDie(),
 | 
			
		||||
 | 
			
		||||
				rbac.NewRule(Read...).Groups(extensionsGroup).Resources("jobs", "daemonsets", "horizontalpodautoscalers",
 | 
			
		||||
					"replicationcontrollers/scale", "replicasets", "replicasets/scale", "deployments", "deployments/scale").RuleOrDie(),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -55,5 +153,6 @@ func ClusterRoleBindings() []rbac.ClusterRoleBinding {
 | 
			
		||||
	return []rbac.ClusterRoleBinding{
 | 
			
		||||
		rbac.NewClusterBinding("cluster-admin").Groups(user.SystemPrivilegedGroup).BindingOrDie(),
 | 
			
		||||
		rbac.NewClusterBinding("system:discovery").Groups(user.AllAuthenticated, user.AllUnauthenticated).BindingOrDie(),
 | 
			
		||||
		rbac.NewClusterBinding("system:basic-user").Groups(user.AllAuthenticated, user.AllUnauthenticated).BindingOrDie(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										138
									
								
								plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2016 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 bootstrappolicy_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	rbac "k8s.io/kubernetes/pkg/apis/rbac"
 | 
			
		||||
	rbacvalidation "k8s.io/kubernetes/pkg/apis/rbac/validation"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
			
		||||
	"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// semanticRoles is a few enumerated roles for which the relationships are well established
 | 
			
		||||
// and we want to maintain symmetric roles
 | 
			
		||||
type semanticRoles struct {
 | 
			
		||||
	admin *rbac.ClusterRole
 | 
			
		||||
	edit  *rbac.ClusterRole
 | 
			
		||||
	view  *rbac.ClusterRole
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getSemanticRoles(roles []rbac.ClusterRole) semanticRoles {
 | 
			
		||||
	ret := semanticRoles{}
 | 
			
		||||
	for i := range roles {
 | 
			
		||||
		role := roles[i]
 | 
			
		||||
		switch role.Name {
 | 
			
		||||
		case "admin":
 | 
			
		||||
			ret.admin = &role
 | 
			
		||||
		case "edit":
 | 
			
		||||
			ret.edit = &role
 | 
			
		||||
		case "view":
 | 
			
		||||
			ret.view = &role
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Some roles should always cover others
 | 
			
		||||
func TestCovers(t *testing.T) {
 | 
			
		||||
	semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
 | 
			
		||||
 | 
			
		||||
	if covers, miss := rbacvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.edit.Rules); !covers {
 | 
			
		||||
		t.Errorf("failed to cover: %#v", miss)
 | 
			
		||||
	}
 | 
			
		||||
	if covers, miss := rbacvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.view.Rules); !covers {
 | 
			
		||||
		t.Errorf("failed to cover: %#v", miss)
 | 
			
		||||
	}
 | 
			
		||||
	if covers, miss := rbacvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.view.Rules); !covers {
 | 
			
		||||
		t.Errorf("failed to cover: %#v", miss)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// additionalAdminPowers is the list of powers that we expect to be different than the editor role.
 | 
			
		||||
// one resource per rule to make the "does not already contain" check easy
 | 
			
		||||
var additionalAdminPowers = []rbac.PolicyRule{
 | 
			
		||||
	rbac.NewRule("create").Groups("authorization.k8s.io").Resources("localsubjectaccessreviews").RuleOrDie(),
 | 
			
		||||
	rbac.NewRule(bootstrappolicy.ReadWrite...).Groups("rbac.authorization.k8s.io").Resources("rolebindings").RuleOrDie(),
 | 
			
		||||
	rbac.NewRule(bootstrappolicy.ReadWrite...).Groups("rbac.authorization.k8s.io").Resources("roles").RuleOrDie(),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAdminEditRelationship(t *testing.T) {
 | 
			
		||||
	semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
 | 
			
		||||
 | 
			
		||||
	// confirm that the edit role doesn't already have extra powers
 | 
			
		||||
	for _, rule := range additionalAdminPowers {
 | 
			
		||||
		if covers, _ := rbacvalidation.Covers(semanticRoles.edit.Rules, []rbac.PolicyRule{rule}); covers {
 | 
			
		||||
			t.Errorf("edit has extra powers: %#v", rule)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	semanticRoles.edit.Rules = append(semanticRoles.edit.Rules, additionalAdminPowers...)
 | 
			
		||||
 | 
			
		||||
	// at this point, we should have a two way covers relationship
 | 
			
		||||
	if covers, miss := rbacvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.edit.Rules); !covers {
 | 
			
		||||
		t.Errorf("admin has lost rules for: %#v", miss)
 | 
			
		||||
	}
 | 
			
		||||
	if covers, miss := rbacvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.admin.Rules); !covers {
 | 
			
		||||
		t.Errorf("edit is missing rules for: %#v\nIf these should only be admin powers, add them to the list.  Otherwise, add them to the edit role.", miss)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// viewEscalatingNamespaceResources is the list of rules that would allow privilege escalation attacks based on
 | 
			
		||||
// ability to view (GET) them
 | 
			
		||||
var viewEscalatingNamespaceResources = []rbac.PolicyRule{
 | 
			
		||||
	rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/attach").RuleOrDie(),
 | 
			
		||||
	rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/proxy").RuleOrDie(),
 | 
			
		||||
	rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/exec").RuleOrDie(),
 | 
			
		||||
	rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/portforward").RuleOrDie(),
 | 
			
		||||
	rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("secrets").RuleOrDie(),
 | 
			
		||||
	rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("services/proxy").RuleOrDie(),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestEditViewRelationship(t *testing.T) {
 | 
			
		||||
	readVerbs := sets.NewString(bootstrappolicy.Read...)
 | 
			
		||||
	semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
 | 
			
		||||
 | 
			
		||||
	// modify the edit role rules to make then read-only for comparison against view role rules
 | 
			
		||||
	for i := range semanticRoles.edit.Rules {
 | 
			
		||||
		rule := semanticRoles.edit.Rules[i]
 | 
			
		||||
		remainingVerbs := []string{}
 | 
			
		||||
		for _, verb := range rule.Verbs {
 | 
			
		||||
			if readVerbs.Has(verb) {
 | 
			
		||||
				remainingVerbs = append(remainingVerbs, verb)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		rule.Verbs = remainingVerbs
 | 
			
		||||
		semanticRoles.edit.Rules[i] = rule
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// confirm that the view role doesn't already have extra powers
 | 
			
		||||
	for _, rule := range viewEscalatingNamespaceResources {
 | 
			
		||||
		if covers, _ := rbacvalidation.Covers(semanticRoles.view.Rules, []rbac.PolicyRule{rule}); covers {
 | 
			
		||||
			t.Errorf("view has extra powers: %#v", rule)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	semanticRoles.view.Rules = append(semanticRoles.view.Rules, viewEscalatingNamespaceResources...)
 | 
			
		||||
 | 
			
		||||
	// at this point, we should have a two way covers relationship
 | 
			
		||||
	if covers, miss := rbacvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.view.Rules); !covers {
 | 
			
		||||
		t.Errorf("edit has lost rules for: %#v", miss)
 | 
			
		||||
	}
 | 
			
		||||
	if covers, miss := rbacvalidation.Covers(semanticRoles.view.Rules, semanticRoles.edit.Rules); !covers {
 | 
			
		||||
		t.Errorf("view is missing rules for: %#v\nIf these are escalating powers, add them to the list.  Otherwise, add them to the view role.", miss)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user