123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- /*
- 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_test
- import (
- "io/ioutil"
- "os"
- "path/filepath"
- "reflect"
- "testing"
- "sigs.k8s.io/yaml"
- "k8s.io/api/core/v1"
- rbacv1 "k8s.io/api/rbac/v1"
- "k8s.io/apimachinery/pkg/api/meta"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/util/diff"
- "k8s.io/apimachinery/pkg/util/sets"
- "k8s.io/kubernetes/pkg/api/legacyscheme"
- api "k8s.io/kubernetes/pkg/apis/core"
- _ "k8s.io/kubernetes/pkg/apis/core/install"
- _ "k8s.io/kubernetes/pkg/apis/rbac/install"
- rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
- rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
- "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
- )
- // semanticRoles is a few enumerated roles for which the relationships are well established
- // and we want to maintain symmetric roles
- type semanticRoles struct {
- admin *rbacv1.ClusterRole
- edit *rbacv1.ClusterRole
- view *rbacv1.ClusterRole
- }
- func getSemanticRoles(roles []rbacv1.ClusterRole) semanticRoles {
- ret := semanticRoles{}
- for i := range roles {
- role := roles[i]
- switch role.Name {
- case "system:aggregate-to-admin":
- ret.admin = &role
- case "system:aggregate-to-edit":
- ret.edit = &role
- case "system:aggregate-to-view":
- ret.view = &role
- }
- }
- return ret
- }
- // viewEscalatingNamespaceResources is the list of rules that would allow privilege escalation attacks based on
- // ability to view (GET) them
- var viewEscalatingNamespaceResources = []rbacv1.PolicyRule{
- rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/attach").RuleOrDie(),
- rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/proxy").RuleOrDie(),
- rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/exec").RuleOrDie(),
- rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/portforward").RuleOrDie(),
- rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("secrets").RuleOrDie(),
- rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("services/proxy").RuleOrDie(),
- }
- // ungettableResources is the list of rules that don't allow to view (GET) them
- // this is purposefully separate list to distinguish from escalating privs
- var ungettableResources = []rbacv1.PolicyRule{
- rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("apps", "extensions").Resources("deployments/rollback").RuleOrDie(),
- }
- func TestEditViewRelationship(t *testing.T) {
- readVerbs := sets.NewString(bootstrappolicy.Read...)
- semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
- // modify the edit role rules to make then read-only for comparison against view role rules
- for i := range semanticRoles.edit.Rules {
- rule := semanticRoles.edit.Rules[i]
- remainingVerbs := []string{}
- for _, verb := range rule.Verbs {
- if readVerbs.Has(verb) {
- remainingVerbs = append(remainingVerbs, verb)
- }
- }
- rule.Verbs = remainingVerbs
- semanticRoles.edit.Rules[i] = rule
- }
- // confirm that the view role doesn't already have extra powers
- for _, rule := range viewEscalatingNamespaceResources {
- if covers, _ := rbacregistryvalidation.Covers(semanticRoles.view.Rules, []rbacv1.PolicyRule{rule}); covers {
- t.Errorf("view has extra powers: %#v", rule)
- }
- }
- semanticRoles.view.Rules = append(semanticRoles.view.Rules, viewEscalatingNamespaceResources...)
- // confirm that the view role doesn't have ungettable resources
- for _, rule := range ungettableResources {
- if covers, _ := rbacregistryvalidation.Covers(semanticRoles.view.Rules, []rbacv1.PolicyRule{rule}); covers {
- t.Errorf("view has ungettable resource: %#v", rule)
- }
- }
- semanticRoles.view.Rules = append(semanticRoles.view.Rules, ungettableResources...)
- }
- func TestBootstrapNamespaceRoles(t *testing.T) {
- list := &api.List{}
- names := sets.NewString()
- roles := map[string]runtime.Object{}
- namespaceRoles := bootstrappolicy.NamespaceRoles()
- for _, namespace := range sets.StringKeySet(namespaceRoles).List() {
- bootstrapRoles := namespaceRoles[namespace]
- for i := range bootstrapRoles {
- role := bootstrapRoles[i]
- names.Insert(role.Name)
- roles[role.Name] = &role
- }
- for _, name := range names.List() {
- list.Items = append(list.Items, roles[name])
- }
- }
- 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()
- roles := map[string]runtime.Object{}
- bootstrapRoles := bootstrappolicy.ClusterRoles()
- for i := range bootstrapRoles {
- role := bootstrapRoles[i]
- names.Insert(role.Name)
- roles[role.Name] = &role
- }
- for _, name := range names.List() {
- list.Items = append(list.Items, roles[name])
- }
- testObjects(t, list, "cluster-roles.yaml")
- }
- func TestBootstrapClusterRoleBindings(t *testing.T) {
- list := &api.List{}
- names := sets.NewString()
- roleBindings := map[string]runtime.Object{}
- bootstrapRoleBindings := bootstrappolicy.ClusterRoleBindings()
- for i := range bootstrapRoleBindings {
- role := bootstrapRoleBindings[i]
- names.Insert(role.Name)
- roleBindings[role.Name] = &role
- }
- for _, name := range names.List() {
- list.Items = append(list.Items, roleBindings[name])
- }
- testObjects(t, list, "cluster-role-bindings.yaml")
- }
- func TestBootstrapControllerRoles(t *testing.T) {
- list := &api.List{}
- names := sets.NewString()
- roles := map[string]runtime.Object{}
- bootstrapRoles := bootstrappolicy.ControllerRoles()
- for i := range bootstrapRoles {
- role := bootstrapRoles[i]
- names.Insert(role.Name)
- roles[role.Name] = &role
- }
- for _, name := range names.List() {
- list.Items = append(list.Items, roles[name])
- }
- testObjects(t, list, "controller-roles.yaml")
- }
- func TestBootstrapControllerRoleBindings(t *testing.T) {
- list := &api.List{}
- names := sets.NewString()
- roleBindings := map[string]runtime.Object{}
- bootstrapRoleBindings := bootstrappolicy.ControllerRoleBindings()
- 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, "controller-role-bindings.yaml")
- }
- func testObjects(t *testing.T, list *api.List, fixtureFilename string) {
- filename := filepath.Join("testdata", fixtureFilename)
- expectedYAML, err := ioutil.ReadFile(filename)
- if err != nil {
- t.Fatal(err)
- }
- if err := runtime.EncodeList(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, rbacv1.SchemeGroupVersion), list.Items); err != nil {
- t.Fatal(err)
- }
- jsonData, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, rbacv1.SchemeGroupVersion), list)
- if err != nil {
- t.Fatal(err)
- }
- yamlData, err := yaml.JSONToYAML(jsonData)
- if err != nil {
- t.Fatal(err)
- }
- if string(yamlData) != string(expectedYAML) {
- t.Errorf("Bootstrap policy data does not match the test fixture in %s", filename)
- const updateEnvVar = "UPDATE_BOOTSTRAP_POLICY_FIXTURE_DATA"
- if os.Getenv(updateEnvVar) == "true" {
- if err := ioutil.WriteFile(filename, []byte(yamlData), os.FileMode(0755)); err == nil {
- t.Logf("Updated data in %s", filename)
- t.Logf("Verify the diff, commit changes, and rerun the tests")
- } else {
- t.Logf("Could not update data in %s: %v", filename, err)
- }
- } else {
- t.Logf("Diff between bootstrap data and fixture data in %s:\n-------------\n%s", filename, diff.StringDiff(string(yamlData), string(expectedYAML)))
- t.Logf("If the change is expected, re-run with %s=true to update the fixtures", updateEnvVar)
- }
- }
- }
- func TestClusterRoleLabel(t *testing.T) {
- roles := bootstrappolicy.ClusterRoles()
- for i := range roles {
- role := roles[i]
- accessor, err := meta.Accessor(&role)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- 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"})
- }
- }
- rolebindings := bootstrappolicy.ClusterRoleBindings()
- for i := range rolebindings {
- rolebinding := rolebindings[i]
- accessor, err := meta.Accessor(&rolebinding)
- if err != nil {
- 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("ClusterRoleBinding: %s GetLabels() = %s, want %s", accessor.GetName(), got, want)
- }
- }
- }
|