diff --git a/pkg/controlplane/apiserver/config.go b/pkg/controlplane/apiserver/config.go index 824469107ea..2ee85df6e53 100644 --- a/pkg/controlplane/apiserver/config.go +++ b/pkg/controlplane/apiserver/config.go @@ -174,7 +174,10 @@ func BuildGenericConfig( // BuildAuthorizer constructs the authorizer func BuildAuthorizer(s controlplaneapiserver.CompletedOptions, EgressSelector *egressselector.EgressSelector, versionedInformers clientgoinformers.SharedInformerFactory) (authorizer.Authorizer, authorizer.RuleResolver, error) { - authorizationConfig := s.Authorization.ToAuthorizationConfig(versionedInformers) + authorizationConfig, err := s.Authorization.ToAuthorizationConfig(versionedInformers) + if err != nil { + return nil, nil, err + } if EgressSelector != nil { egressDialer, err := EgressSelector.Lookup(egressselector.ControlPlane.AsNetworkContext()) diff --git a/pkg/kubeapiserver/authorizer/config.go b/pkg/kubeapiserver/authorizer/config.go index f9c6aeabc86..c33e2fa24d3 100644 --- a/pkg/kubeapiserver/authorizer/config.go +++ b/pkg/kubeapiserver/authorizer/config.go @@ -19,10 +19,9 @@ package authorizer import ( "errors" "fmt" - "time" - utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/wait" + authzconfig "k8s.io/apiserver/pkg/apis/apiserver" "k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizerfactory" @@ -40,8 +39,6 @@ import ( // Config contains the data on how to authorize a request to the Kube API Server type Config struct { - AuthorizationModes []string - // Options for ModeABAC // Path to an ABAC policy file. @@ -49,14 +46,6 @@ type Config struct { // Options for ModeWebhook - // Kubeconfig file for Webhook authorization plugin. - WebhookConfigFile string - // API version of subject access reviews to send to the webhook (e.g. "v1", "v1beta1") - WebhookVersion string - // TTL for caching of authorized responses from the webhook server. - WebhookCacheAuthorizedTTL time.Duration - // TTL for caching of unauthorized responses from the webhook server. - WebhookCacheUnauthorizedTTL time.Duration // WebhookRetryBackoff specifies the backoff parameters for the authorization webhook retry logic. // This allows us to configure the sleep time at each iteration and the maximum number of retries allowed // before we fail the webhook call in order to limit the fan out that ensues when the system is degraded. @@ -66,12 +55,16 @@ type Config struct { // Optional field, custom dial function used to connect to webhook CustomDial utilnet.DialFunc + + // AuthorizationConfiguration stores the configuration for the Authorizer chain + // It will deprecate most of the above flags when GA + AuthorizationConfiguration *authzconfig.AuthorizationConfiguration } // New returns the right sort of union of multiple authorizer.Authorizer objects // based on the authorizationMode or an error. func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, error) { - if len(config.AuthorizationModes) == 0 { + if len(config.AuthorizationConfiguration.Authorizers) == 0 { return nil, nil, fmt.Errorf("at least one authorization mode must be passed") } @@ -84,10 +77,10 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro superuserAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup) authorizers = append(authorizers, superuserAuthorizer) - for _, authorizationMode := range config.AuthorizationModes { + for _, configuredAuthorizer := range config.AuthorizationConfiguration.Authorizers { // Keep cases in sync with constant list in k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes/modes.go. - switch authorizationMode { - case modes.ModeNode: + switch configuredAuthorizer.Type { + case authzconfig.AuthorizerType(modes.ModeNode): node.RegisterMetrics() graph := node.NewGraph() node.AddGraphEventHandlers( @@ -101,33 +94,33 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro authorizers = append(authorizers, nodeAuthorizer) ruleResolvers = append(ruleResolvers, nodeAuthorizer) - case modes.ModeAlwaysAllow: + case authzconfig.AuthorizerType(modes.ModeAlwaysAllow): alwaysAllowAuthorizer := authorizerfactory.NewAlwaysAllowAuthorizer() authorizers = append(authorizers, alwaysAllowAuthorizer) ruleResolvers = append(ruleResolvers, alwaysAllowAuthorizer) - case modes.ModeAlwaysDeny: + case authzconfig.AuthorizerType(modes.ModeAlwaysDeny): alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer() authorizers = append(authorizers, alwaysDenyAuthorizer) ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer) - case modes.ModeABAC: + case authzconfig.AuthorizerType(modes.ModeABAC): abacAuthorizer, err := abac.NewFromFile(config.PolicyFile) if err != nil { return nil, nil, err } authorizers = append(authorizers, abacAuthorizer) ruleResolvers = append(ruleResolvers, abacAuthorizer) - case modes.ModeWebhook: + case authzconfig.AuthorizerType(modes.ModeWebhook): if config.WebhookRetryBackoff == nil { return nil, nil, errors.New("retry backoff parameters for authorization webhook has not been specified") } - clientConfig, err := webhookutil.LoadKubeconfig(config.WebhookConfigFile, config.CustomDial) + clientConfig, err := webhookutil.LoadKubeconfig(*configuredAuthorizer.Webhook.ConnectionInfo.KubeConfigFile, config.CustomDial) if err != nil { return nil, nil, err } webhookAuthorizer, err := webhook.New(clientConfig, - config.WebhookVersion, - config.WebhookCacheAuthorizedTTL, - config.WebhookCacheUnauthorizedTTL, + configuredAuthorizer.Webhook.SubjectAccessReviewVersion, + configuredAuthorizer.Webhook.AuthorizedTTL.Duration, + configuredAuthorizer.Webhook.UnauthorizedTTL.Duration, *config.WebhookRetryBackoff, ) if err != nil { @@ -135,7 +128,7 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro } authorizers = append(authorizers, webhookAuthorizer) ruleResolvers = append(ruleResolvers, webhookAuthorizer) - case modes.ModeRBAC: + case authzconfig.AuthorizerType(modes.ModeRBAC): rbacAuthorizer := rbac.New( &rbac.RoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().Roles().Lister()}, &rbac.RoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().RoleBindings().Lister()}, @@ -145,7 +138,7 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro authorizers = append(authorizers, rbacAuthorizer) ruleResolvers = append(ruleResolvers, rbacAuthorizer) default: - return nil, nil, fmt.Errorf("unknown authorization mode %s specified", authorizationMode) + return nil, nil, fmt.Errorf("unknown authorization mode %s specified", configuredAuthorizer.Type) } } diff --git a/pkg/kubeapiserver/options/authorization.go b/pkg/kubeapiserver/options/authorization.go index 778ca1210d7..b9491809754 100644 --- a/pkg/kubeapiserver/options/authorization.go +++ b/pkg/kubeapiserver/options/authorization.go @@ -23,14 +23,20 @@ import ( "github.com/spf13/pflag" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" + authzconfig "k8s.io/apiserver/pkg/apis/apiserver" genericoptions "k8s.io/apiserver/pkg/server/options" versionedinformers "k8s.io/client-go/informers" "k8s.io/kubernetes/pkg/kubeapiserver/authorizer" authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" ) +const ( + defaultWebhookName = "default" +) + // BuiltInAuthorizationOptions contains all build-in authorization options for API Server type BuiltInAuthorizationOptions struct { Modes []string @@ -62,7 +68,6 @@ func (o *BuiltInAuthorizationOptions) Validate() []error { return nil } var allErrors []error - if len(o.Modes) == 0 { allErrors = append(allErrors, fmt.Errorf("at least one authorization-mode must be passed")) } @@ -125,15 +130,54 @@ func (o *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) { } // ToAuthorizationConfig convert BuiltInAuthorizationOptions to authorizer.Config -func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFactory versionedinformers.SharedInformerFactory) authorizer.Config { - return authorizer.Config{ - AuthorizationModes: o.Modes, - PolicyFile: o.PolicyFile, - WebhookConfigFile: o.WebhookConfigFile, - WebhookVersion: o.WebhookVersion, - WebhookCacheAuthorizedTTL: o.WebhookCacheAuthorizedTTL, - WebhookCacheUnauthorizedTTL: o.WebhookCacheUnauthorizedTTL, - VersionedInformerFactory: versionedInformerFactory, - WebhookRetryBackoff: o.WebhookRetryBackoff, +func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFactory versionedinformers.SharedInformerFactory) (authorizer.Config, error) { + + authzConfiguration, err := o.buildAuthorizationConfiguration() + if err != nil { + return authorizer.Config{}, fmt.Errorf("failed to build authorization config: %s", err) } + + return authorizer.Config{ + PolicyFile: o.PolicyFile, + VersionedInformerFactory: versionedInformerFactory, + WebhookRetryBackoff: o.WebhookRetryBackoff, + + AuthorizationConfiguration: authzConfiguration, + }, nil +} + +// buildAuthorizationConfiguration converts existing flags to the AuthorizationConfiguration format +func (o *BuiltInAuthorizationOptions) buildAuthorizationConfiguration() (*authzconfig.AuthorizationConfiguration, error) { + var authorizers []authzconfig.AuthorizerConfiguration + + if len(o.Modes) != sets.NewString(o.Modes...).Len() { + return nil, fmt.Errorf("modes should not be repeated in --authorization-mode") + } + + for _, mode := range o.Modes { + switch mode { + case authzmodes.ModeWebhook: + authorizers = append(authorizers, authzconfig.AuthorizerConfiguration{ + Type: authzconfig.TypeWebhook, + Webhook: &authzconfig.WebhookConfiguration{ + Name: defaultWebhookName, + AuthorizedTTL: metav1.Duration{Duration: o.WebhookCacheAuthorizedTTL}, + UnauthorizedTTL: metav1.Duration{Duration: o.WebhookCacheUnauthorizedTTL}, + // Timeout and FailurePolicy are required for the new configuration. + // Setting these two implicitly to preserve backward compatibility. + Timeout: metav1.Duration{Duration: 30 * time.Second}, + FailurePolicy: authzconfig.FailurePolicyNoOpinion, + SubjectAccessReviewVersion: o.WebhookVersion, + ConnectionInfo: authzconfig.WebhookConnectionInfo{ + Type: authzconfig.AuthorizationWebhookConnectionInfoTypeKubeConfig, + KubeConfigFile: &o.WebhookConfigFile, + }, + }, + }) + default: + authorizers = append(authorizers, authzconfig.AuthorizerConfiguration{Type: authzconfig.AuthorizerType(mode)}) + } + } + + return &authzconfig.AuthorizationConfiguration{Authorizers: authorizers}, nil }