...

Source file src/k8s.io/kubernetes/pkg/kubeapiserver/options/authorization.go

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

     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 options
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"time"
    23  
    24  	genericfeatures "k8s.io/apiserver/pkg/features"
    25  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    26  
    27  	"github.com/spf13/pflag"
    28  
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/util/sets"
    31  	"k8s.io/apimachinery/pkg/util/wait"
    32  	authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
    33  	genericoptions "k8s.io/apiserver/pkg/server/options"
    34  	versionedinformers "k8s.io/client-go/informers"
    35  
    36  	"k8s.io/kubernetes/pkg/kubeapiserver/authorizer"
    37  	authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
    38  )
    39  
    40  const (
    41  	defaultWebhookName                      = "default"
    42  	authorizationModeFlag                   = "authorization-mode"
    43  	authorizationWebhookConfigFileFlag      = "authorization-webhook-config-file"
    44  	authorizationWebhookVersionFlag         = "authorization-webhook-version"
    45  	authorizationWebhookAuthorizedTTLFlag   = "authorization-webhook-cache-authorized-ttl"
    46  	authorizationWebhookUnauthorizedTTLFlag = "authorization-webhook-cache-unauthorized-ttl"
    47  	authorizationPolicyFileFlag             = "authorization-policy-file"
    48  	authorizationConfigFlag                 = "authorization-config"
    49  )
    50  
    51  // BuiltInAuthorizationOptions contains all build-in authorization options for API Server
    52  type BuiltInAuthorizationOptions struct {
    53  	Modes                       []string
    54  	PolicyFile                  string
    55  	WebhookConfigFile           string
    56  	WebhookVersion              string
    57  	WebhookCacheAuthorizedTTL   time.Duration
    58  	WebhookCacheUnauthorizedTTL time.Duration
    59  	// WebhookRetryBackoff specifies the backoff parameters for the authorization webhook retry logic.
    60  	// This allows us to configure the sleep time at each iteration and the maximum number of retries allowed
    61  	// before we fail the webhook call in order to limit the fan out that ensues when the system is degraded.
    62  	WebhookRetryBackoff *wait.Backoff
    63  
    64  	// AuthorizationConfigurationFile is mutually exclusive with all of:
    65  	//	- Modes
    66  	//	- WebhookConfigFile
    67  	//	- WebHookVersion
    68  	//	- WebhookCacheAuthorizedTTL
    69  	//	- WebhookCacheUnauthorizedTTL
    70  	AuthorizationConfigurationFile string
    71  
    72  	AreLegacyFlagsSet func() bool
    73  }
    74  
    75  // NewBuiltInAuthorizationOptions create a BuiltInAuthorizationOptions with default value
    76  func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions {
    77  	return &BuiltInAuthorizationOptions{
    78  		Modes:                       []string{},
    79  		WebhookVersion:              "v1beta1",
    80  		WebhookCacheAuthorizedTTL:   5 * time.Minute,
    81  		WebhookCacheUnauthorizedTTL: 30 * time.Second,
    82  		WebhookRetryBackoff:         genericoptions.DefaultAuthWebhookRetryBackoff(),
    83  	}
    84  }
    85  
    86  // Complete modifies authorization options
    87  func (o *BuiltInAuthorizationOptions) Complete() []error {
    88  	if len(o.AuthorizationConfigurationFile) == 0 && len(o.Modes) == 0 {
    89  		o.Modes = []string{authzmodes.ModeAlwaysAllow}
    90  	}
    91  	return nil
    92  }
    93  
    94  // Validate checks invalid config combination
    95  func (o *BuiltInAuthorizationOptions) Validate() []error {
    96  	if o == nil {
    97  		return nil
    98  	}
    99  	var allErrors []error
   100  
   101  	// if --authorization-config is set, check if
   102  	// 	- the feature flag is set
   103  	//	- legacyFlags are not set
   104  	//	- the config file can be loaded
   105  	//	- the config file represents a valid configuration
   106  	if o.AuthorizationConfigurationFile != "" {
   107  		if !utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StructuredAuthorizationConfiguration) {
   108  			return append(allErrors, fmt.Errorf("--%s cannot be used without enabling StructuredAuthorizationConfiguration feature flag", authorizationConfigFlag))
   109  		}
   110  
   111  		// error out if legacy flags are defined
   112  		if o.AreLegacyFlagsSet != nil && o.AreLegacyFlagsSet() {
   113  			return append(allErrors, fmt.Errorf("--%s can not be specified when --%s or --authorization-webhook-* flags are defined", authorizationConfigFlag, authorizationModeFlag))
   114  		}
   115  
   116  		// load/validate kube-apiserver authz config with no opinion about required modes
   117  		_, err := authorizer.LoadAndValidateFile(o.AuthorizationConfigurationFile, nil)
   118  		if err != nil {
   119  			return append(allErrors, err)
   120  		}
   121  
   122  		return allErrors
   123  	}
   124  
   125  	// validate the legacy flags using the legacy mode if --authorization-config is not passed
   126  	if len(o.Modes) == 0 {
   127  		allErrors = append(allErrors, fmt.Errorf("at least one authorization-mode must be passed"))
   128  	}
   129  
   130  	modes := sets.NewString(o.Modes...)
   131  	for _, mode := range o.Modes {
   132  		if !authzmodes.IsValidAuthorizationMode(mode) {
   133  			allErrors = append(allErrors, fmt.Errorf("authorization-mode %q is not a valid mode", mode))
   134  		}
   135  		if mode == authzmodes.ModeABAC && o.PolicyFile == "" {
   136  			allErrors = append(allErrors, fmt.Errorf("authorization-mode ABAC's authorization policy file not passed"))
   137  		}
   138  		if mode == authzmodes.ModeWebhook && o.WebhookConfigFile == "" {
   139  			allErrors = append(allErrors, fmt.Errorf("authorization-mode Webhook's authorization config file not passed"))
   140  		}
   141  	}
   142  
   143  	if o.PolicyFile != "" && !modes.Has(authzmodes.ModeABAC) {
   144  		allErrors = append(allErrors, fmt.Errorf("cannot specify --authorization-policy-file without mode ABAC"))
   145  	}
   146  
   147  	if o.WebhookConfigFile != "" && !modes.Has(authzmodes.ModeWebhook) {
   148  		allErrors = append(allErrors, fmt.Errorf("cannot specify --authorization-webhook-config-file without mode Webhook"))
   149  	}
   150  
   151  	if len(o.Modes) != modes.Len() {
   152  		allErrors = append(allErrors, fmt.Errorf("authorization-mode %q has mode specified more than once", o.Modes))
   153  	}
   154  
   155  	if o.WebhookRetryBackoff != nil && o.WebhookRetryBackoff.Steps <= 0 {
   156  		allErrors = append(allErrors, fmt.Errorf("number of webhook retry attempts must be greater than 0, but is: %d", o.WebhookRetryBackoff.Steps))
   157  	}
   158  
   159  	return allErrors
   160  }
   161  
   162  // AddFlags returns flags of authorization for a API Server
   163  func (o *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
   164  	if o == nil {
   165  		return
   166  	}
   167  
   168  	fs.StringSliceVar(&o.Modes, authorizationModeFlag, o.Modes, ""+
   169  		"Ordered list of plug-ins to do authorization on secure port. Defaults to AlwaysAllow if --authorization-config is not used. Comma-delimited list of: "+
   170  		strings.Join(authzmodes.AuthorizationModeChoices, ",")+".")
   171  
   172  	fs.StringVar(&o.PolicyFile, authorizationPolicyFileFlag, o.PolicyFile, ""+
   173  		"File with authorization policy in json line by line format, used with --authorization-mode=ABAC, on the secure port.")
   174  
   175  	fs.StringVar(&o.WebhookConfigFile, authorizationWebhookConfigFileFlag, o.WebhookConfigFile, ""+
   176  		"File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. "+
   177  		"The API server will query the remote service to determine access on the API server's secure port.")
   178  
   179  	fs.StringVar(&o.WebhookVersion, authorizationWebhookVersionFlag, o.WebhookVersion, ""+
   180  		"The API version of the authorization.k8s.io SubjectAccessReview to send to and expect from the webhook.")
   181  
   182  	fs.DurationVar(&o.WebhookCacheAuthorizedTTL, authorizationWebhookAuthorizedTTLFlag,
   183  		o.WebhookCacheAuthorizedTTL,
   184  		"The duration to cache 'authorized' responses from the webhook authorizer.")
   185  
   186  	fs.DurationVar(&o.WebhookCacheUnauthorizedTTL,
   187  		authorizationWebhookUnauthorizedTTLFlag, o.WebhookCacheUnauthorizedTTL,
   188  		"The duration to cache 'unauthorized' responses from the webhook authorizer.")
   189  
   190  	fs.StringVar(&o.AuthorizationConfigurationFile, authorizationConfigFlag, o.AuthorizationConfigurationFile, ""+
   191  		"File with Authorization Configuration to configure the authorizer chain."+
   192  		"Note: This feature is in Alpha since v1.29."+
   193  		"--feature-gate=StructuredAuthorizationConfiguration=true feature flag needs to be set to true for enabling the functionality."+
   194  		"This feature is mutually exclusive with the other --authorization-mode and --authorization-webhook-* flags.")
   195  
   196  	// preserves compatibility with any method set during initialization
   197  	oldAreLegacyFlagsSet := o.AreLegacyFlagsSet
   198  	o.AreLegacyFlagsSet = func() bool {
   199  		if oldAreLegacyFlagsSet != nil && oldAreLegacyFlagsSet() {
   200  			return true
   201  		}
   202  
   203  		return fs.Changed(authorizationModeFlag) ||
   204  			fs.Changed(authorizationWebhookConfigFileFlag) ||
   205  			fs.Changed(authorizationWebhookVersionFlag) ||
   206  			fs.Changed(authorizationWebhookAuthorizedTTLFlag) ||
   207  			fs.Changed(authorizationWebhookUnauthorizedTTLFlag)
   208  	}
   209  }
   210  
   211  // ToAuthorizationConfig convert BuiltInAuthorizationOptions to authorizer.Config
   212  func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFactory versionedinformers.SharedInformerFactory) (*authorizer.Config, error) {
   213  	if o == nil {
   214  		return nil, nil
   215  	}
   216  
   217  	var authorizationConfiguration *authzconfig.AuthorizationConfiguration
   218  	var err error
   219  
   220  	// if --authorization-config is set, check if
   221  	// 	- the feature flag is set
   222  	//	- legacyFlags are not set
   223  	//	- the config file can be loaded
   224  	//	- the config file represents a valid configuration
   225  	// else,
   226  	//	- build the AuthorizationConfig from the legacy flags
   227  	if o.AuthorizationConfigurationFile != "" {
   228  		if !utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StructuredAuthorizationConfiguration) {
   229  			return nil, fmt.Errorf("--%s cannot be used without enabling StructuredAuthorizationConfiguration feature flag", authorizationConfigFlag)
   230  		}
   231  		// error out if legacy flags are defined
   232  		if o.AreLegacyFlagsSet != nil && o.AreLegacyFlagsSet() {
   233  			return nil, fmt.Errorf("--%s can not be specified when --%s or --authorization-webhook-* flags are defined", authorizationConfigFlag, authorizationModeFlag)
   234  		}
   235  		// load/validate kube-apiserver authz config with no opinion about required modes
   236  		authorizationConfiguration, err = authorizer.LoadAndValidateFile(o.AuthorizationConfigurationFile, nil)
   237  		if err != nil {
   238  			return nil, err
   239  		}
   240  	} else {
   241  		authorizationConfiguration, err = o.buildAuthorizationConfiguration()
   242  		if err != nil {
   243  			return nil, fmt.Errorf("failed to build authorization config: %s", err)
   244  		}
   245  	}
   246  
   247  	return &authorizer.Config{
   248  		PolicyFile:               o.PolicyFile,
   249  		VersionedInformerFactory: versionedInformerFactory,
   250  		WebhookRetryBackoff:      o.WebhookRetryBackoff,
   251  
   252  		ReloadFile:                 o.AuthorizationConfigurationFile,
   253  		AuthorizationConfiguration: authorizationConfiguration,
   254  	}, nil
   255  }
   256  
   257  // buildAuthorizationConfiguration converts existing flags to the AuthorizationConfiguration format
   258  func (o *BuiltInAuthorizationOptions) buildAuthorizationConfiguration() (*authzconfig.AuthorizationConfiguration, error) {
   259  	var authorizers []authzconfig.AuthorizerConfiguration
   260  
   261  	if len(o.Modes) != sets.NewString(o.Modes...).Len() {
   262  		return nil, fmt.Errorf("modes should not be repeated in --authorization-mode")
   263  	}
   264  
   265  	for _, mode := range o.Modes {
   266  		switch mode {
   267  		case authzmodes.ModeWebhook:
   268  			authorizers = append(authorizers, authzconfig.AuthorizerConfiguration{
   269  				Type: authzconfig.TypeWebhook,
   270  				Name: defaultWebhookName,
   271  				Webhook: &authzconfig.WebhookConfiguration{
   272  					AuthorizedTTL:   metav1.Duration{Duration: o.WebhookCacheAuthorizedTTL},
   273  					UnauthorizedTTL: metav1.Duration{Duration: o.WebhookCacheUnauthorizedTTL},
   274  					// Timeout and FailurePolicy are required for the new configuration.
   275  					// Setting these two implicitly to preserve backward compatibility.
   276  					Timeout:                    metav1.Duration{Duration: 30 * time.Second},
   277  					FailurePolicy:              authzconfig.FailurePolicyNoOpinion,
   278  					SubjectAccessReviewVersion: o.WebhookVersion,
   279  					ConnectionInfo: authzconfig.WebhookConnectionInfo{
   280  						Type:           authzconfig.AuthorizationWebhookConnectionInfoTypeKubeConfigFile,
   281  						KubeConfigFile: &o.WebhookConfigFile,
   282  					},
   283  				},
   284  			})
   285  		default:
   286  			authorizers = append(authorizers, authzconfig.AuthorizerConfiguration{
   287  				Type: authzconfig.AuthorizerType(mode),
   288  				Name: authorizer.GetNameForAuthorizerMode(mode),
   289  			})
   290  		}
   291  	}
   292  
   293  	return &authzconfig.AuthorizationConfiguration{Authorizers: authorizers}, nil
   294  }
   295  

View as plain text