...

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

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

     1  /*
     2  Copyright 2016 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 implements a standard storage for ClusterRole that prevents privilege escalation.
    18  package policybased
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  
    24  	apierrors "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/authorization/authorizer"
    28  	"k8s.io/apiserver/pkg/registry/rest"
    29  	kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
    30  	"k8s.io/kubernetes/pkg/apis/rbac"
    31  	rbacregistry "k8s.io/kubernetes/pkg/registry/rbac"
    32  	rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
    33  )
    34  
    35  var groupResource = rbac.Resource("clusterroles")
    36  
    37  type Storage struct {
    38  	rest.StandardStorage
    39  
    40  	authorizer authorizer.Authorizer
    41  
    42  	ruleResolver rbacregistryvalidation.AuthorizationRuleResolver
    43  }
    44  
    45  func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
    46  	return &Storage{s, authorizer, ruleResolver}
    47  }
    48  
    49  // Destroy cleans up resources on shutdown.
    50  func (r *Storage) Destroy() {
    51  	r.StandardStorage.Destroy()
    52  }
    53  
    54  func (r *Storage) NamespaceScoped() bool {
    55  	return false
    56  }
    57  
    58  func (r *Storage) StorageVersion() runtime.GroupVersioner {
    59  	svp, ok := r.StandardStorage.(rest.StorageVersionProvider)
    60  	if !ok {
    61  		return nil
    62  	}
    63  	return svp.StorageVersion()
    64  }
    65  
    66  var _ rest.StorageVersionProvider = &Storage{}
    67  
    68  var fullAuthority = []rbac.PolicyRule{
    69  	rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(),
    70  	rbac.NewRule("*").URLs("*").RuleOrDie(),
    71  }
    72  
    73  func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
    74  	if rbacregistry.EscalationAllowed(ctx) || rbacregistry.RoleEscalationAuthorized(ctx, s.authorizer) {
    75  		return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, options)
    76  	}
    77  
    78  	clusterRole := obj.(*rbac.ClusterRole)
    79  	rules := clusterRole.Rules
    80  	if err := rbacregistryvalidation.ConfirmNoEscalationInternal(ctx, s.ruleResolver, rules); err != nil {
    81  		return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, err)
    82  	}
    83  	// to set the aggregation rule, since it can gather anything, requires * on *.*
    84  	if hasAggregationRule(clusterRole) {
    85  		if err := rbacregistryvalidation.ConfirmNoEscalationInternal(ctx, s.ruleResolver, fullAuthority); err != nil {
    86  			return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, errors.New("must have cluster-admin privileges to use the aggregationRule"))
    87  		}
    88  	}
    89  
    90  	return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, options)
    91  }
    92  
    93  func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
    94  	if rbacregistry.EscalationAllowed(ctx) || rbacregistry.RoleEscalationAuthorized(ctx, s.authorizer) {
    95  		return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation, forceAllowCreate, options)
    96  	}
    97  
    98  	nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
    99  		clusterRole := obj.(*rbac.ClusterRole)
   100  		oldClusterRole := oldObj.(*rbac.ClusterRole)
   101  
   102  		// if we're only mutating fields needed for the GC to eventually delete this obj, return
   103  		if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) {
   104  			return obj, nil
   105  		}
   106  
   107  		rules := clusterRole.Rules
   108  		if err := rbacregistryvalidation.ConfirmNoEscalationInternal(ctx, s.ruleResolver, rules); err != nil {
   109  			return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, err)
   110  		}
   111  		// to change the aggregation rule, since it can gather anything and prevent tightening, requires * on *.*
   112  		if hasAggregationRule(clusterRole) || hasAggregationRule(oldClusterRole) {
   113  			if err := rbacregistryvalidation.ConfirmNoEscalationInternal(ctx, s.ruleResolver, fullAuthority); err != nil {
   114  				return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, errors.New("must have cluster-admin privileges to use the aggregationRule"))
   115  			}
   116  		}
   117  
   118  		return obj, nil
   119  	})
   120  
   121  	return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation, forceAllowCreate, options)
   122  }
   123  
   124  func hasAggregationRule(clusterRole *rbac.ClusterRole) bool {
   125  	return clusterRole.AggregationRule != nil && len(clusterRole.AggregationRule.ClusterRoleSelectors) > 0
   126  }
   127  
   128  var _ rest.SingularNameProvider = &Storage{}
   129  
   130  func (s *Storage) GetSingularName() string {
   131  	snp, ok := s.StandardStorage.(rest.SingularNameProvider)
   132  	if !ok {
   133  		return ""
   134  	}
   135  	return snp.GetSingularName()
   136  }
   137  

View as plain text