mirror of https://github.com/k3s-io/k3s
212 lines
7.8 KiB
Go
212 lines
7.8 KiB
Go
|
/*
|
||
|
Copyright 2014 The Kubernetes Authors.
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package authenticator
|
||
|
|
||
|
import (
|
||
|
"time"
|
||
|
|
||
|
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||
|
"k8s.io/apiserver/pkg/authentication/group"
|
||
|
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||
|
"k8s.io/apiserver/pkg/authentication/request/union"
|
||
|
"k8s.io/apiserver/pkg/authentication/request/websocket"
|
||
|
"k8s.io/apiserver/pkg/authentication/request/x509"
|
||
|
tokencache "k8s.io/apiserver/pkg/authentication/token/cache"
|
||
|
"k8s.io/apiserver/pkg/authentication/token/tokenfile"
|
||
|
tokenunion "k8s.io/apiserver/pkg/authentication/token/union"
|
||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||
|
"k8s.io/apiserver/plugin/pkg/authenticator/password/passwordfile"
|
||
|
"k8s.io/apiserver/plugin/pkg/authenticator/request/basicauth"
|
||
|
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
|
||
|
// Initialize all known client auth plugins.
|
||
|
certutil "k8s.io/client-go/util/cert"
|
||
|
"k8s.io/kubernetes/pkg/features"
|
||
|
"k8s.io/kubernetes/pkg/serviceaccount"
|
||
|
)
|
||
|
|
||
|
// Config contains the data on how to authenticate a request to the Kube API Server
|
||
|
type Config struct {
|
||
|
BasicAuthFile string
|
||
|
ClientCAFile string
|
||
|
TokenAuthFile string
|
||
|
ServiceAccountKeyFiles []string
|
||
|
ServiceAccountLookup bool
|
||
|
ServiceAccountIssuer string
|
||
|
APIAudiences authenticator.Audiences
|
||
|
WebhookTokenAuthnConfigFile string
|
||
|
WebhookTokenAuthnCacheTTL time.Duration
|
||
|
|
||
|
TokenSuccessCacheTTL time.Duration
|
||
|
TokenFailureCacheTTL time.Duration
|
||
|
|
||
|
// TODO, this is the only non-serializable part of the entire config. Factor it out into a clientconfig
|
||
|
ServiceAccountTokenGetter serviceaccount.ServiceAccountTokenGetter
|
||
|
}
|
||
|
|
||
|
// New returns an authenticator.Request or an error that supports the standard
|
||
|
// Kubernetes authentication mechanisms.
|
||
|
func (config Config) New() (authenticator.Request, error) {
|
||
|
var authenticators []authenticator.Request
|
||
|
var tokenAuthenticators []authenticator.Token
|
||
|
|
||
|
// basic auth
|
||
|
if len(config.BasicAuthFile) > 0 {
|
||
|
basicAuth, err := newAuthenticatorFromBasicAuthFile(config.BasicAuthFile)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
authenticators = append(authenticators, authenticator.WrapAudienceAgnosticRequest(config.APIAudiences, basicAuth))
|
||
|
}
|
||
|
|
||
|
// X509 methods
|
||
|
if len(config.ClientCAFile) > 0 {
|
||
|
certAuth, err := newAuthenticatorFromClientCAFile(config.ClientCAFile)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
authenticators = append(authenticators, certAuth)
|
||
|
}
|
||
|
|
||
|
// Bearer token methods, local first, then remote
|
||
|
if len(config.TokenAuthFile) > 0 {
|
||
|
tokenAuth, err := newAuthenticatorFromTokenFile(config.TokenAuthFile)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, tokenAuth))
|
||
|
}
|
||
|
if len(config.ServiceAccountKeyFiles) > 0 {
|
||
|
serviceAccountAuth, err := newLegacyServiceAccountAuthenticator(config.ServiceAccountKeyFiles, config.ServiceAccountLookup, config.APIAudiences, config.ServiceAccountTokenGetter)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
tokenAuthenticators = append(tokenAuthenticators, serviceAccountAuth)
|
||
|
}
|
||
|
if utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) && config.ServiceAccountIssuer != "" {
|
||
|
serviceAccountAuth, err := newServiceAccountAuthenticator(config.ServiceAccountIssuer, config.ServiceAccountKeyFiles, config.APIAudiences, config.ServiceAccountTokenGetter)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
tokenAuthenticators = append(tokenAuthenticators, serviceAccountAuth)
|
||
|
}
|
||
|
if len(config.WebhookTokenAuthnConfigFile) > 0 {
|
||
|
webhookTokenAuth, err := newWebhookTokenAuthenticator(config.WebhookTokenAuthnConfigFile, config.WebhookTokenAuthnCacheTTL, config.APIAudiences)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
tokenAuthenticators = append(tokenAuthenticators, webhookTokenAuth)
|
||
|
}
|
||
|
|
||
|
if len(tokenAuthenticators) > 0 {
|
||
|
// Union the token authenticators
|
||
|
tokenAuth := tokenunion.New(tokenAuthenticators...)
|
||
|
// Optionally cache authentication results
|
||
|
if config.TokenSuccessCacheTTL > 0 || config.TokenFailureCacheTTL > 0 {
|
||
|
tokenAuth = tokencache.New(tokenAuth, true, config.TokenSuccessCacheTTL, config.TokenFailureCacheTTL)
|
||
|
}
|
||
|
authenticators = append(authenticators, bearertoken.New(tokenAuth), websocket.NewProtocolAuthenticator(tokenAuth))
|
||
|
}
|
||
|
|
||
|
if len(authenticators) == 0 {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
authenticator := union.New(authenticators...)
|
||
|
|
||
|
authenticator = group.NewAuthenticatedGroupAdder(authenticator)
|
||
|
|
||
|
return authenticator, nil
|
||
|
}
|
||
|
|
||
|
// IsValidServiceAccountKeyFile returns true if a valid public RSA key can be read from the given file
|
||
|
func IsValidServiceAccountKeyFile(file string) bool {
|
||
|
_, err := certutil.PublicKeysFromFile(file)
|
||
|
return err == nil
|
||
|
}
|
||
|
|
||
|
// newAuthenticatorFromBasicAuthFile returns an authenticator.Request or an error
|
||
|
func newAuthenticatorFromBasicAuthFile(basicAuthFile string) (authenticator.Request, error) {
|
||
|
basicAuthenticator, err := passwordfile.NewCSV(basicAuthFile)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return basicauth.New(basicAuthenticator), nil
|
||
|
}
|
||
|
|
||
|
// newAuthenticatorFromTokenFile returns an authenticator.Token or an error
|
||
|
func newAuthenticatorFromTokenFile(tokenAuthFile string) (authenticator.Token, error) {
|
||
|
tokenAuthenticator, err := tokenfile.NewCSV(tokenAuthFile)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return tokenAuthenticator, nil
|
||
|
}
|
||
|
|
||
|
// newLegacyServiceAccountAuthenticator returns an authenticator.Token or an error
|
||
|
func newLegacyServiceAccountAuthenticator(keyfiles []string, lookup bool, apiAudiences authenticator.Audiences, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter) (authenticator.Token, error) {
|
||
|
allPublicKeys := []interface{}{}
|
||
|
for _, keyfile := range keyfiles {
|
||
|
publicKeys, err := certutil.PublicKeysFromFile(keyfile)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
allPublicKeys = append(allPublicKeys, publicKeys...)
|
||
|
}
|
||
|
|
||
|
tokenAuthenticator := serviceaccount.JWTTokenAuthenticator(serviceaccount.LegacyIssuer, allPublicKeys, apiAudiences, serviceaccount.NewLegacyValidator(lookup, serviceAccountGetter))
|
||
|
return tokenAuthenticator, nil
|
||
|
}
|
||
|
|
||
|
// newServiceAccountAuthenticator returns an authenticator.Token or an error
|
||
|
func newServiceAccountAuthenticator(iss string, keyfiles []string, apiAudiences authenticator.Audiences, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter) (authenticator.Token, error) {
|
||
|
allPublicKeys := []interface{}{}
|
||
|
for _, keyfile := range keyfiles {
|
||
|
publicKeys, err := certutil.PublicKeysFromFile(keyfile)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
allPublicKeys = append(allPublicKeys, publicKeys...)
|
||
|
}
|
||
|
|
||
|
tokenAuthenticator := serviceaccount.JWTTokenAuthenticator(iss, allPublicKeys, apiAudiences, serviceaccount.NewValidator(serviceAccountGetter))
|
||
|
return tokenAuthenticator, nil
|
||
|
}
|
||
|
|
||
|
// newAuthenticatorFromClientCAFile returns an authenticator.Request or an error
|
||
|
func newAuthenticatorFromClientCAFile(clientCAFile string) (authenticator.Request, error) {
|
||
|
roots, err := certutil.NewPool(clientCAFile)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
opts := x509.DefaultVerifyOptions()
|
||
|
opts.Roots = roots
|
||
|
|
||
|
return x509.New(opts, x509.CommonNameUserConversion), nil
|
||
|
}
|
||
|
|
||
|
func newWebhookTokenAuthenticator(webhookConfigFile string, ttl time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) {
|
||
|
webhookTokenAuthenticator, err := webhook.New(webhookConfigFile, implicitAuds)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return tokencache.New(webhookTokenAuthenticator, false, ttl, ttl), nil
|
||
|
}
|