k8s.io/kubernetes@v1.29.3/pkg/kubeapiserver/authenticator/config.go (about) 1 /* 2 Copyright 2014 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 authenticator 18 19 import ( 20 "errors" 21 "fmt" 22 "time" 23 24 utilnet "k8s.io/apimachinery/pkg/util/net" 25 "k8s.io/apimachinery/pkg/util/wait" 26 "k8s.io/apiserver/pkg/apis/apiserver" 27 "k8s.io/apiserver/pkg/authentication/authenticator" 28 "k8s.io/apiserver/pkg/authentication/authenticatorfactory" 29 "k8s.io/apiserver/pkg/authentication/group" 30 "k8s.io/apiserver/pkg/authentication/request/anonymous" 31 "k8s.io/apiserver/pkg/authentication/request/bearertoken" 32 "k8s.io/apiserver/pkg/authentication/request/headerrequest" 33 "k8s.io/apiserver/pkg/authentication/request/union" 34 "k8s.io/apiserver/pkg/authentication/request/websocket" 35 "k8s.io/apiserver/pkg/authentication/request/x509" 36 tokencache "k8s.io/apiserver/pkg/authentication/token/cache" 37 "k8s.io/apiserver/pkg/authentication/token/tokenfile" 38 tokenunion "k8s.io/apiserver/pkg/authentication/token/union" 39 "k8s.io/apiserver/pkg/server/dynamiccertificates" 40 webhookutil "k8s.io/apiserver/pkg/util/webhook" 41 "k8s.io/apiserver/plugin/pkg/authenticator/token/oidc" 42 "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook" 43 typedv1core "k8s.io/client-go/kubernetes/typed/core/v1" 44 "k8s.io/kube-openapi/pkg/spec3" 45 "k8s.io/kube-openapi/pkg/validation/spec" 46 47 // Initialize all known client auth plugins. 48 _ "k8s.io/client-go/plugin/pkg/client/auth" 49 "k8s.io/client-go/util/keyutil" 50 "k8s.io/kubernetes/pkg/serviceaccount" 51 ) 52 53 // Config contains the data on how to authenticate a request to the Kube API Server 54 type Config struct { 55 Anonymous bool 56 BootstrapToken bool 57 58 TokenAuthFile string 59 AuthenticationConfig *apiserver.AuthenticationConfiguration 60 OIDCSigningAlgs []string 61 ServiceAccountKeyFiles []string 62 ServiceAccountLookup bool 63 ServiceAccountIssuers []string 64 APIAudiences authenticator.Audiences 65 WebhookTokenAuthnConfigFile string 66 WebhookTokenAuthnVersion string 67 WebhookTokenAuthnCacheTTL time.Duration 68 // WebhookRetryBackoff specifies the backoff parameters for the authentication webhook retry logic. 69 // This allows us to configure the sleep time at each iteration and the maximum number of retries allowed 70 // before we fail the webhook call in order to limit the fan out that ensues when the system is degraded. 71 WebhookRetryBackoff *wait.Backoff 72 73 TokenSuccessCacheTTL time.Duration 74 TokenFailureCacheTTL time.Duration 75 76 RequestHeaderConfig *authenticatorfactory.RequestHeaderConfig 77 78 // TODO, this is the only non-serializable part of the entire config. Factor it out into a clientconfig 79 ServiceAccountTokenGetter serviceaccount.ServiceAccountTokenGetter 80 SecretsWriter typedv1core.SecretsGetter 81 BootstrapTokenAuthenticator authenticator.Token 82 // ClientCAContentProvider are the options for verifying incoming connections using mTLS and directly assigning to users. 83 // Generally this is the CA bundle file used to authenticate client certificates 84 // If this value is nil, then mutual TLS is disabled. 85 ClientCAContentProvider dynamiccertificates.CAContentProvider 86 87 // Optional field, custom dial function used to connect to webhook 88 CustomDial utilnet.DialFunc 89 } 90 91 // New returns an authenticator.Request or an error that supports the standard 92 // Kubernetes authentication mechanisms. 93 func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, spec3.SecuritySchemes, error) { 94 var authenticators []authenticator.Request 95 var tokenAuthenticators []authenticator.Token 96 securityDefinitionsV2 := spec.SecurityDefinitions{} 97 securitySchemesV3 := spec3.SecuritySchemes{} 98 99 // front-proxy, BasicAuth methods, local first, then remote 100 // Add the front proxy authenticator if requested 101 if config.RequestHeaderConfig != nil { 102 requestHeaderAuthenticator := headerrequest.NewDynamicVerifyOptionsSecure( 103 config.RequestHeaderConfig.CAContentProvider.VerifyOptions, 104 config.RequestHeaderConfig.AllowedClientNames, 105 config.RequestHeaderConfig.UsernameHeaders, 106 config.RequestHeaderConfig.GroupHeaders, 107 config.RequestHeaderConfig.ExtraHeaderPrefixes, 108 ) 109 authenticators = append(authenticators, authenticator.WrapAudienceAgnosticRequest(config.APIAudiences, requestHeaderAuthenticator)) 110 } 111 112 // X509 methods 113 if config.ClientCAContentProvider != nil { 114 certAuth := x509.NewDynamic(config.ClientCAContentProvider.VerifyOptions, x509.CommonNameUserConversion) 115 authenticators = append(authenticators, certAuth) 116 } 117 118 // Bearer token methods, local first, then remote 119 if len(config.TokenAuthFile) > 0 { 120 tokenAuth, err := newAuthenticatorFromTokenFile(config.TokenAuthFile) 121 if err != nil { 122 return nil, nil, nil, err 123 } 124 tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, tokenAuth)) 125 } 126 if len(config.ServiceAccountKeyFiles) > 0 { 127 serviceAccountAuth, err := newLegacyServiceAccountAuthenticator(config.ServiceAccountKeyFiles, config.ServiceAccountLookup, config.APIAudiences, config.ServiceAccountTokenGetter, config.SecretsWriter) 128 if err != nil { 129 return nil, nil, nil, err 130 } 131 tokenAuthenticators = append(tokenAuthenticators, serviceAccountAuth) 132 } 133 if len(config.ServiceAccountIssuers) > 0 { 134 serviceAccountAuth, err := newServiceAccountAuthenticator(config.ServiceAccountIssuers, config.ServiceAccountKeyFiles, config.APIAudiences, config.ServiceAccountTokenGetter) 135 if err != nil { 136 return nil, nil, nil, err 137 } 138 tokenAuthenticators = append(tokenAuthenticators, serviceAccountAuth) 139 } 140 141 if config.BootstrapToken && config.BootstrapTokenAuthenticator != nil { 142 tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, config.BootstrapTokenAuthenticator)) 143 } 144 145 // NOTE(ericchiang): Keep the OpenID Connect after Service Accounts. 146 // 147 // Because both plugins verify JWTs whichever comes first in the union experiences 148 // cache misses for all requests using the other. While the service account plugin 149 // simply returns an error, the OpenID Connect plugin may query the provider to 150 // update the keys, causing performance hits. 151 if config.AuthenticationConfig != nil { 152 for _, jwtAuthenticator := range config.AuthenticationConfig.JWT { 153 var oidcCAContent oidc.CAContentProvider 154 if len(jwtAuthenticator.Issuer.CertificateAuthority) > 0 { 155 var oidcCAError error 156 oidcCAContent, oidcCAError = dynamiccertificates.NewStaticCAContent("oidc-authenticator", []byte(jwtAuthenticator.Issuer.CertificateAuthority)) 157 if oidcCAError != nil { 158 return nil, nil, nil, oidcCAError 159 } 160 } 161 oidcAuth, err := oidc.New(oidc.Options{ 162 JWTAuthenticator: jwtAuthenticator, 163 CAContentProvider: oidcCAContent, 164 SupportedSigningAlgs: config.OIDCSigningAlgs, 165 }) 166 if err != nil { 167 return nil, nil, nil, err 168 } 169 tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, oidcAuth)) 170 } 171 } 172 173 if len(config.WebhookTokenAuthnConfigFile) > 0 { 174 webhookTokenAuth, err := newWebhookTokenAuthenticator(config) 175 if err != nil { 176 return nil, nil, nil, err 177 } 178 179 tokenAuthenticators = append(tokenAuthenticators, webhookTokenAuth) 180 } 181 182 if len(tokenAuthenticators) > 0 { 183 // Union the token authenticators 184 tokenAuth := tokenunion.New(tokenAuthenticators...) 185 // Optionally cache authentication results 186 if config.TokenSuccessCacheTTL > 0 || config.TokenFailureCacheTTL > 0 { 187 tokenAuth = tokencache.New(tokenAuth, true, config.TokenSuccessCacheTTL, config.TokenFailureCacheTTL) 188 } 189 authenticators = append(authenticators, bearertoken.New(tokenAuth), websocket.NewProtocolAuthenticator(tokenAuth)) 190 191 securityDefinitionsV2["BearerToken"] = &spec.SecurityScheme{ 192 SecuritySchemeProps: spec.SecuritySchemeProps{ 193 Type: "apiKey", 194 Name: "authorization", 195 In: "header", 196 Description: "Bearer Token authentication", 197 }, 198 } 199 securitySchemesV3["BearerToken"] = &spec3.SecurityScheme{ 200 SecuritySchemeProps: spec3.SecuritySchemeProps{ 201 Type: "apiKey", 202 Name: "authorization", 203 In: "header", 204 Description: "Bearer Token authentication", 205 }, 206 } 207 } 208 209 if len(authenticators) == 0 { 210 if config.Anonymous { 211 return anonymous.NewAuthenticator(), &securityDefinitionsV2, securitySchemesV3, nil 212 } 213 return nil, &securityDefinitionsV2, securitySchemesV3, nil 214 } 215 216 authenticator := union.New(authenticators...) 217 218 authenticator = group.NewAuthenticatedGroupAdder(authenticator) 219 220 if config.Anonymous { 221 // If the authenticator chain returns an error, return an error (don't consider a bad bearer token 222 // or invalid username/password combination anonymous). 223 authenticator = union.NewFailOnError(authenticator, anonymous.NewAuthenticator()) 224 } 225 226 return authenticator, &securityDefinitionsV2, securitySchemesV3, nil 227 } 228 229 // IsValidServiceAccountKeyFile returns true if a valid public RSA key can be read from the given file 230 func IsValidServiceAccountKeyFile(file string) bool { 231 _, err := keyutil.PublicKeysFromFile(file) 232 return err == nil 233 } 234 235 // newAuthenticatorFromTokenFile returns an authenticator.Token or an error 236 func newAuthenticatorFromTokenFile(tokenAuthFile string) (authenticator.Token, error) { 237 tokenAuthenticator, err := tokenfile.NewCSV(tokenAuthFile) 238 if err != nil { 239 return nil, err 240 } 241 242 return tokenAuthenticator, nil 243 } 244 245 // newLegacyServiceAccountAuthenticator returns an authenticator.Token or an error 246 func newLegacyServiceAccountAuthenticator(keyfiles []string, lookup bool, apiAudiences authenticator.Audiences, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter, secretsWriter typedv1core.SecretsGetter) (authenticator.Token, error) { 247 allPublicKeys := []interface{}{} 248 for _, keyfile := range keyfiles { 249 publicKeys, err := keyutil.PublicKeysFromFile(keyfile) 250 if err != nil { 251 return nil, err 252 } 253 allPublicKeys = append(allPublicKeys, publicKeys...) 254 } 255 validator, err := serviceaccount.NewLegacyValidator(lookup, serviceAccountGetter, secretsWriter) 256 if err != nil { 257 return nil, fmt.Errorf("while creating legacy validator, err: %w", err) 258 } 259 260 tokenAuthenticator := serviceaccount.JWTTokenAuthenticator([]string{serviceaccount.LegacyIssuer}, allPublicKeys, apiAudiences, validator) 261 return tokenAuthenticator, nil 262 } 263 264 // newServiceAccountAuthenticator returns an authenticator.Token or an error 265 func newServiceAccountAuthenticator(issuers []string, keyfiles []string, apiAudiences authenticator.Audiences, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter) (authenticator.Token, error) { 266 allPublicKeys := []interface{}{} 267 for _, keyfile := range keyfiles { 268 publicKeys, err := keyutil.PublicKeysFromFile(keyfile) 269 if err != nil { 270 return nil, err 271 } 272 allPublicKeys = append(allPublicKeys, publicKeys...) 273 } 274 275 tokenAuthenticator := serviceaccount.JWTTokenAuthenticator(issuers, allPublicKeys, apiAudiences, serviceaccount.NewValidator(serviceAccountGetter)) 276 return tokenAuthenticator, nil 277 } 278 279 func newWebhookTokenAuthenticator(config Config) (authenticator.Token, error) { 280 if config.WebhookRetryBackoff == nil { 281 return nil, errors.New("retry backoff parameters for authentication webhook has not been specified") 282 } 283 284 clientConfig, err := webhookutil.LoadKubeconfig(config.WebhookTokenAuthnConfigFile, config.CustomDial) 285 if err != nil { 286 return nil, err 287 } 288 webhookTokenAuthenticator, err := webhook.New(clientConfig, config.WebhookTokenAuthnVersion, config.APIAudiences, *config.WebhookRetryBackoff) 289 if err != nil { 290 return nil, err 291 } 292 293 return tokencache.New(webhookTokenAuthenticator, false, config.WebhookTokenAuthnCacheTTL, config.WebhookTokenAuthnCacheTTL), nil 294 }