diff --git a/pkg/apis/rbac/helpers.go b/pkg/apis/rbac/helpers.go index 0ae692bfb80..93ed6f99546 100644 --- a/pkg/apis/rbac/helpers.go +++ b/pkg/apis/rbac/helpers.go @@ -20,6 +20,7 @@ import ( "fmt" "strings" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" ) @@ -162,3 +163,61 @@ func (r *PolicyRuleBuilder) Rule() (PolicyRule, error) { return r.PolicyRule, nil } + +// +k8s:deepcopy-gen=false +// ClusterRoleBindingBuilder let's us attach methods. A no-no for API types. +// We use it to construct bindings in code. It's more compact than trying to write them +// out in a literal. +type ClusterRoleBindingBuilder struct { + ClusterRoleBinding ClusterRoleBinding +} + +func NewClusterBinding(clusterRoleName string) *ClusterRoleBindingBuilder { + return &ClusterRoleBindingBuilder{ + ClusterRoleBinding: ClusterRoleBinding{ + ObjectMeta: api.ObjectMeta{Name: clusterRoleName}, + RoleRef: RoleRef{ + APIGroup: GroupName, + Kind: "ClusterRole", + Name: clusterRoleName, + }, + }, + } +} + +func (r *ClusterRoleBindingBuilder) Groups(groups ...string) *ClusterRoleBindingBuilder { + for _, group := range groups { + r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: GroupKind, Name: group}) + } + return r +} + +func (r *ClusterRoleBindingBuilder) Users(users ...string) *ClusterRoleBindingBuilder { + for _, user := range users { + r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: UserKind, Name: user}) + } + return r +} + +func (r *ClusterRoleBindingBuilder) SAs(namespace string, serviceAccountNames ...string) *ClusterRoleBindingBuilder { + for _, saName := range serviceAccountNames { + r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, Subject{Kind: ServiceAccountKind, Namespace: namespace, Name: saName}) + } + return r +} + +func (r *ClusterRoleBindingBuilder) BindingOrDie() ClusterRoleBinding { + ret, err := r.Binding() + if err != nil { + panic(err) + } + return ret +} + +func (r *ClusterRoleBindingBuilder) Binding() (ClusterRoleBinding, error) { + if len(r.ClusterRoleBinding.Subjects) == 0 { + return ClusterRoleBinding{}, fmt.Errorf("subjects are required: %#v", r.ClusterRoleBinding) + } + + return r.ClusterRoleBinding, nil +} diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index dfa85bdbd81..9b729ddc63c 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -5004,6 +5004,23 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{ Dependencies: []string{ "api.ObjectMeta", "rbac.RoleRef", "rbac.Subject", "unversioned.TypeMeta"}, }, + "rbac.ClusterRoleBindingBuilder": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterRoleBindingBuilder let's us attach methods. A no-no for API types. We use it to construct bindings in code. It's more compact than trying to write them out in a literal.", + Properties: map[string]spec.Schema{ + "ClusterRoleBinding": { + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef("#/definitions/rbac.ClusterRoleBinding"), + }, + }, + }, + Required: []string{"ClusterRoleBinding"}, + }, + }, + Dependencies: []string{ + "rbac.ClusterRoleBinding"}, + }, "rbac.ClusterRoleBindingList": { Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ diff --git a/pkg/registry/rbac/rest/storage_rbac.go b/pkg/registry/rbac/rest/storage_rbac.go index f77d7bc2b37..0f34400c645 100644 --- a/pkg/registry/rbac/rest/storage_rbac.go +++ b/pkg/registry/rbac/rest/storage_rbac.go @@ -131,5 +131,25 @@ func PostStartHook(hookContext genericapiserver.PostStartHookContext) error { glog.Infof("Created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name) } + existingClusterRoleBindings, err := clientset.ClusterRoleBindings().List(api.ListOptions{}) + if err != nil { + utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err)) + return nil + } + // if clusterrolebindings already exist, then assume we don't have work to do because we've already + // initialized or another API server has started this task + if len(existingClusterRoleBindings.Items) > 0 { + return nil + } + + for _, clusterRoleBinding := range append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...) { + if _, err := clientset.ClusterRoleBindings().Create(&clusterRoleBinding); err != nil { + // don't fail on failures, try to create as many as you can + utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err)) + continue + } + glog.Infof("Created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name) + } + return nil } diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go index e00854f97ba..b92483e5a04 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go @@ -25,14 +25,18 @@ import ( rbac "k8s.io/kubernetes/pkg/apis/rbac" ) +const saRolePrefix = "system:controller:" + var ( // controllerRoles is a slice of roles used for controllers controllerRoles = []rbac.ClusterRole{} + // controllerRoleBindings is a slice of roles used for controllers + controllerRoleBindings = []rbac.ClusterRoleBinding{} ) func addControllerRole(role rbac.ClusterRole) { - if !strings.HasPrefix(role.Name, "system:controller:") { - glog.Fatalf(`role %q must start with "system:controller:"`, role.Name) + if !strings.HasPrefix(role.Name, saRolePrefix) { + glog.Fatalf(`role %q must start with %q`, role.Name, saRolePrefix) } for _, existingRole := range controllerRoles { @@ -42,6 +46,8 @@ func addControllerRole(role rbac.ClusterRole) { } controllerRoles = append(controllerRoles, role) + controllerRoleBindings = append(controllerRoleBindings, + rbac.NewClusterBinding(role.Name).SAs("kube-system", role.Name[len(saRolePrefix):]).BindingOrDie()) } func eventsRule() rbac.PolicyRule { @@ -50,7 +56,7 @@ func eventsRule() rbac.PolicyRule { func init() { addControllerRole(rbac.ClusterRole{ - ObjectMeta: api.ObjectMeta{Name: "system:controller:replication-controller"}, + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "replication-controller"}, Rules: []rbac.PolicyRule{ rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(), rbac.NewRule("update").Groups(legacyGroup).Resources("replicationcontrollers/status").RuleOrDie(), @@ -64,3 +70,8 @@ func init() { func ControllerRoles() []rbac.ClusterRole { return controllerRoles } + +// ControllerRoleBindings returns the role bindings used by controllers +func ControllerRoleBindings() []rbac.ClusterRoleBinding { + return controllerRoleBindings +} diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go index 592b5f8668f..a8b3084bb44 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go @@ -19,6 +19,7 @@ package bootstrappolicy import ( "k8s.io/kubernetes/pkg/api" rbac "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/auth/user" ) var ( @@ -48,3 +49,11 @@ func ClusterRoles() []rbac.ClusterRole { }, } } + +// ClusterRoleBindings return default rolebindings to the default roles +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(), + } +}