handle clusterrole migration

This commit is contained in:
David Eads 2017-10-20 11:01:52 -04:00
parent e52383c486
commit f34fb9b0ab
14 changed files with 480 additions and 132 deletions

View File

@ -42,7 +42,10 @@ func NewStorage(s rest.StandardStorage, ruleResolver rbacregistryvalidation.Auth
return &Storage{s, ruleResolver} return &Storage{s, ruleResolver}
} }
var fullAuthority = []rbac.PolicyRule{rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie()} var fullAuthority = []rbac.PolicyRule{
rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(),
rbac.NewRule("*").URLs("*").RuleOrDie(),
}
func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) { if rbacregistry.EscalationAllowed(ctx) {

View File

@ -18,6 +18,7 @@ go_test(
"//pkg/apis/core/helper:go_default_library", "//pkg/apis/core/helper:go_default_library",
"//pkg/apis/rbac:go_default_library", "//pkg/apis/rbac:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
], ],
) )

View File

@ -66,6 +66,14 @@ func (o ClusterRoleRuleOwner) SetRules(in []rbac.PolicyRule) {
o.ClusterRole.Rules = in o.ClusterRole.Rules = in
} }
func (o ClusterRoleRuleOwner) GetAggregationRule() *rbac.AggregationRule {
return o.ClusterRole.AggregationRule
}
func (o ClusterRoleRuleOwner) SetAggregationRule(in *rbac.AggregationRule) {
o.ClusterRole.AggregationRule = in
}
type ClusterRoleModifier struct { type ClusterRoleModifier struct {
Client internalversion.ClusterRoleInterface Client internalversion.ClusterRoleInterface
} }

View File

