...

Source file src/k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased/storage_test.go

Documentation: k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package policybased
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	rbacv1 "k8s.io/api/rbac/v1"
    24  	"k8s.io/apimachinery/pkg/api/errors"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/apiserver/pkg/authentication/user"
    28  	"k8s.io/apiserver/pkg/authorization/authorizer"
    29  	"k8s.io/apiserver/pkg/endpoints/request"
    30  	"k8s.io/apiserver/pkg/registry/rest"
    31  	"k8s.io/kubernetes/pkg/apis/rbac"
    32  	_ "k8s.io/kubernetes/pkg/apis/rbac/install"
    33  	"k8s.io/kubernetes/pkg/registry/rbac/validation"
    34  )
    35  
    36  func TestEscalation(t *testing.T) {
    37  	createContext := request.WithRequestInfo(request.WithNamespace(context.TODO(), ""), &request.RequestInfo{
    38  		IsResourceRequest: true,
    39  		Verb:              "create",
    40  		APIGroup:          "rbac.authorization.k8s.io",
    41  		APIVersion:        "v1",
    42  		Resource:          "clusterroles",
    43  		Name:              "",
    44  	})
    45  	updateContext := request.WithRequestInfo(request.WithNamespace(context.TODO(), ""), &request.RequestInfo{
    46  		IsResourceRequest: true,
    47  		Verb:              "update",
    48  		APIGroup:          "rbac.authorization.k8s.io",
    49  		APIVersion:        "v1",
    50  		Resource:          "clusterroles",
    51  		Name:              "myrole",
    52  	})
    53  
    54  	superuser := &user.DefaultInfo{Name: "superuser", Groups: []string{"system:masters"}}
    55  	bob := &user.DefaultInfo{Name: "bob"}
    56  	steve := &user.DefaultInfo{Name: "steve"}
    57  	alice := &user.DefaultInfo{Name: "alice"}
    58  
    59  	authzCalled := 0
    60  	fakeStorage := &fakeStorage{}
    61  	fakeAuthorizer := authorizer.AuthorizerFunc(func(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) {
    62  		authzCalled++
    63  		if attr.GetUser().GetName() == "steve" {
    64  			return authorizer.DecisionAllow, "", nil
    65  		}
    66  		return authorizer.DecisionNoOpinion, "", nil
    67  	})
    68  	fakeRuleResolver, _ := validation.NewTestRuleResolver(
    69  		nil,
    70  		nil,
    71  		[]*rbacv1.ClusterRole{{ObjectMeta: metav1.ObjectMeta{Name: "alice-role"}, Rules: []rbacv1.PolicyRule{{APIGroups: []string{"*"}, Resources: []string{"*"}, Verbs: []string{"*"}}}}},
    72  		[]*rbacv1.ClusterRoleBinding{{RoleRef: rbacv1.RoleRef{Name: "alice-role", APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole"}, Subjects: []rbacv1.Subject{{Name: "alice", Kind: "User", APIGroup: "rbac.authorization.k8s.io"}}}},
    73  	)
    74  
    75  	role := &rbac.ClusterRole{
    76  		ObjectMeta: metav1.ObjectMeta{Name: "myrole", Namespace: ""},
    77  		Rules:      []rbac.PolicyRule{{APIGroups: []string{""}, Verbs: []string{"get"}, Resources: []string{"pods"}}},
    78  	}
    79  
    80  	s := NewStorage(fakeStorage, fakeAuthorizer, fakeRuleResolver)
    81  
    82  	testcases := []struct {
    83  		name          string
    84  		user          user.Info
    85  		expectAllowed bool
    86  		expectAuthz   bool
    87  	}{
    88  		// superuser doesn't even trigger an authz check, and is allowed
    89  		{
    90  			name:          "superuser",
    91  			user:          superuser,
    92  			expectAuthz:   false,
    93  			expectAllowed: true,
    94  		},
    95  		// bob triggers an authz check, is disallowed by the authorizer, and has no RBAC permissions, so is not allowed
    96  		{
    97  			name:          "bob",
    98  			user:          bob,
    99  			expectAuthz:   true,
   100  			expectAllowed: false,
   101  		},
   102  		// steve triggers an authz check, is allowed by the authorizer, and has no RBAC permissions, but is still allowed
   103  		{
   104  			name:          "steve",
   105  			user:          steve,
   106  			expectAuthz:   true,
   107  			expectAllowed: true,
   108  		},
   109  		// alice triggers an authz check, is denied by the authorizer, but has RBAC permissions in the fakeRuleResolver, so is allowed
   110  		{
   111  			name:          "alice",
   112  			user:          alice,
   113  			expectAuthz:   true,
   114  			expectAllowed: true,
   115  		},
   116  	}
   117  
   118  	for _, tc := range testcases {
   119  		t.Run(tc.name, func(t *testing.T) {
   120  			authzCalled, fakeStorage.created, fakeStorage.updated = 0, 0, 0
   121  			_, err := s.Create(request.WithUser(createContext, tc.user), role, nil, nil)
   122  
   123  			if tc.expectAllowed {
   124  				if err != nil {
   125  					t.Error(err)
   126  					return
   127  				}
   128  				if fakeStorage.created != 1 {
   129  					t.Errorf("unexpected calls to underlying storage.Create: %d", fakeStorage.created)
   130  					return
   131  				}
   132  			} else {
   133  				if !errors.IsForbidden(err) {
   134  					t.Errorf("expected forbidden, got %v", err)
   135  					return
   136  				}
   137  				if fakeStorage.created != 0 {
   138  					t.Errorf("unexpected calls to underlying storage.Create: %d", fakeStorage.created)
   139  					return
   140  				}
   141  			}
   142  
   143  			if tc.expectAuthz != (authzCalled > 0) {
   144  				t.Fatalf("expected authz=%v, saw %d calls", tc.expectAuthz, authzCalled)
   145  			}
   146  
   147  			authzCalled, fakeStorage.created, fakeStorage.updated = 0, 0, 0
   148  			_, _, err = s.Update(request.WithUser(updateContext, tc.user), role.Name, rest.DefaultUpdatedObjectInfo(role), nil, nil, false, nil)
   149  
   150  			if tc.expectAllowed {
   151  				if err != nil {
   152  					t.Error(err)
   153  					return
   154  				}
   155  				if fakeStorage.updated != 1 {
   156  					t.Errorf("unexpected calls to underlying storage.Update: %d", fakeStorage.updated)
   157  					return
   158  				}
   159  			} else {
   160  				if !errors.IsForbidden(err) {
   161  					t.Errorf("expected forbidden, got %v", err)
   162  					return
   163  				}
   164  				if fakeStorage.updated != 0 {
   165  					t.Errorf("unexpected calls to underlying storage.Update: %d", fakeStorage.updated)
   166  					return
   167  				}
   168  			}
   169  
   170  			if tc.expectAuthz != (authzCalled > 0) {
   171  				t.Fatalf("expected authz=%v, saw %d calls", tc.expectAuthz, authzCalled)
   172  			}
   173  		})
   174  	}
   175  }
   176  
   177  type fakeStorage struct {
   178  	updated int
   179  	created int
   180  	rest.StandardStorage
   181  }
   182  
   183  func (f *fakeStorage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
   184  	f.created++
   185  	return nil, nil
   186  }
   187  
   188  func (f *fakeStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
   189  	obj, err := objInfo.UpdatedObject(ctx, &rbac.ClusterRole{})
   190  	if err != nil {
   191  		return obj, false, err
   192  	}
   193  	f.updated++
   194  	return nil, false, nil
   195  }
   196  

View as plain text