...

Source file src/k8s.io/kubernetes/pkg/kubeapiserver/authorizer/config.go

Documentation: k8s.io/kubernetes/pkg/kubeapiserver/authorizer

     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 authorizer
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"os"
    23  	"strings"
    24  	"time"
    25  
    26  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    27  	utilnet "k8s.io/apimachinery/pkg/util/net"
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  	"k8s.io/apimachinery/pkg/util/wait"
    30  	authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
    31  	"k8s.io/apiserver/pkg/apis/apiserver/load"
    32  	"k8s.io/apiserver/pkg/apis/apiserver/validation"
    33  	"k8s.io/apiserver/pkg/authorization/authorizer"
    34  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    35  	versionedinformers "k8s.io/client-go/informers"
    36  	resourcev1alpha2informers "k8s.io/client-go/informers/resource/v1alpha2"
    37  	"k8s.io/kubernetes/pkg/auth/authorizer/abac"
    38  	"k8s.io/kubernetes/pkg/auth/nodeidentifier"
    39  	"k8s.io/kubernetes/pkg/features"
    40  	"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
    41  	"k8s.io/kubernetes/plugin/pkg/auth/authorizer/node"
    42  	"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
    43  	"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
    44  )
    45  
    46  // Config contains the data on how to authorize a request to the Kube API Server
    47  type Config struct {
    48  	// Options for ModeABAC
    49  
    50  	// Path to an ABAC policy file.
    51  	PolicyFile string
    52  
    53  	// Options for ModeWebhook
    54  
    55  	// WebhookRetryBackoff specifies the backoff parameters for the authorization webhook retry logic.
    56  	// This allows us to configure the sleep time at each iteration and the maximum number of retries allowed
    57  	// before we fail the webhook call in order to limit the fan out that ensues when the system is degraded.
    58  	WebhookRetryBackoff *wait.Backoff
    59  
    60  	VersionedInformerFactory versionedinformers.SharedInformerFactory
    61  
    62  	// Optional field, custom dial function used to connect to webhook
    63  	CustomDial utilnet.DialFunc
    64  
    65  	// ReloadFile holds the filename to reload authorization configuration from
    66  	ReloadFile string
    67  	// AuthorizationConfiguration stores the configuration for the Authorizer chain
    68  	// It will deprecate most of the above flags when GA
    69  	AuthorizationConfiguration *authzconfig.AuthorizationConfiguration
    70  }
    71  
    72  // New returns the right sort of union of multiple authorizer.Authorizer objects
    73  // based on the authorizationMode or an error.
    74  // stopCh is used to shut down config reload goroutines when the server is shutting down.
    75  func (config Config) New(ctx context.Context, serverID string) (authorizer.Authorizer, authorizer.RuleResolver, error) {
    76  	if len(config.AuthorizationConfiguration.Authorizers) == 0 {
    77  		return nil, nil, fmt.Errorf("at least one authorization mode must be passed")
    78  	}
    79  
    80  	r := &reloadableAuthorizerResolver{
    81  		initialConfig:    config,
    82  		apiServerID:      serverID,
    83  		lastLoadedConfig: config.AuthorizationConfiguration,
    84  		reloadInterval:   time.Minute,
    85  	}
    86  
    87  	seenTypes := sets.New[authzconfig.AuthorizerType]()
    88  
    89  	// Build and store authorizers which will persist across reloads
    90  	for _, configuredAuthorizer := range config.AuthorizationConfiguration.Authorizers {
    91  		seenTypes.Insert(configuredAuthorizer.Type)
    92  
    93  		// Keep cases in sync with constant list in k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes/modes.go.
    94  		switch configuredAuthorizer.Type {
    95  		case authzconfig.AuthorizerType(modes.ModeNode):
    96  			var slices resourcev1alpha2informers.ResourceSliceInformer
    97  			if utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) {
    98  				slices = config.VersionedInformerFactory.Resource().V1alpha2().ResourceSlices()
    99  			}
   100  			node.RegisterMetrics()
   101  			graph := node.NewGraph()
   102  			node.AddGraphEventHandlers(
   103  				graph,
   104  				config.VersionedInformerFactory.Core().V1().Nodes(),
   105  				config.VersionedInformerFactory.Core().V1().Pods(),
   106  				config.VersionedInformerFactory.Core().V1().PersistentVolumes(),
   107  				config.VersionedInformerFactory.Storage().V1().VolumeAttachments(),
   108  				slices, // Nil check in AddGraphEventHandlers can be removed when always creating this.
   109  			)
   110  			r.nodeAuthorizer = node.NewAuthorizer(graph, nodeidentifier.NewDefaultNodeIdentifier(), bootstrappolicy.NodeRules())
   111  
   112  		case authzconfig.AuthorizerType(modes.ModeABAC):
   113  			var err error
   114  			r.abacAuthorizer, err = abac.NewFromFile(config.PolicyFile)
   115  			if err != nil {
   116  				return nil, nil, err
   117  			}
   118  		case authzconfig.AuthorizerType(modes.ModeRBAC):
   119  			r.rbacAuthorizer = rbac.New(
   120  				&rbac.RoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().Roles().Lister()},
   121  				&rbac.RoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().RoleBindings().Lister()},
   122  				&rbac.ClusterRoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().ClusterRoles().Lister()},
   123  				&rbac.ClusterRoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().ClusterRoleBindings().Lister()},
   124  			)
   125  		}
   126  	}
   127  
   128  	// Require all non-webhook authorizer types to remain specified in the file on reload
   129  	seenTypes.Delete(authzconfig.TypeWebhook)
   130  	r.requireNonWebhookTypes = seenTypes
   131  
   132  	// Construct the authorizers / ruleResolvers for the given configuration
   133  	authorizer, ruleResolver, err := r.newForConfig(r.initialConfig.AuthorizationConfiguration)
   134  	if err != nil {
   135  		return nil, nil, err
   136  	}
   137  
   138  	r.current.Store(&authorizerResolver{
   139  		authorizer:   authorizer,
   140  		ruleResolver: ruleResolver,
   141  	})
   142  
   143  	if r.initialConfig.ReloadFile != "" {
   144  		go r.runReload(ctx)
   145  	}
   146  
   147  	return r, r, nil
   148  }
   149  
   150  // RepeatableAuthorizerTypes is the list of Authorizer that can be repeated in the Authorization Config
   151  var repeatableAuthorizerTypes = []string{modes.ModeWebhook}
   152  
   153  // GetNameForAuthorizerMode returns the name to be set for the mode in AuthorizationConfiguration
   154  // For now, lower cases the mode name
   155  func GetNameForAuthorizerMode(mode string) string {
   156  	return strings.ToLower(mode)
   157  }
   158  
   159  func LoadAndValidateFile(configFile string, requireNonWebhookTypes sets.Set[authzconfig.AuthorizerType]) (*authzconfig.AuthorizationConfiguration, error) {
   160  	data, err := os.ReadFile(configFile)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	return LoadAndValidateData(data, requireNonWebhookTypes)
   165  }
   166  
   167  func LoadAndValidateData(data []byte, requireNonWebhookTypes sets.Set[authzconfig.AuthorizerType]) (*authzconfig.AuthorizationConfiguration, error) {
   168  	// load the file and check for errors
   169  	authorizationConfiguration, err := load.LoadFromData(data)
   170  	if err != nil {
   171  		return nil, fmt.Errorf("failed to load AuthorizationConfiguration from file: %w", err)
   172  	}
   173  
   174  	// validate the file and return any error
   175  	if errors := validation.ValidateAuthorizationConfiguration(nil, authorizationConfiguration,
   176  		sets.NewString(modes.AuthorizationModeChoices...),
   177  		sets.NewString(repeatableAuthorizerTypes...),
   178  	); len(errors) != 0 {
   179  		return nil, fmt.Errorf(errors.ToAggregate().Error())
   180  	}
   181  
   182  	// test to check if the authorizer names passed conform to the authorizers for type!=Webhook
   183  	// this test is only for kube-apiserver and hence checked here
   184  	// it preserves compatibility with o.buildAuthorizationConfiguration
   185  	var allErrors []error
   186  	seenModes := sets.New[authzconfig.AuthorizerType]()
   187  	for _, authorizer := range authorizationConfiguration.Authorizers {
   188  		if string(authorizer.Type) == modes.ModeWebhook {
   189  			continue
   190  		}
   191  		seenModes.Insert(authorizer.Type)
   192  
   193  		expectedName := GetNameForAuthorizerMode(string(authorizer.Type))
   194  		if expectedName != authorizer.Name {
   195  			allErrors = append(allErrors, fmt.Errorf("expected name %s for authorizer %s instead of %s", expectedName, authorizer.Type, authorizer.Name))
   196  		}
   197  
   198  	}
   199  
   200  	if missingTypes := requireNonWebhookTypes.Difference(seenModes); missingTypes.Len() > 0 {
   201  		allErrors = append(allErrors, fmt.Errorf("missing required types: %v", sets.List(missingTypes)))
   202  	}
   203  
   204  	if len(allErrors) > 0 {
   205  		return nil, utilerrors.NewAggregate(allErrors)
   206  	}
   207  
   208  	return authorizationConfiguration, nil
   209  }
   210  

View as plain text