From e24b1c0c254e559ea3add89a6ec32dabb7918a02 Mon Sep 17 00:00:00 2001 From: deads2k Date: Tue, 28 Feb 2017 11:11:32 -0500 Subject: [PATCH] add namespaced role bindings --- pkg/apis/rbac/helpers.go | 16 ++++++++++ pkg/registry/rbac/rest/storage_rbac.go | 32 +++++++++++++++++++ .../rbac/bootstrappolicy/namespace_policy.go | 26 +++++++++++++++ .../rbac/bootstrappolicy/policy_test.go | 22 +++++++++++++ .../testdata/namespace-role-bindings.yaml | 4 +++ 5 files changed, 100 insertions(+) create mode 100644 plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/namespace-role-bindings.yaml diff --git a/pkg/apis/rbac/helpers.go b/pkg/apis/rbac/helpers.go index c0bef888fd7..8b4677bb869 100644 --- a/pkg/apis/rbac/helpers.go +++ b/pkg/apis/rbac/helpers.go @@ -282,6 +282,22 @@ func NewRoleBinding(roleName, namespace string) *RoleBindingBuilder { } } +func NewRoleBindingForClusterRole(roleName, namespace string) *RoleBindingBuilder { + return &RoleBindingBuilder{ + RoleBinding: RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: namespace, + }, + RoleRef: RoleRef{ + APIGroup: GroupName, + Kind: "ClusterRole", + Name: roleName, + }, + }, + } +} + // Groups adds the specified groups as the subjects of the RoleBinding. func (r *RoleBindingBuilder) Groups(groups ...string) *RoleBindingBuilder { for _, group := range groups { diff --git a/pkg/registry/rbac/rest/storage_rbac.go b/pkg/registry/rbac/rest/storage_rbac.go index 981a02433ef..6ac39c2cb9d 100644 --- a/pkg/registry/rbac/rest/storage_rbac.go +++ b/pkg/registry/rbac/rest/storage_rbac.go @@ -233,6 +233,38 @@ func PostStartHook(hookContext genericapiserver.PostStartHookContext) error { } } + // ensure bootstrap namespaced rolebindings are created or reconciled + for namespace, roleBindings := range bootstrappolicy.NamespaceRoleBindings() { + for _, roleBinding := range roleBindings { + opts := reconciliation.ReconcileRoleBindingOptions{ + RoleBinding: reconciliation.RoleBindingAdapter{RoleBinding: &roleBinding}, + Client: reconciliation.RoleBindingClientAdapter{Client: clientset}, + Confirm: true, + } + err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { + result, err := opts.Run() + if err != nil { + return err + } + switch { + case result.Protected && result.Operation != reconciliation.ReconcileNone: + glog.Warningf("skipped reconcile-protected rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects) + case result.Operation == reconciliation.ReconcileUpdate: + glog.Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects) + case result.Operation == reconciliation.ReconcileCreate: + glog.Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace) + case result.Operation == reconciliation.ReconcileRecreate: + glog.Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace) + } + return nil + }) + if err != nil { + // don't fail on failures, try to create as many as you can + utilruntime.HandleError(fmt.Errorf("unable to reconcile rolebinding.%s/%s in %v: %v", rbac.GroupName, roleBinding.Name, namespace, err)) + } + } + } + return true, nil }) // if we're never able to make it through intialization, kill the API server diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/namespace_policy.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/namespace_policy.go index 35b9cae165c..f0370de76e4 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/namespace_policy.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/namespace_policy.go @@ -28,6 +28,9 @@ import ( var ( // namespaceRoles is a map of namespace to slice of roles to create namespaceRoles = map[string][]rbac.Role{} + + // namespaceRoleBindings is a map of namespace to slice of roleBindings to create + namespaceRoleBindings = map[string][]rbac.RoleBinding{} ) func addNamespaceRole(namespace string, role rbac.Role) { @@ -48,6 +51,24 @@ func addNamespaceRole(namespace string, role rbac.Role) { namespaceRoles[namespace] = existingRoles } +func addNamespaceRoleBinding(namespace string, roleBinding rbac.RoleBinding) { + if !strings.HasPrefix(namespace, "kube-") { + glog.Fatalf(`roles can only be bootstrapped into reserved namespaces starting with "kube-", not %q`, namespace) + } + + existingRoleBindings := namespaceRoleBindings[namespace] + for _, existingRoleBinding := range existingRoleBindings { + if roleBinding.Name == existingRoleBinding.Name { + glog.Fatalf("rolebinding %q was already registered in %q", roleBinding.Name, namespace) + } + } + + roleBinding.Namespace = namespace + addDefaultMetadata(&roleBinding) + existingRoleBindings = append(existingRoleBindings, roleBinding) + namespaceRoleBindings[namespace] = existingRoleBindings +} + func init() { addNamespaceRole(metav1.NamespaceSystem, rbac.Role{ // role for finding authentication config info for starting a server @@ -63,3 +84,8 @@ func init() { func NamespaceRoles() map[string][]rbac.Role { return namespaceRoles } + +// NamespaceRoleBindings returns a map of namespace to slice of roles to create +func NamespaceRoleBindings() map[string][]rbac.RoleBinding { + return namespaceRoleBindings +} diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy_test.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy_test.go index 22d0d2532ac..dcd9cdd0d37 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy_test.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy_test.go @@ -173,6 +173,28 @@ func TestBootstrapNamespaceRoles(t *testing.T) { testObjects(t, list, "namespace-roles.yaml") } +func TestBootstrapNamespaceRoleBindings(t *testing.T) { + list := &api.List{} + names := sets.NewString() + roleBindings := map[string]runtime.Object{} + + namespaceRoleBindings := bootstrappolicy.NamespaceRoleBindings() + for _, namespace := range sets.StringKeySet(namespaceRoleBindings).List() { + bootstrapRoleBindings := namespaceRoleBindings[namespace] + for i := range bootstrapRoleBindings { + roleBinding := bootstrapRoleBindings[i] + names.Insert(roleBinding.Name) + roleBindings[roleBinding.Name] = &roleBinding + } + + for _, name := range names.List() { + list.Items = append(list.Items, roleBindings[name]) + } + } + + testObjects(t, list, "namespace-role-bindings.yaml") +} + func TestBootstrapClusterRoles(t *testing.T) { list := &api.List{} names := sets.NewString() diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/namespace-role-bindings.yaml b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/namespace-role-bindings.yaml new file mode 100644 index 00000000000..1668104b64a --- /dev/null +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/namespace-role-bindings.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +items: null +kind: List +metadata: {}