use AuthorizationConfiguration in kube-apiserver for storing authorizer config

Signed-off-by: Nabarun Pal <pal.nabarun95@gmail.com>
This commit is contained in:
Nabarun Pal 2023-09-14 19:14:46 +05:30
parent 52c582ca77
commit 108d195595
No known key found for this signature in database
GPG Key ID: E71158161DF2A2CB
3 changed files with 78 additions and 38 deletions

View File

@ -174,7 +174,10 @@ func BuildGenericConfig(
// BuildAuthorizer constructs the authorizer // BuildAuthorizer constructs the authorizer
func BuildAuthorizer(s controlplaneapiserver.CompletedOptions, EgressSelector *egressselector.EgressSelector, versionedInformers clientgoinformers.SharedInformerFactory) (authorizer.Authorizer, authorizer.RuleResolver, error) { 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 { if EgressSelector != nil {
egressDialer, err := EgressSelector.Lookup(egressselector.ControlPlane.AsNetworkContext()) egressDialer, err := EgressSelector.Lookup(egressselector.ControlPlane.AsNetworkContext())

View File

@ -19,10 +19,9 @@ package authorizer
import ( import (
"errors" "errors"
"fmt" "fmt"
"time"
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/authorization/authorizerfactory" "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 // Config contains the data on how to authorize a request to the Kube API Server
type Config struct { type Config struct {
AuthorizationModes []string
// Options for ModeABAC // Options for ModeABAC
// Path to an ABAC policy file. // Path to an ABAC policy file.
@ -49,14 +46,6 @@ type Config struct {
// Options for ModeWebhook // 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. // 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 // 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. // 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 // Optional field, custom dial function used to connect to webhook
CustomDial utilnet.DialFunc 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 // New returns the right sort of union of multiple authorizer.Authorizer objects
// based on the authorizationMode or an error. // based on the authorizationMode or an error.
func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, 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") 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) superuserAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
authorizers = append(authorizers, superuserAuthorizer) 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. // Keep cases in sync with constant list in k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes/modes.go.
switch authorizationMode { switch configuredAuthorizer.Type {
case modes.ModeNode: case authzconfig.AuthorizerType(modes.ModeNode):
node.RegisterMetrics() node.RegisterMetrics()
graph := node.NewGraph() graph := node.NewGraph()
node.AddGraphEventHandlers( node.AddGraphEventHandlers(
@ -101,33 +94,33 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro
authorizers = append(authorizers, nodeAuthorizer) authorizers = append(authorizers, nodeAuthorizer)
ruleResolvers = append(ruleResolvers, nodeAuthorizer) ruleResolvers = append(ruleResolvers, nodeAuthorizer)
case modes.ModeAlwaysAllow: case authzconfig.AuthorizerType(modes.ModeAlwaysAllow):
alwaysAllowAuthorizer := authorizerfactory.NewAlwaysAllowAuthorizer() alwaysAllowAuthorizer := authorizerfactory.NewAlwaysAllowAuthorizer()
authorizers = append(authorizers, alwaysAllowAuthorizer) authorizers = append(authorizers, alwaysAllowAuthorizer)
ruleResolvers = append(ruleResolvers, alwaysAllowAuthorizer) ruleResolvers = append(ruleResolvers, alwaysAllowAuthorizer)
case modes.ModeAlwaysDeny: case authzconfig.AuthorizerType(modes.ModeAlwaysDeny):
alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer() alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer()
authorizers = append(authorizers, alwaysDenyAuthorizer) authorizers = append(authorizers, alwaysDenyAuthorizer)
ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer) ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer)
case modes.ModeABAC: case authzconfig.AuthorizerType(modes.ModeABAC):
abacAuthorizer, err := abac.NewFromFile(config.PolicyFile) abacAuthorizer, err := abac.NewFromFile(config.PolicyFile)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
authorizers = append(authorizers, abacAuthorizer) authorizers = append(authorizers, abacAuthorizer)
ruleResolvers = append(ruleResolvers, abacAuthorizer) ruleResolvers = append(ruleResolvers, abacAuthorizer)
case modes.ModeWebhook: case authzconfig.AuthorizerType(modes.ModeWebhook):
if config.WebhookRetryBackoff == nil { if config.WebhookRetryBackoff == nil {
return nil, nil, errors.New("retry backoff parameters for authorization webhook has not been specified") 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }
webhookAuthorizer, err := webhook.New(clientConfig, webhookAuthorizer, err := webhook.New(clientConfig,
config.WebhookVersion, configuredAuthorizer.Webhook.SubjectAccessReviewVersion,
config.WebhookCacheAuthorizedTTL, configuredAuthorizer.Webhook.AuthorizedTTL.Duration,
config.WebhookCacheUnauthorizedTTL, configuredAuthorizer.Webhook.UnauthorizedTTL.Duration,
*config.WebhookRetryBackoff, *config.WebhookRetryBackoff,
) )
if err != nil { if err != nil {
@ -135,7 +128,7 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro
} }
authorizers = append(authorizers, webhookAuthorizer) authorizers = append(authorizers, webhookAuthorizer)
ruleResolvers = append(ruleResolvers, webhookAuthorizer) ruleResolvers = append(ruleResolvers, webhookAuthorizer)
case modes.ModeRBAC: case authzconfig.AuthorizerType(modes.ModeRBAC):
rbacAuthorizer := rbac.New( rbacAuthorizer := rbac.New(
&rbac.RoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().Roles().Lister()}, &rbac.RoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().Roles().Lister()},
&rbac.RoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().RoleBindings().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) authorizers = append(authorizers, rbacAuthorizer)
ruleResolvers = append(ruleResolvers, rbacAuthorizer) ruleResolvers = append(ruleResolvers, rbacAuthorizer)
default: default:
return nil, nil, fmt.Errorf("unknown authorization mode %s specified", authorizationMode) return nil, nil, fmt.Errorf("unknown authorization mode %s specified", configuredAuthorizer.Type)
} }
} }

View File

@ -23,14 +23,20 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
genericoptions "k8s.io/apiserver/pkg/server/options" genericoptions "k8s.io/apiserver/pkg/server/options"
versionedinformers "k8s.io/client-go/informers" versionedinformers "k8s.io/client-go/informers"
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer" "k8s.io/kubernetes/pkg/kubeapiserver/authorizer"
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
) )
const (
defaultWebhookName = "default"
)
// BuiltInAuthorizationOptions contains all build-in authorization options for API Server // BuiltInAuthorizationOptions contains all build-in authorization options for API Server
type BuiltInAuthorizationOptions struct { type BuiltInAuthorizationOptions struct {
Modes []string Modes []string
@ -62,7 +68,6 @@ func (o *BuiltInAuthorizationOptions) Validate() []error {
return nil return nil
} }
var allErrors []error var allErrors []error
if len(o.Modes) == 0 { if len(o.Modes) == 0 {
allErrors = append(allErrors, fmt.Errorf("at least one authorization-mode must be passed")) 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 // ToAuthorizationConfig convert BuiltInAuthorizationOptions to authorizer.Config
func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFactory versionedinformers.SharedInformerFactory) authorizer.Config { 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{ return authorizer.Config{
AuthorizationModes: o.Modes,
PolicyFile: o.PolicyFile, PolicyFile: o.PolicyFile,
WebhookConfigFile: o.WebhookConfigFile,
WebhookVersion: o.WebhookVersion,
WebhookCacheAuthorizedTTL: o.WebhookCacheAuthorizedTTL,
WebhookCacheUnauthorizedTTL: o.WebhookCacheUnauthorizedTTL,
VersionedInformerFactory: versionedInformerFactory, VersionedInformerFactory: versionedInformerFactory,
WebhookRetryBackoff: o.WebhookRetryBackoff, 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
} }