mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 07:47:56 +00:00
add admin,edit,view roles
This commit is contained in:
parent
928b8cbdb8
commit
d56a27f130
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user