func init() { addControllerRole(rbac.ClusterRole{ ObjectMeta: api.ObjectMeta{Name: "system:controller: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(), rbac.NewRule("list", "watch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), eventsRule(), }, }) }
func init() { addControllerRole(rbac.ClusterRole{ ObjectMeta: api.ObjectMeta{Name: saRolePrefix + "replication-controller"}, Rules: []rbac.PolicyRule{ // 1.0 controllers needed get, update, so without these old controllers break on new servers rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(), rbac.NewRule("update").Groups(legacyGroup).Resources("replicationcontrollers/status").RuleOrDie(), rbac.NewRule("list", "watch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), eventsRule(), }, }) }
// ClusterRoles returns the cluster roles to bootstrap an API server with func ClusterRoles() []rbac.ClusterRole { return []rbac.ClusterRole{ { // a "root" role which can do absolutely anything ObjectMeta: api.ObjectMeta{Name: "cluster-admin"}, Rules: []rbac.PolicyRule{ rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(), rbac.NewRule("*").URLs("*").RuleOrDie(), }, }, { // a role which provides just enough power to discovery API versions for negotiation ObjectMeta: api.ObjectMeta{Name: "system:discovery"}, Rules: []rbac.PolicyRule{ rbac.NewRule("get").URLs("/version", "/api", "/api/*", "/apis", "/apis/*").RuleOrDie(), }, }, } }
func TestRuleMatches(t *testing.T) { tests := []struct { name string rule rbac.PolicyRule requestsToExpected map[authorizer.AttributesRecord]bool }{ { name: "star verb, exact match other", rule: rbac.NewRule("*").Groups("group1").Resources("resource1").RuleOrDie(), requestsToExpected: map[authorizer.AttributesRecord]bool{ resourceRequest("verb1").Group("group1").Resource("resource1").New(): true, resourceRequest("verb1").Group("group2").Resource("resource1").New(): false, resourceRequest("verb1").Group("group1").Resource("resource2").New(): false, resourceRequest("verb1").Group("group2").Resource("resource2").New(): false, resourceRequest("verb2").Group("group1").Resource("resource1").New(): true, resourceRequest("verb2").Group("group2").Resource("resource1").New(): false, resourceRequest("verb2").Group("group1").Resource("resource2").New(): false, resourceRequest("verb2").Group("group2").Resource("resource2").New(): false, }, }, { name: "star group, exact match other", rule: rbac.NewRule("verb1").Groups("*").Resources("resource1").RuleOrDie(), requestsToExpected: map[authorizer.AttributesRecord]bool{ resourceRequest("verb1").Group("group1").Resource("resource1").New(): true, resourceRequest("verb1").Group("group2").Resource("resource1").New(): true, resourceRequest("verb1").Group("group1").Resource("resource2").New(): false, resourceRequest("verb1").Group("group2").Resource("resource2").New(): false, resourceRequest("verb2").Group("group1").Resource("resource1").New(): false, resourceRequest("verb2").Group("group2").Resource("resource1").New(): false, resourceRequest("verb2").Group("group1").Resource("resource2").New(): false, resourceRequest("verb2").Group("group2").Resource("resource2").New(): false, }, }, { name: "star resource, exact match other", rule: rbac.NewRule("verb1").Groups("group1").Resources("*").RuleOrDie(), requestsToExpected: map[authorizer.AttributesRecord]bool{ resourceRequest("verb1").Group("group1").Resource("resource1").New(): true, resourceRequest("verb1").Group("group2").Resource("resource1").New(): false, resourceRequest("verb1").Group("group1").Resource("resource2").New(): true, resourceRequest("verb1").Group("group2").Resource("resource2").New(): false, resourceRequest("verb2").Group("group1").Resource("resource1").New(): false, resourceRequest("verb2").Group("group2").Resource("resource1").New(): false, resourceRequest("verb2").Group("group1").Resource("resource2").New(): false, resourceRequest("verb2").Group("group2").Resource("resource2").New(): false, }, }, { name: "tuple expansion", rule: rbac.NewRule("verb1", "verb2").Groups("group1", "group2").Resources("resource1", "resource2").RuleOrDie(), requestsToExpected: map[authorizer.AttributesRecord]bool{ resourceRequest("verb1").Group("group1").Resource("resource1").New(): true, resourceRequest("verb1").Group("group2").Resource("resource1").New(): true, resourceRequest("verb1").Group("group1").Resource("resource2").New(): true, resourceRequest("verb1").Group("group2").Resource("resource2").New(): true, resourceRequest("verb2").Group("group1").Resource("resource1").New(): true, resourceRequest("verb2").Group("group2").Resource("resource1").New(): true, resourceRequest("verb2").Group("group1").Resource("resource2").New(): true, resourceRequest("verb2").Group("group2").Resource("resource2").New(): true, }, }, { name: "subresource expansion", rule: rbac.NewRule("*").Groups("*").Resources("resource1/subresource1").RuleOrDie(), requestsToExpected: map[authorizer.AttributesRecord]bool{ resourceRequest("verb1").Group("group1").Resource("resource1").Subresource("subresource1").New(): true, resourceRequest("verb1").Group("group2").Resource("resource1").Subresource("subresource2").New(): false, resourceRequest("verb1").Group("group1").Resource("resource2").Subresource("subresource1").New(): false, resourceRequest("verb1").Group("group2").Resource("resource2").Subresource("subresource1").New(): false, resourceRequest("verb2").Group("group1").Resource("resource1").Subresource("subresource1").New(): true, resourceRequest("verb2").Group("group2").Resource("resource1").Subresource("subresource2").New(): false, resourceRequest("verb2").Group("group1").Resource("resource2").Subresource("subresource1").New(): false, resourceRequest("verb2").Group("group2").Resource("resource2").Subresource("subresource1").New(): false, }, }, { name: "star nonresource, exact match other", rule: rbac.NewRule("verb1").URLs("*").RuleOrDie(), requestsToExpected: map[authorizer.AttributesRecord]bool{ nonresourceRequest("verb1").URL("/foo").New(): true, nonresourceRequest("verb1").URL("/foo/bar").New(): true, nonresourceRequest("verb1").URL("/foo/baz").New(): true, nonresourceRequest("verb1").URL("/foo/bar/one").New(): true, nonresourceRequest("verb1").URL("/foo/baz/one").New(): true, nonresourceRequest("verb2").URL("/foo").New(): false, nonresourceRequest("verb2").URL("/foo/bar").New(): false, nonresourceRequest("verb2").URL("/foo/baz").New(): false, nonresourceRequest("verb2").URL("/foo/bar/one").New(): false, nonresourceRequest("verb2").URL("/foo/baz/one").New(): false, }, }, { name: "star nonresource subpath", rule: rbac.NewRule("verb1").URLs("/foo/*").RuleOrDie(), requestsToExpected: map[authorizer.AttributesRecord]bool{ nonresourceRequest("verb1").URL("/foo").New(): false, nonresourceRequest("verb1").URL("/foo/bar").New(): true, nonresourceRequest("verb1").URL("/foo/baz").New(): true, nonresourceRequest("verb1").URL("/foo/bar/one").New(): true, nonresourceRequest("verb1").URL("/foo/baz/one").New(): true, nonresourceRequest("verb1").URL("/notfoo").New(): false, nonresourceRequest("verb1").URL("/notfoo/bar").New(): false, nonresourceRequest("verb1").URL("/notfoo/baz").New(): false, nonresourceRequest("verb1").URL("/notfoo/bar/one").New(): false, nonresourceRequest("verb1").URL("/notfoo/baz/one").New(): false, }, }, { name: "star verb, exact nonresource", rule: rbac.NewRule("*").URLs("/foo", "/foo/bar/one").RuleOrDie(), requestsToExpected: map[authorizer.AttributesRecord]bool{ nonresourceRequest("verb1").URL("/foo").New(): true, nonresourceRequest("verb1").URL("/foo/bar").New(): false, nonresourceRequest("verb1").URL("/foo/baz").New(): false, nonresourceRequest("verb1").URL("/foo/bar/one").New(): true, nonresourceRequest("verb1").URL("/foo/baz/one").New(): false, nonresourceRequest("verb2").URL("/foo").New(): true, nonresourceRequest("verb2").URL("/foo/bar").New(): false, nonresourceRequest("verb2").URL("/foo/baz").New(): false, nonresourceRequest("verb2").URL("/foo/bar/one").New(): true, nonresourceRequest("verb2").URL("/foo/baz/one").New(): false, }, }, } for _, tc := range tests { for request, expected := range tc.requestsToExpected { if e, a := expected, RuleAllows(request, tc.rule); e != a { t.Errorf("%q: expected %v, got %v for %v", tc.name, e, a, request) } } } }
} ` forbiddenNamespace = ` { "apiVersion": "` + registered.GroupOrDie(api.GroupName).GroupVersion.String() + `", "kind": "Namespace", "metadata": { "name": "forbidden-namespace"%s } } ` ) // Declare some PolicyRules beforehand. var ( ruleAllowAll = rbacapi.NewRule("*").Groups("*").Resources("*").RuleOrDie() ruleReadPods = rbacapi.NewRule("list", "get", "watch").Groups("").Resources("pods").RuleOrDie() ruleWriteJobs = rbacapi.NewRule("*").Groups("batch").Resources("*").RuleOrDie() ) func TestRBAC(t *testing.T) { superUser := "******" tests := []struct { bootstrapRoles bootstrapRoles requests []request }{ { bootstrapRoles: bootstrapRoles{ clusterRoles: []rbacapi.ClusterRole{
if covers, miss := rbacvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.edit.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } if covers, miss := rbacvalidation.Covers(semanticRoles.admin.Rules, semanticRoles.view.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } if covers, miss := rbacvalidation.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 = []rbac.PolicyRule{ rbac.NewRule("create").Groups("authorization.k8s.io").Resources("localsubjectaccessreviews").RuleOrDie(), rbac.NewRule(bootstrappolicy.ReadWrite...).Groups("rbac.authorization.k8s.io").Resources("rolebindings").RuleOrDie(), rbac.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, _ := rbacvalidation.Covers(semanticRoles.edit.Rules, []rbac.PolicyRule{rule}); covers { t.Errorf("edit has extra powers: %#v", rule) } } semanticRoles.edit.Rules = append(semanticRoles.edit.Rules, additionalAdminPowers...)
func init() { addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.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: metav1.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", "update", "delete").Groups(batchGroup).Resources("jobs").RuleOrDie(), rbac.NewRule("update").Groups(batchGroup).Resources("cronjobs/status").RuleOrDie(), rbac.NewRule("list", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.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: metav1.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", "patch", "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: metav1.ObjectMeta{Name: saRolePrefix + "disruption-controller"}, Rules: []rbac.PolicyRule{ rbac.NewRule("get", "list", "watch").Groups(extensionsGroup).Resources("deployments").RuleOrDie(), rbac.NewRule("get", "list", "watch").Groups(extensionsGroup).Resources("replicasets").RuleOrDie(), rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(), rbac.NewRule("get", "list", "watch").Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(), rbac.NewRule("get", "list", "watch").Groups(appsGroup).Resources("statefulsets").RuleOrDie(), rbac.NewRule("update").Groups(policyGroup).Resources("poddisruptionbudgets/status").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.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: metav1.ObjectMeta{Name: saRolePrefix + "generic-garbage-collector"}, Rules: []rbac.PolicyRule{ // the GC controller needs to run list/watches, selective gets, and updates against any resource rbac.NewRule("get", "list", "watch", "patch", "update", "delete").Groups("*").Resources("*").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.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(), // TODO this should be removable when the HPA contoller is fixed rbac.NewRule("get", "update").Groups(extensionsGroup).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:", "http:heapster:").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "job-controller"}, Rules: []rbac.PolicyRule{ rbac.NewRule("get", "list", "watch", "update").Groups(batchGroup).Resources("jobs").RuleOrDie(), rbac.NewRule("update").Groups(batchGroup).Resources("jobs/status").RuleOrDie(), rbac.NewRule("list", "watch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.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: metav1.ObjectMeta{Name: saRolePrefix + "node-controller"}, Rules: []rbac.PolicyRule{ rbac.NewRule("get", "list", "update", "delete").Groups(legacyGroup).Resources("nodes").RuleOrDie(), rbac.NewRule("update").Groups(legacyGroup).Resources("nodes/status").RuleOrDie(), // used for pod eviction rbac.NewRule("update").Groups(legacyGroup).Resources("pods/status").RuleOrDie(), rbac.NewRule("list", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.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(), // recyclerClient.WatchPod rbac.NewRule("watch").Groups(legacyGroup).Resources("events").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "pod-garbage-collector"}, Rules: []rbac.PolicyRule{ rbac.NewRule("list", "watch", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), rbac.NewRule("list").Groups(legacyGroup).Resources("nodes").RuleOrDie(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.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", "patch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "replication-controller"}, Rules: []rbac.PolicyRule{ // 1.0 controllers needed get, update, so without these old controllers break on new servers rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(), rbac.NewRule("update").Groups(legacyGroup).Resources("replicationcontrollers/status").RuleOrDie(), rbac.NewRule("list", "watch", "patch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "resourcequota-controller"}, Rules: []rbac.PolicyRule{ // quota can count quota on anything for reconcilation, so it needs full viewing powers rbac.NewRule("list", "watch").Groups("*").Resources("*").RuleOrDie(), rbac.NewRule("update").Groups(legacyGroup).Resources("resourcequotas/status").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "route-controller"}, Rules: []rbac.PolicyRule{ rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(), rbac.NewRule("patch").Groups(legacyGroup).Resources("nodes/status").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "service-account-controller"}, Rules: []rbac.PolicyRule{ rbac.NewRule("create").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(), eventsRule(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.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: metav1.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(), }, }) addControllerRole(rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "certificate-controller"}, Rules: []rbac.PolicyRule{ rbac.NewRule("get", "list", "watch").Groups(certificatesGroup).Resources("certificatesigningrequests").RuleOrDie(), rbac.NewRule("update").Groups(certificatesGroup).Resources("certificatesigningrequests/status", "certificatesigningrequests/approval").RuleOrDie(), eventsRule(), }, }) }
func eventsRule() rbac.PolicyRule { return rbac.NewRule("create", "update", "patch").Groups(legacyGroup).Resources("events").RuleOrDie() }
// ClusterRoles returns the cluster roles to bootstrap an API server with func ClusterRoles() []rbac.ClusterRole { roles := []rbac.ClusterRole{ { // a "root" role which can do absolutely anything ObjectMeta: api.ObjectMeta{Name: "cluster-admin"}, Rules: []rbac.PolicyRule{ rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(), rbac.NewRule("*").URLs("*").RuleOrDie(), }, }, { // a role which provides just enough power to discovery API versions for negotiation ObjectMeta: api.ObjectMeta{Name: "system:discovery"}, Rules: []rbac.PolicyRule{ rbac.NewRule("get").URLs("/version", "/api", "/api/*", "/apis", "/apis/*").RuleOrDie(), }, }, { // a role which provides minimal resource access to allow a "normal" user to learn information about themselves ObjectMeta: api.ObjectMeta{Name: "system:basic-user"}, Rules: []rbac.PolicyRule{ // TODO add future selfsubjectrulesreview, project request APIs, project listing APIs rbac.NewRule("create").Groups(authorizationGroup).Resources("selfsubjectaccessreviews").RuleOrDie(), }, }, { // a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users. ObjectMeta: api.ObjectMeta{Name: "admin"}, 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("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", "services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(), rbac.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. rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets", "horizontalpodautoscalers", "replicationcontrollers/scale", "replicasets", "replicasets/scale", "deployments", "deployments/scale").RuleOrDie(), // additional admin powers rbac.NewRule("create").Groups(authorizationGroup).Resources("localsubjectaccessreviews").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(rbacGroup).Resources("roles", "rolebindings").RuleOrDie(), }, }, { // 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: api.ObjectMeta{Name: "edit"}, 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("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", "services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(), rbac.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. rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets", "horizontalpodautoscalers", "replicationcontrollers/scale", "replicasets", "replicasets/scale", "deployments", "deployments/scale").RuleOrDie(), }, }, { // a role for namespace level viewing. It grants Read-only access to non-escalating resources in // a namespace. ObjectMeta: api.ObjectMeta{Name: "view"}, Rules: []rbac.PolicyRule{ rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", "services", "endpoints", "persistentvolumeclaims", "configmaps").RuleOrDie(), rbac.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. rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), rbac.NewRule(Read...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(), rbac.NewRule(Read...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), rbac.NewRule(Read...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), rbac.NewRule(Read...).Groups(extensionsGroup).Resources("daemonsets", "horizontalpodautoscalers", "replicationcontrollers/scale", "replicasets", "replicasets/scale", "deployments", "deployments/scale").RuleOrDie(), }, }, { // a role for nodes to use to have the access they need for running pods ObjectMeta: api.ObjectMeta{Name: "system:node"}, Rules: []rbac.PolicyRule{ // Needed to check API access. These creates are non-mutating rbac.NewRule("create").Groups(authenticationGroup).Resources("tokenreviews").RuleOrDie(), rbac.NewRule("create").Groups(authorizationGroup).Resources("subjectaccessreviews", "localsubjectaccessreviews").RuleOrDie(), // Needed to build serviceLister, to populate env vars for services rbac.NewRule(Read...).Groups(legacyGroup).Resources("services").RuleOrDie(), // Nodes can register themselves // TODO: restrict to creating a node with the same name they announce rbac.NewRule("create", "get", "list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(), // TODO: restrict to the bound node once supported rbac.NewRule("update", "patch").Groups(legacyGroup).Resources("nodes/status").RuleOrDie(), // TODO: restrict to the bound node as creator once supported rbac.NewRule("create", "update", "patch").Groups(legacyGroup).Resources("events").RuleOrDie(), // TODO: restrict to pods scheduled on the bound node once supported rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods").RuleOrDie(), // TODO: remove once mirror pods are removed // TODO: restrict deletion to mirror pods created by the bound node once supported // Needed for the node to create/delete mirror pods rbac.NewRule("get", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), // TODO: restrict to pods scheduled on the bound node once supported rbac.NewRule("update").Groups(legacyGroup).Resources("pods/status").RuleOrDie(), // TODO: restrict to secrets and configmaps used by pods scheduled on bound node once supported // Needed for imagepullsecrets, rbd/ceph and secret volumes, and secrets in envs // Needed for configmap volume and envs rbac.NewRule("get").Groups(legacyGroup).Resources("secrets", "configmaps").RuleOrDie(), // TODO: restrict to claims/volumes used by pods scheduled on bound node once supported // Needed for persistent volumes rbac.NewRule("get").Groups(legacyGroup).Resources("persistentvolumeclaims", "persistentvolumes").RuleOrDie(), // TODO: restrict to namespaces of pods scheduled on bound node once supported // TODO: change glusterfs to use DNS lookup so this isn't needed? // Needed for glusterfs volumes rbac.NewRule("get").Groups(legacyGroup).Resources("endpoints").RuleOrDie(), }, }, { // a role to use for setting up a proxy ObjectMeta: api.ObjectMeta{Name: "system:node-proxier"}, Rules: []rbac.PolicyRule{ // Used to build serviceLister rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(), rbac.NewRule("get").Groups(legacyGroup).Resources("nodes").RuleOrDie(), }, }, { // a role to use for allowing authentication and authorization delegation ObjectMeta: api.ObjectMeta{Name: "system:auth-delegator"}, Rules: []rbac.PolicyRule{ // These creates are non-mutating rbac.NewRule("create").Groups(authenticationGroup).Resources("tokenreviews").RuleOrDie(), rbac.NewRule("create").Groups(authorizationGroup).Resources("subjectaccessreviews").RuleOrDie(), }, }, { // a role to use for bootstrapping the kube-controller-manager so it can create the shared informers // service accounts, and secrets that we need to create separate identities for other controllers ObjectMeta: api.ObjectMeta{Name: "system:kube-controller-manager"}, Rules: []rbac.PolicyRule{ eventsRule(), rbac.NewRule("create").Groups(legacyGroup).Resources("endpoints", "secrets", "serviceaccounts").RuleOrDie(), rbac.NewRule("delete").Groups(legacyGroup).Resources("secrets").RuleOrDie(), rbac.NewRule("get").Groups(legacyGroup).Resources("endpoints", "namespaces", "serviceaccounts").RuleOrDie(), rbac.NewRule("update").Groups(legacyGroup).Resources("endpoints", "serviceaccounts").RuleOrDie(), rbac.NewRule("list", "watch").Groups("*").Resources("namespaces", "nodes", "persistentvolumeclaims", "persistentvolumes", "pods", "secrets", "serviceaccounts").RuleOrDie(), rbac.NewRule("list", "watch").Groups(extensionsGroup).Resources("daemonsets", "deployments", "replicasets").RuleOrDie(), rbac.NewRule("list", "watch").Groups(batchGroup).Resources("jobs", "cronjobs").RuleOrDie(), }, }, } addClusterRoleLabel(roles) return roles }
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{ // 1.0 controllers needed get, update, so without these old controllers break on new servers rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(), rbac.NewRule("update").Groups(legacyGroup).Resources("replicationcontrollers/status").RuleOrDie(), rbac.NewRule("list", "watch", "create", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(), 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(), }, }) }
func TestRBAC(t *testing.T) { superUser := "******" tests := []struct { bootstrapRoles bootstrapRoles requests []request }{ { bootstrapRoles: bootstrapRoles{ clusterRoles: []rbacapi.ClusterRole{ { ObjectMeta: metav1.ObjectMeta{Name: "allow-all"}, Rules: []rbacapi.PolicyRule{ruleAllowAll}, }, { ObjectMeta: metav1.ObjectMeta{Name: "read-pods"}, Rules: []rbacapi.PolicyRule{ruleReadPods}, }, }, clusterRoleBindings: []rbacapi.ClusterRoleBinding{ { ObjectMeta: metav1.ObjectMeta{Name: "read-pods"}, Subjects: []rbacapi.Subject{ {Kind: "User", Name: "pod-reader"}, }, RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "read-pods"}, }, }, }, requests: []request{ // Create the namespace used later in the test {superUser, "POST", "", "namespaces", "", "", podNamespace, http.StatusCreated}, {superUser, "GET", "", "pods", "", "", "", http.StatusOK}, {superUser, "GET", "", "pods", "pod-namespace", "a", "", http.StatusNotFound}, {superUser, "POST", "", "pods", "pod-namespace", "", aPod, http.StatusCreated}, {superUser, "GET", "", "pods", "pod-namespace", "a", "", http.StatusOK}, {"bob", "GET", "", "pods", "", "", "", http.StatusForbidden}, {"bob", "GET", "", "pods", "pod-namespace", "a", "", http.StatusForbidden}, {"pod-reader", "GET", "", "pods", "", "", "", http.StatusOK}, {"pod-reader", "POST", "", "pods", "pod-namespace", "", aPod, http.StatusForbidden}, }, }, { bootstrapRoles: bootstrapRoles{ clusterRoles: []rbacapi.ClusterRole{ { ObjectMeta: metav1.ObjectMeta{Name: "write-jobs"}, Rules: []rbacapi.PolicyRule{ruleWriteJobs}, }, { ObjectMeta: metav1.ObjectMeta{Name: "create-rolebindings"}, Rules: []rbacapi.PolicyRule{ rbacapi.NewRule("create").Groups("rbac.authorization.k8s.io").Resources("rolebindings").RuleOrDie(), }, }, { ObjectMeta: metav1.ObjectMeta{Name: "bind-any-clusterrole"}, Rules: []rbacapi.PolicyRule{ rbacapi.NewRule("bind").Groups("rbac.authorization.k8s.io").Resources("clusterroles").RuleOrDie(), }, }, }, clusterRoleBindings: []rbacapi.ClusterRoleBinding{ { ObjectMeta: metav1.ObjectMeta{Name: "write-jobs"}, Subjects: []rbacapi.Subject{{Kind: "User", Name: "job-writer"}}, RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "write-jobs"}, }, { ObjectMeta: metav1.ObjectMeta{Name: "create-rolebindings"}, Subjects: []rbacapi.Subject{ {Kind: "User", Name: "job-writer"}, {Kind: "User", Name: "nonescalating-rolebinding-writer"}, {Kind: "User", Name: "any-rolebinding-writer"}, }, RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "create-rolebindings"}, }, { ObjectMeta: metav1.ObjectMeta{Name: "bind-any-clusterrole"}, Subjects: []rbacapi.Subject{{Kind: "User", Name: "any-rolebinding-writer"}}, RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "bind-any-clusterrole"}, }, }, roleBindings: []rbacapi.RoleBinding{ { ObjectMeta: metav1.ObjectMeta{Name: "write-jobs", Namespace: "job-namespace"}, Subjects: []rbacapi.Subject{{Kind: "User", Name: "job-writer-namespace"}}, RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "write-jobs"}, }, { ObjectMeta: metav1.ObjectMeta{Name: "create-rolebindings", Namespace: "job-namespace"}, Subjects: []rbacapi.Subject{{Kind: "User", Name: "job-writer-namespace"}}, RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "create-rolebindings"}, }, }, }, requests: []request{ // Create the namespace used later in the test {superUser, "POST", "", "namespaces", "", "", jobNamespace, http.StatusCreated}, {superUser, "POST", "", "namespaces", "", "", forbiddenNamespace, http.StatusCreated}, {"user-with-no-permissions", "POST", "batch", "jobs", "job-namespace", "", aJob, http.StatusForbidden}, {"user-with-no-permissions", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusForbidden}, // job-writer-namespace cannot write to the "forbidden-namespace" {"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "", "", http.StatusForbidden}, {"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusForbidden}, {"job-writer-namespace", "POST", "batch", "jobs", "forbidden-namespace", "", aJob, http.StatusForbidden}, {"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusForbidden}, // job-writer can write to any namespace {"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "", "", http.StatusOK}, {"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusNotFound}, {"job-writer", "POST", "batch", "jobs", "forbidden-namespace", "", aJob, http.StatusCreated}, {"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusOK}, {"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "", "", http.StatusOK}, {"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusNotFound}, {"job-writer-namespace", "POST", "batch", "jobs", "job-namespace", "", aJob, http.StatusCreated}, {"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusOK}, // cannot bind role anywhere {"user-with-no-permissions", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusForbidden}, // can only bind role in namespace where they have covering permissions {"job-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "", writeJobsRoleBinding, http.StatusForbidden}, {"job-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusCreated}, {superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "pi", "", http.StatusOK}, // can bind role in any namespace where they have covering permissions {"job-writer", "POST", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "", writeJobsRoleBinding, http.StatusCreated}, {superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "pi", "", http.StatusOK}, // cannot bind role because they don't have covering permissions {"nonescalating-rolebinding-writer", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusForbidden}, // can bind role because they have explicit bind permission {"any-rolebinding-writer", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusCreated}, {superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "pi", "", http.StatusOK}, }, }, } for i, tc := range tests { // Create an API Server. masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.GenericConfig.Authorizer = newRBACAuthorizer(masterConfig) masterConfig.GenericConfig.Authenticator = newFakeAuthenticator() _, s := framework.RunAMaster(masterConfig) defer s.Close() clientConfig := &restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{NegotiatedSerializer: api.Codecs}} // Bootstrap the API Server with the test case's initial roles. if err := tc.bootstrapRoles.bootstrap(clientsetForUser(superUser, clientConfig)); err != nil { t.Errorf("case %d: failed to apply initial roles: %v", i, err) continue } previousResourceVersion := make(map[string]float64) for j, r := range tc.requests { testGroup, ok := testapi.Groups[r.apiGroup] if !ok { t.Errorf("case %d %d: unknown api group %q, %s", i, j, r.apiGroup, r) continue } path := testGroup.ResourcePath(r.resource, r.namespace, r.name) var body io.Reader if r.body != "" { sub := "" if r.verb == "PUT" { // For update operations, insert previous resource version if resVersion := previousResourceVersion[getPreviousResourceVersionKey(path, "")]; resVersion != 0 { sub += fmt.Sprintf(",\"resourceVersion\": \"%v\"", resVersion) } } // For any creation requests, add the namespace to the object meta. if r.verb == "POST" || r.verb == "PUT" { if r.namespace != "" { sub += fmt.Sprintf(",\"namespace\": %q", r.namespace) } } body = strings.NewReader(fmt.Sprintf(r.body, sub)) } req, err := http.NewRequest(r.verb, s.URL+path, body) if err != nil { t.Fatalf("failed to create request: %v", err) } func() { reqDump, err := httputil.DumpRequest(req, true) if err != nil { t.Fatalf("failed to dump request: %v", err) return } resp, err := clientForUser(r.user).Do(req) if err != nil { t.Errorf("case %d, req %d: failed to make request: %v", i, j, err) return } defer resp.Body.Close() respDump, err := httputil.DumpResponse(resp, true) if err != nil { t.Fatalf("failed to dump response: %v", err) return } if resp.StatusCode != r.expectedStatus { // When debugging is on, dump the entire request and response. Very helpful for // debugging malformed test cases. // // To turn on debugging, use the '-args' flag. // // go test -v -tags integration -run RBAC -args -v 10 // glog.V(8).Infof("case %d, req %d: %s\n%s\n", i, j, reqDump, respDump) t.Errorf("case %d, req %d: %s expected %q got %q", i, j, r, statusCode(r.expectedStatus), statusCode(resp.StatusCode)) } b, _ := ioutil.ReadAll(resp.Body) if r.verb == "POST" && (resp.StatusCode/100) == 2 { // For successful create operations, extract resourceVersion id, currentResourceVersion, err := parseResourceVersion(b) if err == nil { key := getPreviousResourceVersionKey(path, id) previousResourceVersion[key] = currentResourceVersion } else { t.Logf("error in trying to extract resource version: %s", err) } } }() } } }
// ClusterRoles returns the cluster roles to bootstrap an API server with func ClusterRoles() []rbac.ClusterRole { return []rbac.ClusterRole{ { // a "root" role which can do absolutely anything ObjectMeta: api.ObjectMeta{Name: "cluster-admin"}, Rules: []rbac.PolicyRule{ rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(), rbac.NewRule("*").URLs("*").RuleOrDie(), }, }, { // a role which provides just enough power to discovery API versions for negotiation ObjectMeta: api.ObjectMeta{Name: "system:discovery"}, Rules: []rbac.PolicyRule{ rbac.NewRule("get").URLs("/version", "/api", "/api/*", "/apis", "/apis/*").RuleOrDie(), }, }, { // a role which provides minimal resource access to allow a "normal" user to learn information about themselves ObjectMeta: api.ObjectMeta{Name: "system:basic-user"}, Rules: []rbac.PolicyRule{ // TODO add future selfsubjectrulesreview, project request APIs, project listing APIs rbac.NewRule("create").Groups(authorizationGroup).Resources("selfsubjectaccessreviews").RuleOrDie(), }, }, { // a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users. ObjectMeta: api.ObjectMeta{Name: "admin"}, 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("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", "services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(), rbac.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. rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("jobs", "daemonsets", "horizontalpodautoscalers", "replicationcontrollers/scale", "replicasets", "replicasets/scale", "deployments", "deployments/scale").RuleOrDie(), // additional admin powers rbac.NewRule("create").Groups(authorizationGroup).Resources("localsubjectaccessreviews").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(rbacGroup).Resources("roles", "rolebindings").RuleOrDie(), }, }, { // 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: api.ObjectMeta{Name: "edit"}, 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("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", "services", "services/proxy", "endpoints", "persistentvolumeclaims", "configmaps", "secrets").RuleOrDie(), rbac.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. rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("jobs", "daemonsets", "horizontalpodautoscalers", "replicationcontrollers/scale", "replicasets", "replicasets/scale", "deployments", "deployments/scale").RuleOrDie(), }, }, { // a role for namespace level viewing. It grants Read-only access to non-escalating resources in // a namespace. ObjectMeta: api.ObjectMeta{Name: "view"}, Rules: []rbac.PolicyRule{ rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", "services", "endpoints", "persistentvolumeclaims", "configmaps").RuleOrDie(), rbac.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. rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), rbac.NewRule(Read...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(), rbac.NewRule(Read...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), rbac.NewRule(Read...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), rbac.NewRule(Read...).Groups(extensionsGroup).Resources("jobs", "daemonsets", "horizontalpodautoscalers", "replicationcontrollers/scale", "replicasets", "replicasets/scale", "deployments", "deployments/scale").RuleOrDie(), }, }, } }