@ -20,7 +20,9 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/registry/rbac/validation" "k8s.io/kubernetes/pkg/registry/rbac/validation"
@ -51,6 +53,8 @@ type RuleOwner interface {
SetAnnotations(map[string]string) SetAnnotations(map[string]string)
GetRules() []rbac.PolicyRule GetRules() []rbac.PolicyRule
SetRules([]rbac.PolicyRule) SetRules([]rbac.PolicyRule)
GetAggregationRule() *rbac.AggregationRule
SetAggregationRule(*rbac.AggregationRule)
DeepCopyRuleOwner() RuleOwner DeepCopyRuleOwner() RuleOwner
} }
@ -75,6 +79,11 @@ type ReconcileClusterRoleResult struct {
// ExtraRules contains extra permissions the currently persisted role had // ExtraRules contains extra permissions the currently persisted role had
ExtraRules []rbac.PolicyRule ExtraRules []rbac.PolicyRule
// MissingAggregationRuleSelectors contains expected selectors that were missing from the currently persisted role
MissingAggregationRuleSelectors []metav1.LabelSelector
// ExtraAggregationRuleSelectors contains extra selectors the currently persisted role had
ExtraAggregationRuleSelectors []metav1.LabelSelector
// Operation is the API operation required to reconcile. // Operation is the API operation required to reconcile.
// If no reconciliation was needed, it is set to ReconcileNone. // If no reconciliation was needed, it is set to ReconcileNone.
// If options.Confirm == false, the reconcile was in dry-run mode, so the operation was not performed. // If options.Confirm == false, the reconcile was in dry-run mode, so the operation was not performed.
@ -101,10 +110,15 @@ func (o *ReconcileRoleOptions) run(attempts int) (*ReconcileClusterRoleResult, e
existing, err := o.Client.Get(o.Role.GetNamespace(), o.Role.GetName()) existing, err := o.Client.Get(o.Role.GetNamespace(), o.Role.GetName())
switch { switch {
case errors.IsNotFound(err): case errors.IsNotFound(err):
aggregationRule := o.Role.GetAggregationRule()
if aggregationRule == nil {
aggregationRule = &rbac.AggregationRule{}
}
result = &ReconcileClusterRoleResult{ result = &ReconcileClusterRoleResult{
Role: o.Role, Role: o.Role,
MissingRules: o.Role.GetRules(), MissingRules: o.Role.GetRules(),
Operation: ReconcileCreate, MissingAggregationRuleSelectors: aggregationRule.ClusterRoleSelectors,
Operation: ReconcileCreate,
} }
case err != nil: case err != nil:
@ -195,6 +209,26 @@ func computeReconciledRole(existing, expected RuleOwner, removeExtraPermissions
result.Operation = ReconcileUpdate result.Operation = ReconcileUpdate
} }
// Compute extra and missing rules
_, result.ExtraAggregationRuleSelectors = aggregationRuleCovers(expected.GetAggregationRule(), existing.GetAggregationRule())
_, result.MissingAggregationRuleSelectors = aggregationRuleCovers(existing.GetAggregationRule(), expected.GetAggregationRule())
switch {
case !removeExtraPermissions && len(result.MissingAggregationRuleSelectors) > 0:
// add missing rules in the union case
aggregationRule := result.Role.GetAggregationRule()
if aggregationRule == nil {
aggregationRule = &rbac.AggregationRule{}
}
aggregationRule.ClusterRoleSelectors = append(aggregationRule.ClusterRoleSelectors, result.MissingAggregationRuleSelectors...)
result.Role.SetAggregationRule(aggregationRule)
result.Operation = ReconcileUpdate
case removeExtraPermissions && (len(result.MissingAggregationRuleSelectors) > 0 || len(result.ExtraAggregationRuleSelectors) > 0):
result.Role.SetAggregationRule(expected.GetAggregationRule())
result.Operation = ReconcileUpdate
}
return result, nil return result, nil
} }
@ -211,3 +245,37 @@ func merge(maps ...map[string]string) map[string]string {
} }
return output return output
} }
// aggregationRuleCovers determines whether or not the ownerSelectors cover the servantSelectors in terms of semantically
// equal label selectors.
// It returns whether or not the ownerSelectors cover and a list of the rules that the ownerSelectors do not cover.
func aggregationRuleCovers(ownerRule, servantRule *rbac.AggregationRule) (bool, []metav1.LabelSelector) {
switch {
case ownerRule == nil && servantRule == nil:
return true, []metav1.LabelSelector{}
case ownerRule == nil && servantRule != nil:
return false, servantRule.ClusterRoleSelectors
case ownerRule != nil && servantRule == nil:
return true, []metav1.LabelSelector{}
}
ownerSelectors := ownerRule.ClusterRoleSelectors
servantSelectors := servantRule.ClusterRoleSelectors
uncoveredSelectors := []metav1.LabelSelector{}
for _, servantSelector := range servantSelectors {
covered := false
for _, ownerSelector := range ownerSelectors {
if equality.Semantic.DeepEqual(ownerSelector, servantSelector) {
covered = true
break
}
}
if !covered {
uncoveredSelectors = append(uncoveredSelectors, servantSelector)
}
}
return (len(uncoveredSelectors) == 0), uncoveredSelectors
}

View File

