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
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())

View File

@ -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)
}
}

View File

@ -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
}