diff --git a/pkg/kubeapiserver/authorizer/config.go b/pkg/kubeapiserver/authorizer/config.go index b2bc689e420..fd40423caa6 100644 --- a/pkg/kubeapiserver/authorizer/config.go +++ b/pkg/kubeapiserver/authorizer/config.go @@ -19,10 +19,15 @@ package authorizer import ( "errors" "fmt" + "strings" + utilerrors "k8s.io/apimachinery/pkg/util/errors" utilnet "k8s.io/apimachinery/pkg/util/net" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" authzconfig "k8s.io/apiserver/pkg/apis/apiserver" + "k8s.io/apiserver/pkg/apis/apiserver/load" + "k8s.io/apiserver/pkg/apis/apiserver/validation" "k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizerfactory" @@ -156,3 +161,56 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro return union.New(authorizers...), union.NewRuleResolvers(ruleResolvers...), nil } + +// RepeatableAuthorizerTypes is the list of Authorizer that can be repeated in the Authorization Config +var repeatableAuthorizerTypes = []string{modes.ModeWebhook} + +// GetNameForAuthorizerMode returns the name to be set for the mode in AuthorizationConfiguration +// For now, lower cases the mode name +func GetNameForAuthorizerMode(mode string) string { + return strings.ToLower(mode) +} + +func LoadAndValidateFile(configFile string, requireNonWebhookTypes sets.Set[authzconfig.AuthorizerType]) (*authzconfig.AuthorizationConfiguration, error) { + // load the file and check for errors + authorizationConfiguration, err := load.LoadFromFile(configFile) + if err != nil { + return nil, fmt.Errorf("failed to load AuthorizationConfiguration from file: %v", err) + } + + // validate the file and return any error + if errors := validation.ValidateAuthorizationConfiguration(nil, authorizationConfiguration, + sets.NewString(modes.AuthorizationModeChoices...), + sets.NewString(repeatableAuthorizerTypes...), + ); len(errors) != 0 { + return nil, fmt.Errorf(errors.ToAggregate().Error()) + } + + // test to check if the authorizer names passed conform to the authorizers for type!=Webhook + // this test is only for kube-apiserver and hence checked here + // it preserves compatibility with o.buildAuthorizationConfiguration + var allErrors []error + seenModes := sets.New[authzconfig.AuthorizerType]() + for _, authorizer := range authorizationConfiguration.Authorizers { + if string(authorizer.Type) == modes.ModeWebhook { + continue + } + seenModes.Insert(authorizer.Type) + + expectedName := GetNameForAuthorizerMode(string(authorizer.Type)) + if expectedName != authorizer.Name { + allErrors = append(allErrors, fmt.Errorf("expected name %s for authorizer %s instead of %s", expectedName, authorizer.Type, authorizer.Name)) + } + + } + + if missingTypes := requireNonWebhookTypes.Difference(seenModes); missingTypes.Len() > 0 { + allErrors = append(allErrors, fmt.Errorf("missing required types: %v", sets.List(missingTypes))) + } + + if len(allErrors) > 0 { + return nil, utilerrors.NewAggregate(allErrors) + } + + return authorizationConfiguration, nil +} diff --git a/pkg/kubeapiserver/options/authorization.go b/pkg/kubeapiserver/options/authorization.go index 02d8675e549..f1bee22f43c 100644 --- a/pkg/kubeapiserver/options/authorization.go +++ b/pkg/kubeapiserver/options/authorization.go @@ -21,7 +21,6 @@ import ( "strings" "time" - "k8s.io/apiserver/pkg/apis/apiserver/load" genericfeatures "k8s.io/apiserver/pkg/features" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -31,7 +30,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" authzconfig "k8s.io/apiserver/pkg/apis/apiserver" - "k8s.io/apiserver/pkg/apis/apiserver/validation" genericoptions "k8s.io/apiserver/pkg/server/options" versionedinformers "k8s.io/client-go/informers" @@ -50,9 +48,6 @@ const ( authorizationConfigFlag = "authorization-config" ) -// RepeatableAuthorizerTypes is the list of Authorizer that can be repeated in the Authorization Config -var repeatableAuthorizerTypes = []string{authzmodes.ModeWebhook} - // BuiltInAuthorizationOptions contains all build-in authorization options for API Server type BuiltInAuthorizationOptions struct { Modes []string @@ -118,32 +113,10 @@ func (o *BuiltInAuthorizationOptions) Validate() []error { return append(allErrors, fmt.Errorf("--%s can not be specified when --%s or --authorization-webhook-* flags are defined", authorizationConfigFlag, authorizationModeFlag)) } - // load the file and check for errors - config, err := load.LoadFromFile(o.AuthorizationConfigurationFile) + // load/validate kube-apiserver authz config with no opinion about required modes + _, err := authorizer.LoadAndValidateFile(o.AuthorizationConfigurationFile, nil) if err != nil { - return append(allErrors, fmt.Errorf("failed to load AuthorizationConfiguration from file: %v", err)) - } - - // validate the file and return any error - if errors := validation.ValidateAuthorizationConfiguration(nil, config, - sets.NewString(authzmodes.AuthorizationModeChoices...), - sets.NewString(repeatableAuthorizerTypes...), - ); len(errors) != 0 { - allErrors = append(allErrors, errors.ToAggregate().Errors()...) - } - - // test to check if the authorizer names passed conform to the authorizers for type!=Webhook - // this test is only for kube-apiserver and hence checked here - // it preserves compatibility with o.buildAuthorizationConfiguration - for _, authorizer := range config.Authorizers { - if string(authorizer.Type) == authzmodes.ModeWebhook { - continue - } - - expectedName := getNameForAuthorizerMode(string(authorizer.Type)) - if expectedName != authorizer.Name { - allErrors = append(allErrors, fmt.Errorf("expected name %s for authorizer %s instead of %s", expectedName, authorizer.Type, authorizer.Name)) - } + return append(allErrors, err) } return allErrors @@ -255,24 +228,14 @@ func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFac if !utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StructuredAuthorizationConfiguration) { return nil, fmt.Errorf("--%s cannot be used without enabling StructuredAuthorizationConfiguration feature flag", authorizationConfigFlag) } - // error out if legacy flags are defined if o.AreLegacyFlagsSet != nil && o.AreLegacyFlagsSet() { return nil, fmt.Errorf("--%s can not be specified when --%s or --authorization-webhook-* flags are defined", authorizationConfigFlag, authorizationModeFlag) } - - // load the file and check for errors - authorizationConfiguration, err = load.LoadFromFile(o.AuthorizationConfigurationFile) + // load/validate kube-apiserver authz config with no opinion about required modes + authorizationConfiguration, err = authorizer.LoadAndValidateFile(o.AuthorizationConfigurationFile, nil) if err != nil { - return nil, fmt.Errorf("failed to load AuthorizationConfiguration from file: %v", err) - } - - // validate the file and return any error - if errors := validation.ValidateAuthorizationConfiguration(nil, authorizationConfiguration, - sets.NewString(authzmodes.AuthorizationModeChoices...), - sets.NewString(repeatableAuthorizerTypes...), - ); len(errors) != 0 { - return nil, fmt.Errorf(errors.ToAggregate().Error()) + return nil, err } } else { authorizationConfiguration, err = o.buildAuthorizationConfiguration() @@ -321,16 +284,10 @@ func (o *BuiltInAuthorizationOptions) buildAuthorizationConfiguration() (*authzc default: authorizers = append(authorizers, authzconfig.AuthorizerConfiguration{ Type: authzconfig.AuthorizerType(mode), - Name: getNameForAuthorizerMode(mode), + Name: authorizer.GetNameForAuthorizerMode(mode), }) } } return &authzconfig.AuthorizationConfiguration{Authorizers: authorizers}, nil } - -// getNameForAuthorizerMode returns the name to be set for the mode in AuthorizationConfiguration -// For now, lower cases the mode name -func getNameForAuthorizerMode(mode string) string { - return strings.ToLower(mode) -}