mirror of https://github.com/k3s-io/k3s
aggregate admin from edit and view to ensure coverage
parent
cef2d325ee
commit
3c1495d0a4
|
@ -28,6 +28,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
Write = []string{"create", "update", "patch", "delete", "deletecollection"}
|
||||
ReadWrite = []string{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"}
|
||||
Read = []string{"get", "list", "watch"}
|
||||
ReadUpdate = []string{"get", "list", "watch", "update", "patch"}
|
||||
|
@ -203,59 +204,36 @@ func ClusterRoles() []rbacv1.ClusterRole {
|
|||
// a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users.
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "admin"},
|
||||
AggregationRule: &rbacv1.AggregationRule{
|
||||
ClusterRoleSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-admin": "true"}}},
|
||||
ClusterRoleSelectors: []metav1.LabelSelector{
|
||||
{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-admin": "true"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// 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: metav1.ObjectMeta{Name: "edit"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "edit", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-admin": "true"}},
|
||||
AggregationRule: &rbacv1.AggregationRule{
|
||||
ClusterRoleSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-edit": "true"}}},
|
||||
ClusterRoleSelectors: []metav1.LabelSelector{
|
||||
{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-edit": "true"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role for namespace level viewing. It grants Read-only access to non-escalating resources in
|
||||
// a namespace.
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "view"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "view", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-edit": "true"}},
|
||||
AggregationRule: &rbacv1.AggregationRule{
|
||||
ClusterRoleSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-view": "true"}}},
|
||||
ClusterRoleSelectors: []metav1.LabelSelector{
|
||||
{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-view": "true"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users.
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-admin", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-admin": "true"}},
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
|
||||
"services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(),
|
||||
rbacv1helpers.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.
|
||||
rbacv1helpers.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(),
|
||||
rbacv1helpers.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(appsGroup).Resources(
|
||||
"statefulsets", "statefulsets/scale",
|
||||
"daemonsets",
|
||||
"deployments", "deployments/scale", "deployments/rollback",
|
||||
"replicasets", "replicasets/scale").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets",
|
||||
"deployments", "deployments/scale", "deployments/rollback", "ingresses",
|
||||
"replicasets", "replicasets/scale", "replicationcontrollers/scale",
|
||||
"networkpolicies").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(networkingGroup).Resources("networkpolicies").RuleOrDie(),
|
||||
|
||||
// additional admin powers
|
||||
rbacv1helpers.NewRule("create").Groups(authorizationGroup).Resources("localsubjectaccessreviews").RuleOrDie(),
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(rbacGroup).Resources("roles", "rolebindings").RuleOrDie(),
|
||||
|
@ -267,34 +245,32 @@ func ClusterRoles() []rbacv1.ClusterRole {
|
|||
// subresources or `quota`/`limits` which are used to control namespaces
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-edit", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-edit": "true"}},
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
|
||||
"services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(),
|
||||
rbacv1helpers.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.
|
||||
rbacv1helpers.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(),
|
||||
// Allow read on escalating resources
|
||||
rbacv1helpers.NewRule(Read...).Groups(legacyGroup).Resources("pods/attach", "pods/proxy", "pods/exec", "pods/portforward", "secrets", "services/proxy").RuleOrDie(),
|
||||
rbacv1helpers.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(appsGroup).Resources(
|
||||
rbacv1helpers.NewRule(Write...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
|
||||
rbacv1helpers.NewRule(Write...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
|
||||
"services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(Write...).Groups(appsGroup).Resources(
|
||||
"statefulsets", "statefulsets/scale",
|
||||
"daemonsets",
|
||||
"deployments", "deployments/scale", "deployments/rollback",
|
||||
"replicasets", "replicasets/scale").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
|
||||
rbacv1helpers.NewRule(Write...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs").RuleOrDie(),
|
||||
rbacv1helpers.NewRule(Write...).Groups(batchGroup).Resources("jobs", "cronjobs").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets",
|
||||
rbacv1helpers.NewRule(Write...).Groups(extensionsGroup).Resources("daemonsets",
|
||||
"deployments", "deployments/scale", "deployments/rollback", "ingresses",
|
||||
"replicasets", "replicasets/scale", "replicationcontrollers/scale",
|
||||
"networkpolicies").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(),
|
||||
rbacv1helpers.NewRule(Write...).Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(),
|
||||
|
||||
rbacv1helpers.NewRule(ReadWrite...).Groups(networkingGroup).Resources("networkpolicies").RuleOrDie(),
|
||||
rbacv1helpers.NewRule(Write...).Groups(networkingGroup).Resources("networkpolicies").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -64,49 +64,6 @@ func getSemanticRoles(roles []rbacv1.ClusterRole) semanticRoles {
|
|||
return ret
|
||||
}
|
||||
|
||||
// Some roles should always cover others
|
||||
func TestCovers(t *testing.T) {
|
||||
semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
|
||||
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.edit.Rules); !covers {
|
||||
t.Errorf("failed to cover: %#v", miss)
|
||||
}
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.view.Rules); !covers {
|
||||
t.Errorf("failed to cover: %#v", miss)
|
||||
}
|
||||
if covers, miss := rbacregistryvalidation.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 = []rbacv1.PolicyRule{
|
||||
rbacv1helpers.NewRule("create").Groups("authorization.k8s.io").Resources("localsubjectaccessreviews").RuleOrDie(),
|
||||
rbacv1helpers.NewRule(bootstrappolicy.ReadWrite...).Groups("rbac.authorization.k8s.io").Resources("rolebindings").RuleOrDie(),
|
||||
rbacv1helpers.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, _ := rbacregistryvalidation.Covers(semanticRoles.edit.Rules, []rbacv1.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 := rbacregistryvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.edit.Rules); !covers {
|
||||
t.Errorf("admin has lost rules for: %#v", miss)
|
||||
}
|
||||
if covers, miss := rbacregistryvalidation.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 = []rbacv1.PolicyRule{
|
||||
|
@ -156,14 +113,6 @@ func TestEditViewRelationship(t *testing.T) {
|
|||
}
|
||||
}
|
||||
semanticRoles.view.Rules = append(semanticRoles.view.Rules, ungettableResources...)
|
||||
|
||||
// at this point, we should have a two way covers relationship
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.view.Rules); !covers {
|
||||
t.Errorf("edit has lost rules for: %#v", miss)
|
||||
}
|
||||
if covers, miss := rbacregistryvalidation.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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBootstrapNamespaceRoles(t *testing.T) {
|
||||
|
|
|
@ -46,6 +46,7 @@ items:
|
|||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
rbac.authorization.k8s.io/aggregate-to-admin: "true"
|
||||
name: edit
|
||||
rules: null
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
|
@ -59,168 +60,6 @@ items:
|
|||
rbac.authorization.k8s.io/aggregate-to-admin: "true"
|
||||
name: system:aggregate-to-admin
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/attach
|
||||
- pods/exec
|
||||
- pods/portforward
|
||||
- pods/proxy
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
- endpoints
|
||||
- persistentvolumeclaims
|
||||
- replicationcontrollers
|
||||
- replicationcontrollers/scale
|
||||
- secrets
|
||||
- serviceaccounts
|
||||
- services
|
||||
- services/proxy
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- bindings
|
||||
- events
|
||||
- limitranges
|
||||
- namespaces/status
|
||||
- pods/log
|
||||
- pods/status
|
||||
- replicationcontrollers/status
|
||||
- resourcequotas
|
||||
- resourcequotas/status
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- namespaces
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- impersonate
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- daemonsets
|
||||
- deployments
|
||||
- deployments/rollback
|
||||
- deployments/scale
|
||||
- replicasets
|
||||
- replicasets/scale
|
||||
- statefulsets
|
||||
- statefulsets/scale
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- autoscaling
|
||||
resources:
|
||||
- horizontalpodautoscalers
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- cronjobs
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- daemonsets
|
||||
- deployments
|
||||
- deployments/rollback
|
||||
- deployments/scale
|
||||
- ingresses
|
||||
- networkpolicies
|
||||
- replicasets
|
||||
- replicasets/scale
|
||||
- replicationcontrollers/scale
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- policy
|
||||
resources:
|
||||
- poddisruptionbudgets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
resources:
|
||||
- networkpolicies
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- authorization.k8s.io
|
||||
resources:
|
||||
|
@ -252,6 +91,25 @@ items:
|
|||
rbac.authorization.k8s.io/aggregate-to-edit: "true"
|
||||
name: system:aggregate-to-edit
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods/attach
|
||||
- pods/exec
|
||||
- pods/portforward
|
||||
- pods/proxy
|
||||
- secrets
|
||||
- services/proxy
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- impersonate
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
|
@ -264,11 +122,8 @@ items:
|
|||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
|
@ -285,41 +140,8 @@ items:
|
|||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- bindings
|
||||
- events
|
||||
- limitranges
|
||||
- namespaces/status
|
||||
- pods/log
|
||||
- pods/status
|
||||
- replicationcontrollers/status
|
||||
- resourcequotas
|
||||
- resourcequotas/status
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- namespaces
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- impersonate
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
|
@ -335,11 +157,8 @@ items:
|
|||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- autoscaling
|
||||
resources:
|
||||
|
@ -348,11 +167,8 @@ items:
|
|||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
|
@ -362,11 +178,8 @@ items:
|
|||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
|
@ -383,11 +196,8 @@ items:
|
|||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- policy
|
||||
resources:
|
||||
|
@ -396,11 +206,8 @@ items:
|
|||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
resources:
|
||||
|
@ -409,11 +216,8 @@ items:
|
|||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
|
@ -1324,6 +1128,7 @@ items:
|
|||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
rbac.authorization.k8s.io/aggregate-to-edit: "true"
|
||||
name: view
|
||||
rules: null
|
||||
kind: List
|
||||
|
|
Loading…
Reference in New Issue