@ -20,12 +20,16 @@ import (
"testing" "testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/kubernetes/pkg/apis/core/helper" "k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
) )
func role(rules []rbac.PolicyRule, labels map[string]string, annotations map[string]string) *rbac.ClusterRole { func role(rules []rbac.PolicyRule, labels map[string]string, annotations map[string]string) *rbac.ClusterRole {
return &rbac.ClusterRole{Rules: rules, ObjectMeta: metav1.ObjectMeta{Labels: labels, Annotations: annotations}} return &rbac.ClusterRole{
Rules: rules,
ObjectMeta: metav1.ObjectMeta{Labels: labels, Annotations: annotations},
}
} }
func rules(resources ...string) []rbac.PolicyRule { func rules(resources ...string) []rbac.PolicyRule {
@ -38,7 +42,7 @@ func rules(resources ...string) []rbac.PolicyRule {
type ss map[string]string type ss map[string]string
func TestComputeReconciledRole(t *testing.T) { func TestComputeReconciledRoleRules(t *testing.T) {
tests := map[string]struct { tests := map[string]struct {
expectedRole *rbac.ClusterRole expectedRole *rbac.ClusterRole
actualRole *rbac.ClusterRole actualRole *rbac.ClusterRole
@ -273,3 +277,96 @@ func TestComputeReconciledRole(t *testing.T) {
} }
} }
} }
func aggregatedRole(aggregationRule *rbac.AggregationRule) *rbac.ClusterRole {
return &rbac.ClusterRole{
AggregationRule: aggregationRule,
}
}
func aggregationrule(selectors []map[string]string) *rbac.AggregationRule {
ret := &rbac.AggregationRule{}
for _, selector := range selectors {
ret.ClusterRoleSelectors = append(ret.ClusterRoleSelectors,
metav1.LabelSelector{MatchLabels: selector})
}
return ret
}
func TestComputeReconciledRoleAggregationRules(t *testing.T) {
tests := map[string]struct {
expectedRole *rbac.ClusterRole
actualRole *rbac.ClusterRole
removeExtraPermissions bool
expectedReconciledRole *rbac.ClusterRole
expectedReconciliationNeeded bool
}{
"empty": {
expectedRole: aggregatedRole(&rbac.AggregationRule{}),
actualRole: aggregatedRole(nil),
removeExtraPermissions: true,
expectedReconciledRole: nil,
expectedReconciliationNeeded: false,
},
"empty-2": {
expectedRole: aggregatedRole(&rbac.AggregationRule{}),
actualRole: aggregatedRole(&rbac.AggregationRule{}),
removeExtraPermissions: true,
expectedReconciledRole: nil,
expectedReconciliationNeeded: false,
},
"match without union": {
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
removeExtraPermissions: true,
expectedReconciledRole: nil,
expectedReconciliationNeeded: false,
},
"match with union": {
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
removeExtraPermissions: false,
expectedReconciledRole: nil,
expectedReconciliationNeeded: false,
},
"different rules without union": {
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"alpha": "bravo"}})),
removeExtraPermissions: true,
expectedReconciledRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
expectedReconciliationNeeded: true,
},
"different rules with union": {
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"alpha": "bravo"}})),
removeExtraPermissions: false,
expectedReconciledRole: aggregatedRole(aggregationrule([]map[string]string{{"alpha": "bravo"}, {"foo": "bar"}})),
expectedReconciliationNeeded: true,
},
}
for k, tc := range tests {
actualRole := ClusterRoleRuleOwner{ClusterRole: tc.actualRole}
expectedRole := ClusterRoleRuleOwner{ClusterRole: tc.expectedRole}
result, err := computeReconciledRole(actualRole, expectedRole, tc.removeExtraPermissions)
if err != nil {
t.Errorf("%s: %v", k, err)
continue
}
reconciliationNeeded := result.Operation != ReconcileNone
if reconciliationNeeded != tc.expectedReconciliationNeeded {
t.Errorf("%s: Expected\n\t%v\ngot\n\t%v", k, tc.expectedReconciliationNeeded, reconciliationNeeded)
continue
}
if reconciliationNeeded && !helper.Semantic.DeepEqual(result.Role.(ClusterRoleRuleOwner).ClusterRole, tc.expectedReconciledRole) {
t.Errorf("%s: %v", k, diff.ObjectDiff(tc.expectedReconciledRole, result.Role.(ClusterRoleRuleOwner).ClusterRole))
}
}
}

View File

