k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubeapiserver/options/authorization.go (about) 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 }