diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 76309fbca48..042b92a3f22 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -201,6 +201,7 @@ func Run(s *options.APIServer) error { } apiAuthenticator, err := authenticator.New(authenticator.AuthenticatorConfig{ + Anonymous: s.AnonymousAuth, BasicAuthFile: s.BasicAuthFile, ClientCAFile: s.ClientCAFile, TokenAuthFile: s.TokenAuthFile, diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index 1565648c3ef..6f8b0c2fd20 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -115,6 +115,7 @@ func Run(s *options.ServerRunOptions) error { } apiAuthenticator, err := authenticator.New(authenticator.AuthenticatorConfig{ + Anonymous: s.AnonymousAuth, BasicAuthFile: s.BasicAuthFile, ClientCAFile: s.ClientCAFile, TokenAuthFile: s.TokenAuthFile, diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index 7435cf1a08f..710fb586bbd 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -9,6 +9,7 @@ all-namespaces allocate-node-cidrs allow-privileged allowed-not-ready-nodes +anonymous-auth api-advertise-addresses api-external-dns-names api-burst diff --git a/pkg/apiserver/authenticator/authn.go b/pkg/apiserver/authenticator/authn.go index 1a738308873..f7a1554d6a3 100644 --- a/pkg/apiserver/authenticator/authn.go +++ b/pkg/apiserver/authenticator/authn.go @@ -27,6 +27,7 @@ import ( certutil "k8s.io/kubernetes/pkg/util/cert" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/password/keystone" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/password/passwordfile" + "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/anonymous" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/basicauth" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/x509" @@ -36,6 +37,7 @@ import ( ) type AuthenticatorConfig struct { + Anonymous bool BasicAuthFile string ClientCAFile string TokenAuthFile string @@ -57,6 +59,7 @@ type AuthenticatorConfig struct { func New(config AuthenticatorConfig) (authenticator.Request, error) { var authenticators []authenticator.Request + // BasicAuth methods, local first, then remote if len(config.BasicAuthFile) > 0 { basicAuth, err := newAuthenticatorFromBasicAuthFile(config.BasicAuthFile) if err != nil { @@ -64,7 +67,15 @@ func New(config AuthenticatorConfig) (authenticator.Request, error) { } authenticators = append(authenticators, basicAuth) } + if len(config.KeystoneURL) > 0 { + keystoneAuth, err := newAuthenticatorFromKeystoneURL(config.KeystoneURL) + if err != nil { + return nil, err + } + authenticators = append(authenticators, keystoneAuth) + } + // X509 methods if len(config.ClientCAFile) > 0 { certAuth, err := newAuthenticatorFromClientCAFile(config.ClientCAFile) if err != nil { @@ -73,6 +84,7 @@ func New(config AuthenticatorConfig) (authenticator.Request, error) { authenticators = append(authenticators, certAuth) } + // Bearer token methods, local first, then remote if len(config.TokenAuthFile) > 0 { tokenAuth, err := newAuthenticatorFromTokenFile(config.TokenAuthFile) if err != nil { @@ -80,7 +92,6 @@ func New(config AuthenticatorConfig) (authenticator.Request, error) { } authenticators = append(authenticators, tokenAuth) } - if len(config.ServiceAccountKeyFile) > 0 { serviceAccountAuth, err := newServiceAccountAuthenticator(config.ServiceAccountKeyFile, config.ServiceAccountLookup, config.ServiceAccountTokenGetter) if err != nil { @@ -88,7 +99,6 @@ func New(config AuthenticatorConfig) (authenticator.Request, error) { } authenticators = append(authenticators, serviceAccountAuth) } - // NOTE(ericchiang): Keep the OpenID Connect after Service Accounts. // // Because both plugins verify JWTs whichever comes first in the union experiences @@ -102,15 +112,6 @@ func New(config AuthenticatorConfig) (authenticator.Request, error) { } authenticators = append(authenticators, oidcAuth) } - - if len(config.KeystoneURL) > 0 { - keystoneAuth, err := newAuthenticatorFromKeystoneURL(config.KeystoneURL) - if err != nil { - return nil, err - } - authenticators = append(authenticators, keystoneAuth) - } - if len(config.WebhookTokenAuthnConfigFile) > 0 { webhookTokenAuth, err := newWebhookTokenAuthenticator(config.WebhookTokenAuthnConfigFile, config.WebhookTokenAuthnCacheTTL) if err != nil { @@ -119,14 +120,21 @@ func New(config AuthenticatorConfig) (authenticator.Request, error) { authenticators = append(authenticators, webhookTokenAuth) } - switch len(authenticators) { - case 0: + if len(authenticators) == 0 { + if config.Anonymous { + return anonymous.NewAuthenticator(), nil + } return nil, nil - case 1: - return authenticators[0], nil - default: - return union.New(authenticators...), nil } + + authenticator := union.New(authenticators...) + + if config.Anonymous { + // If the authenticator chain returns an error, return an error (don't consider a bad bearer token anonymous). + authenticator = union.NewFailOnError(authenticator, anonymous.NewAuthenticator()) + } + + return authenticator, nil } // IsValidServiceAccountKeyFile returns true if a valid public RSA key can be read from the given file diff --git a/pkg/genericapiserver/options/server_run_options.go b/pkg/genericapiserver/options/server_run_options.go index ca5e3852abe..68d9f1396f8 100644 --- a/pkg/genericapiserver/options/server_run_options.go +++ b/pkg/genericapiserver/options/server_run_options.go @@ -71,6 +71,7 @@ type ServerRunOptions struct { AuthorizationWebhookCacheUnauthorizedTTL time.Duration AuthorizationRBACSuperUser string + AnonymousAuth bool BasicAuthFile string BindAddress net.IP CertDirectory string @@ -127,6 +128,7 @@ func NewServerRunOptions() *ServerRunOptions { APIGroupPrefix: "/apis", APIPrefix: "/api", AdmissionControl: "AlwaysAdmit", + AnonymousAuth: true, AuthorizationMode: "AlwaysAllow", AuthorizationWebhookCacheAuthorizedTTL: 5 * time.Minute, AuthorizationWebhookCacheUnauthorizedTTL: 30 * time.Second, @@ -269,6 +271,11 @@ func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) { "If specified, a username which avoids RBAC authorization checks and role binding "+ "privilege escalation checks, to be used with --authorization-mode=RBAC.") + fs.BoolVar(&s.AnonymousAuth, "anonymous-auth", s.AnonymousAuth, ""+ + "Enables anonymous requests to the secure port of the API server. "+ + "Requests that are not rejected by another authentication method are treated as anonymous requests. "+ + "Anonymous requests have a username of system:anonymous, and a group name of system:unauthenticated.") + fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, ""+ "If set, the file that will be used to admit requests to the secure port of the API server "+ "via http basic authentication.") diff --git a/plugin/pkg/auth/authenticator/request/union/union.go b/plugin/pkg/auth/authenticator/request/union/union.go index dc2640a613c..020f3558c66 100644 --- a/plugin/pkg/auth/authenticator/request/union/union.go +++ b/plugin/pkg/auth/authenticator/request/union/union.go @@ -35,12 +35,18 @@ type unionAuthRequestHandler struct { // New returns a request authenticator that validates credentials using a chain of authenticator.Request objects. // The entire chain is tried until one succeeds. If all fail, an aggregate error is returned. func New(authRequestHandlers ...authenticator.Request) authenticator.Request { + if len(authRequestHandlers) == 1 { + return authRequestHandlers[0] + } return &unionAuthRequestHandler{Handlers: authRequestHandlers, FailOnError: false} } // NewFailOnError returns a request authenticator that validates credentials using a chain of authenticator.Request objects. // The first error short-circuits the chain. func NewFailOnError(authRequestHandlers ...authenticator.Request) authenticator.Request { + if len(authRequestHandlers) == 1 { + return authRequestHandlers[0] + } return &unionAuthRequestHandler{Handlers: authRequestHandlers, FailOnError: true} }