@ -69,6 +69,13 @@ func (o RoleRuleOwner) SetRules(in []rbac.PolicyRule) {
o.Role.Rules = in o.Role.Rules = in
} }
func (o RoleRuleOwner) GetAggregationRule() *rbac.AggregationRule {
return nil
}
func (o RoleRuleOwner) SetAggregationRule(in *rbac.AggregationRule) {
}
type RoleModifier struct { type RoleModifier struct {
Client internalversion.RolesGetter Client internalversion.RolesGetter
NamespaceClient core.NamespaceInterface NamespaceClient core.NamespaceInterface

View File

@ -26,6 +26,7 @@ import (
rbacapiv1 "k8s.io/api/rbac/v1" rbacapiv1 "k8s.io/api/rbac/v1"
rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1" rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1"
rbacapiv1beta1 "k8s.io/api/rbac/v1beta1" rbacapiv1beta1 "k8s.io/api/rbac/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@ -134,10 +135,11 @@ func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceCon
func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) { func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
policy := &PolicyData{ policy := &PolicyData{
ClusterRoles: append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...), ClusterRoles: append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...),
ClusterRoleBindings: append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...), ClusterRoleBindings: append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...),
Roles: bootstrappolicy.NamespaceRoles(), Roles: bootstrappolicy.NamespaceRoles(),
RoleBindings: bootstrappolicy.NamespaceRoleBindings(), RoleBindings: bootstrappolicy.NamespaceRoleBindings(),
ClusterRolesToAggregate: bootstrappolicy.ClusterRolesToAggregate(),
} }
return PostStartHookName, policy.EnsureRBACPolicy(), nil return PostStartHookName, policy.EnsureRBACPolicy(), nil
} }
@ -147,6 +149,8 @@ type PolicyData struct {
ClusterRoleBindings []rbac.ClusterRoleBinding ClusterRoleBindings []rbac.ClusterRoleBinding
Roles map[string][]rbac.Role Roles map[string][]rbac.Role
RoleBindings map[string][]rbac.RoleBinding RoleBindings map[string][]rbac.RoleBinding
// ClusterRolesToAggregate maps from previous clusterrole name to the new clusterrole name
ClusterRolesToAggregate map[string]string
} }
func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc { func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
@ -176,6 +180,13 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
return false, nil return false, nil
} }
// if the new cluster roles to aggregate do not yet exist, then we need to copy the old roles if they don't exist
// in new locations
if err := primeAggregatedClusterRoles(p.ClusterRolesToAggregate, clientset); err != nil {
utilruntime.HandleError(fmt.Errorf("unable to prime aggregated clusterroles: %v", err))
return false, nil
}
// ensure bootstrap roles are created or reconciled // ensure bootstrap roles are created or reconciled
for _, clusterRole := range p.ClusterRoles { for _, clusterRole := range p.ClusterRoles {
opts := reconciliation.ReconcileRoleOptions{ opts := reconciliation.ReconcileRoleOptions{
@ -310,3 +321,32 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
func (p RESTStorageProvider) GroupName() string { func (p RESTStorageProvider) GroupName() string {
return rbac.GroupName return rbac.GroupName
} }
// primeAggregatedClusterRoles copies roles that have transitioned to aggregated roles and may need to pick up changes
// that were done to the legacy roles.
func primeAggregatedClusterRoles(clusterRolesToAggregate map[string]string, clusterRoleClient rbacclient.ClusterRolesGetter) error {
for oldName, newName := range clusterRolesToAggregate {
_, err := clusterRoleClient.ClusterRoles().Get(newName, metav1.GetOptions{})
if err == nil {
continue
}
if !apierrors.IsNotFound(err) {
return err
}
existingRole, err := clusterRoleClient.ClusterRoles().Get(oldName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
continue
}
if err != nil {
return err
}
glog.V(1).Infof("migrating %v to %v", existingRole.Name, newName)
existingRole.Name = newName
if _, err := clusterRoleClient.ClusterRoles().Create(existingRole); err != nil && !apierrors.IsAlreadyExists(err) {
return err
}
}
return nil
}

View File

@ -68,6 +68,14 @@ func buildControllerRoles() ([]rbac.ClusterRole, []rbac.ClusterRoleBinding) {
eventsRule(), eventsRule(),
}, },
}) })
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "clusterrole-aggregation-controller"},
Rules: []rbac.PolicyRule{
// this controller must have full permissions to allow it to mutate any role in any way
rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(),
rbac.NewRule("*").URLs("*").RuleOrDie(),
},
})
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{ addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "cronjob-controller"}, ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "cronjob-controller"},
Rules: []rbac.PolicyRule{ Rules: []rbac.PolicyRule{

View File

@ -32,6 +32,7 @@ var rolesWithAllowStar = sets.NewString(
saRolePrefix+"generic-garbage-collector", saRolePrefix+"generic-garbage-collector",
saRolePrefix+"resourcequota-controller", saRolePrefix+"resourcequota-controller",
saRolePrefix+"horizontal-pod-autoscaler", saRolePrefix+"horizontal-pod-autoscaler",
saRolePrefix+"clusterrole-aggregation-controller",
) )
// TestNoStarsForControllers confirms that no controller role has star verbs, groups, // TestNoStarsForControllers confirms that no controller role has star verbs, groups,

View File

@ -176,6 +176,30 @@ func ClusterRoles() []rbac.ClusterRole {
{ {
// a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users. // a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users.
ObjectMeta: metav1.ObjectMeta{Name: "admin"}, ObjectMeta: metav1.ObjectMeta{Name: "admin"},
AggregationRule: &rbac.AggregationRule{
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"},
AggregationRule: &rbac.AggregationRule{
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"},
AggregationRule: &rbac.AggregationRule{
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: []rbac.PolicyRule{ 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("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
@ -211,7 +235,7 @@ func ClusterRoles() []rbac.ClusterRole {
// a role for a namespace level editor. It grants access to all user level actions in a namespace. // 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` // 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 // subresources or `quota`/`limits` which are used to control namespaces
ObjectMeta: metav1.ObjectMeta{Name: "edit"}, ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-edit", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-edit": "true"}},
Rules: []rbac.PolicyRule{ 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("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
@ -242,7 +266,7 @@ func ClusterRoles() []rbac.ClusterRole {
{ {
// a role for namespace level viewing. It grants Read-only access to non-escalating resources in // a role for namespace level viewing. It grants Read-only access to non-escalating resources in
// a namespace. // a namespace.
ObjectMeta: metav1.ObjectMeta{Name: "view"}, ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-view", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-view": "true"}},
Rules: []rbac.PolicyRule{ Rules: []rbac.PolicyRule{
rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
"services", "endpoints", "persistentvolumeclaims", "configmaps").RuleOrDie(), "services", "endpoints", "persistentvolumeclaims", "configmaps").RuleOrDie(),
@ -444,3 +468,11 @@ func ClusterRoleBindings() []rbac.ClusterRoleBinding {
return rolebindings return rolebindings
} }
func ClusterRolesToAggregate() map[string]string {
return map[string]string{
"admin": "system:aggregate-to-admin",
"edit": "system:aggregate-to-edit",
"view": "system:aggregate-to-view",
}
}

View File

@ -53,11 +53,11 @@ func getSemanticRoles(roles []rbac.ClusterRole) semanticRoles {
for i := range roles { for i := range roles {
role := roles[i] role := roles[i]
switch role.Name { switch role.Name {
case "admin": case "system:aggregate-to-admin":
ret.admin = &role ret.admin = &role
case "edit": case "system:aggregate-to-edit":
ret.edit = &role ret.edit = &role
case "view": case "system:aggregate-to-view":
ret.view = &role ret.view = &role
} }
} }
@ -319,8 +319,9 @@ func TestClusterRoleLabel(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if got, want := accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"}; !reflect.DeepEqual(got, want) {
t.Errorf("ClusterRole: %s GetLabels() = %s, want %s", accessor.GetName(), got, want) if accessor.GetLabels()["kubernetes.io/bootstrapping"] != "rbac-defaults" {
t.Errorf("ClusterRole: %s GetLabels() = %s, want %s", accessor.GetName(), accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"})
} }
} }

View File

@ -1,6 +1,10 @@
apiVersion: v1 apiVersion: v1
items: items:
- apiVersion: rbac.authorization.k8s.io/v1 - aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.authorization.k8s.io/aggregate-to-admin: "true"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole kind: ClusterRole
metadata: metadata:
annotations: annotations:
@ -9,6 +13,51 @@ items:
labels: labels:
kubernetes.io/bootstrapping: rbac-defaults kubernetes.io/bootstrapping: rbac-defaults
name: admin name: admin
rules: null
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: cluster-admin
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
- aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.authorization.k8s.io/aggregate-to-edit: "true"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: edit
rules: null
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
rbac.authorization.k8s.io/aggregate-to-admin: "true"
name: system:aggregate-to-admin
rules: rules:
- apiGroups: - apiGroups:
- "" - ""
@ -185,27 +234,8 @@ items:
creationTimestamp: null creationTimestamp: null
labels: labels:
kubernetes.io/bootstrapping: rbac-defaults kubernetes.io/bootstrapping: rbac-defaults
name: cluster-admin rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules: name: system:aggregate-to-edit
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: edit
rules: rules:
- apiGroups: - apiGroups:
- "" - ""
@ -354,6 +384,108 @@ items:
- patch - patch
- update - update
- watch - watch
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
rbac.authorization.k8s.io/aggregate-to-view: "true"
name: system:aggregate-to-view
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- persistentvolumeclaims
- pods
- replicationcontrollers
- replicationcontrollers/scale
- serviceaccounts
- services
verbs:
- get
- list
- 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:
- apps
resources:
- daemonsets
- deployments
- deployments/scale
- replicasets
- replicasets/scale
- statefulsets
verbs:
- get
- list
- watch
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- get
- list
- watch
- apiGroups:
- batch
resources:
- cronjobs
- jobs
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- daemonsets
- deployments
- deployments/scale
- ingresses
- replicasets
- replicasets/scale
- replicationcontrollers/scale
verbs:
- get
- list
- watch
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- get
- list
- watch
- apiVersion: rbac.authorization.k8s.io/v1 - apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole kind: ClusterRole
metadata: metadata:
@ -935,7 +1067,11 @@ items:
- create - create
- patch - patch
- update - update
- apiVersion: rbac.authorization.k8s.io/v1 - aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.authorization.k8s.io/aggregate-to-view: "true"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole kind: ClusterRole
metadata: metadata:
annotations: annotations:
@ -944,97 +1080,6 @@ items:
labels: labels:
kubernetes.io/bootstrapping: rbac-defaults kubernetes.io/bootstrapping: rbac-defaults
name: view name: view
rules: rules: null
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- persistentvolumeclaims
- pods
- replicationcontrollers
- replicationcontrollers/scale
- serviceaccounts
- services
verbs:
- get
- list
- 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:
- apps
resources:
- daemonsets
- deployments
- deployments/scale
- replicasets
- replicasets/scale
- statefulsets
verbs:
- get
- list
- watch
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- get
- list
- watch
- apiGroups:
- batch
resources:
- cronjobs
- jobs
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- daemonsets
- deployments
- deployments/scale
- ingresses
- replicasets
- replicasets/scale
- replicationcontrollers/scale
verbs:
- get
- list
- watch
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- get
- list
- watch
kind: List kind: List
metadata: {} metadata: {}

View File

@ -34,6 +34,23 @@ items:
- kind: ServiceAccount - kind: ServiceAccount
name: certificate-controller name: certificate-controller
namespace: kube-system namespace: kube-system
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:controller:clusterrole-aggregation-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:controller:clusterrole-aggregation-controller
subjects:
- kind: ServiceAccount
name: clusterrole-aggregation-controller
namespace: kube-system
- apiVersion: rbac.authorization.k8s.io/v1 - apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding kind: ClusterRoleBinding
metadata: metadata:

View File

@ -87,6 +87,26 @@ items:
- create - create
- patch - patch
- update - update
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:controller:clusterrole-aggregation-controller
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
- apiVersion: rbac.authorization.k8s.io/v1 - apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole kind: ClusterRole
metadata: metadata: