diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/BUILD b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/BUILD index a593e757580..5dacb7d862c 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/BUILD +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/BUILD @@ -36,3 +36,11 @@ go_test( "//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:go_default_library", ], ) + +go_test( + name = "go_default_test", + srcs = ["controller_policy_test.go"], + library = "go_default_library", + tags = ["automanaged"], + deps = ["//pkg/util/sets:go_default_library"], +) diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go index 21045024f95..a815281bf6e 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go @@ -55,6 +55,130 @@ func eventsRule() rbac.PolicyRule { } func init() { + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "attachdetach-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("persistentvolumes", "persistentvolumeclaims").RuleOrDie(), + rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(), + rbac.NewRule("patch", "update").Groups(legacyGroup).Resources("nodes/status").RuleOrDie(), + rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("pods").RuleOrDie(), + eventsRule(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "cronjob-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch", "update").Groups(batchGroup).Resources("cronjobs").RuleOrDie(), + rbac.NewRule("get", "list", "watch", "create", "delete").Groups(batchGroup).Resources("jobs").RuleOrDie(), + rbac.NewRule("update").Groups(batchGroup).Resources("cronjobs/status").RuleOrDie(), + eventsRule(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "daemon-set-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch").Groups(extensionsGroup).Resources("daemonsets").RuleOrDie(), + rbac.NewRule("update").Groups(extensionsGroup).Resources("daemonsets/status").RuleOrDie(), + rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(), + rbac.NewRule("list", "watch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), + rbac.NewRule("create").Groups(legacyGroup).Resources("pods/binding").RuleOrDie(), + eventsRule(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "deployment-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch", "update").Groups(extensionsGroup).Resources("deployments").RuleOrDie(), + rbac.NewRule("update").Groups(extensionsGroup).Resources("deployments/status").RuleOrDie(), + rbac.NewRule("get", "list", "watch", "create", "update", "delete").Groups(extensionsGroup).Resources("replicasets").RuleOrDie(), + // TODO: remove "update" once + // https://github.com/kubernetes/kubernetes/issues/36897 is resolved. + rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("pods").RuleOrDie(), + eventsRule(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "disruption-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list").Groups(extensionsGroup).Resources("deployments").RuleOrDie(), + rbac.NewRule("get", "list").Groups(extensionsGroup).Resources("replicasets").RuleOrDie(), + rbac.NewRule("get", "list").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(), + rbac.NewRule("get", "list", "watch").Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(), + rbac.NewRule("update").Groups(policyGroup).Resources("poddisruptionbudgets/status").RuleOrDie(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "endpoint-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("services", "pods").RuleOrDie(), + rbac.NewRule("get", "list", "create", "update", "delete").Groups(legacyGroup).Resources("endpoints").RuleOrDie(), + rbac.NewRule("create").Groups(legacyGroup).Resources("endpoints/restricted").RuleOrDie(), + eventsRule(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "horizontal-pod-autoscaler"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch").Groups(autoscalingGroup, extensionsGroup).Resources("horizontalpodautoscalers").RuleOrDie(), + rbac.NewRule("update").Groups(autoscalingGroup, extensionsGroup).Resources("horizontalpodautoscalers/status").RuleOrDie(), + rbac.NewRule("get", "update").Groups(legacyGroup).Resources("replicationcontrollers/scale").RuleOrDie(), + rbac.NewRule("get", "update").Groups(extensionsGroup).Resources("deployments/scale", "replicasets/scale").RuleOrDie(), + rbac.NewRule("list").Groups(legacyGroup).Resources("pods").RuleOrDie(), + // TODO: fix MetricsClient to no longer require root proxy access + // TODO: restrict this to the appropriate namespace + rbac.NewRule("proxy").Groups(legacyGroup).Resources("services").Names("https:heapster:").RuleOrDie(), + eventsRule(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "job-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch", "update").Groups(batchGroup, extensionsGroup).Resources("jobs").RuleOrDie(), + rbac.NewRule("update").Groups(batchGroup, extensionsGroup).Resources("jobs/status").RuleOrDie(), + rbac.NewRule("list", "watch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), + eventsRule(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "namespace-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch", "delete").Groups(legacyGroup).Resources("namespaces").RuleOrDie(), + rbac.NewRule("update").Groups(legacyGroup).Resources("namespaces/finalize", "namespaces/status").RuleOrDie(), + rbac.NewRule("get", "list", "delete", "deletecollection").Groups("*").Resources("*").RuleOrDie(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "persistent-volume-binder"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch", "update", "create", "delete").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(), + rbac.NewRule("update").Groups(legacyGroup).Resources("persistentvolumes/status").RuleOrDie(), + rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(), + rbac.NewRule("update").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie(), + rbac.NewRule("list", "watch", "get", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), + + // glusterfs + rbac.NewRule("get", "list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(), + rbac.NewRule("get", "create", "delete").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(), + rbac.NewRule("get").Groups(legacyGroup).Resources("secrets").RuleOrDie(), + + eventsRule(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "pod-garbage-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("list", "watch", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "replicaset-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch", "update").Groups(extensionsGroup).Resources("replicasets").RuleOrDie(), + rbac.NewRule("update").Groups(extensionsGroup).Resources("replicasets/status").RuleOrDie(), + rbac.NewRule("list", "watch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), + eventsRule(), + }, + }) addControllerRole(rbac.ClusterRole{ ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "replication-controller"}, Rules: []rbac.PolicyRule{ @@ -65,6 +189,26 @@ func init() { eventsRule(), }, }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "service-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("services").RuleOrDie(), + rbac.NewRule("update").Groups(legacyGroup).Resources("services/status").RuleOrDie(), + rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(), + eventsRule(), + }, + }) + addControllerRole(rbac.ClusterRole{ + ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "statefulset-controller"}, + Rules: []rbac.PolicyRule{ + rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("pods").RuleOrDie(), + rbac.NewRule("get", "list", "watch").Groups(appsGroup).Resources("statefulsets").RuleOrDie(), + rbac.NewRule("update").Groups(appsGroup).Resources("statefulsets/status").RuleOrDie(), + rbac.NewRule("get", "create", "delete", "update").Groups(legacyGroup).Resources("pods").RuleOrDie(), + rbac.NewRule("get", "create").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(), + eventsRule(), + }, + }) } // ControllerRoles returns the cluster roles used by controllers diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy_test.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy_test.go new file mode 100644 index 00000000000..6fa055c5a50 --- /dev/null +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy_test.go @@ -0,0 +1,60 @@ +/* +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 + +import ( + "testing" + + "k8s.io/kubernetes/pkg/util/sets" +) + +// rolesWithAllowStar are the controller roles which are allowed to contain a *. These are +// namespace lifecycle and GC which have to delete anything. If you're adding to this list +// tag sig-auth +var rolesWithAllowStar = sets.NewString( + saRolePrefix+"namespace-controller", + saRolePrefix+"generic-garbage-collector", +) + +// TestNoStarsForControllers confirms that no controller role has star verbs, groups, +// or resources. There are two known exceptions, namespace lifecycle and GC which have to +// delete anything +func TestNoStarsForControllers(t *testing.T) { + for _, role := range ControllerRoles() { + if rolesWithAllowStar.Has(role.Name) { + continue + } + + for i, rule := range role.Rules { + for j, verb := range rule.Verbs { + if verb == "*" { + t.Errorf("%s.Rule[%d].Verbs[%d] is star", role.Name, i, j) + } + } + for j, group := range rule.APIGroups { + if group == "*" { + t.Errorf("%s.Rule[%d].APIGroups[%d] is star", role.Name, i, j) + } + } + for j, resource := range rule.Resources { + if resource == "*" { + t.Errorf("%s.Rule[%d].Resources[%d] is star", role.Name, i, j) + } + } + } + } +}