diff --git a/pkg/apis/apiserver/types.go b/pkg/apis/apiserver/types.go index a31b87536..af70fe244 100644 --- a/pkg/apis/apiserver/types.go +++ b/pkg/apis/apiserver/types.go @@ -165,6 +165,25 @@ type AuthenticationConfiguration struct { metav1.TypeMeta JWT []JWTAuthenticator + + // If present --anonymous-auth must not be set + Anonymous *AnonymousAuthConfig +} + +// AnonymousAuthConfig provides the configuration for the anonymous authenticator. +type AnonymousAuthConfig struct { + Enabled bool + + // If set, anonymous auth is only allowed if the request meets one of the + // conditions. + Conditions []AnonymousAuthCondition +} + +// AnonymousAuthCondition describes the condition under which anonymous auth +// should be enabled. +type AnonymousAuthCondition struct { + // Path for which anonymous auth is enabled. + Path string } // JWTAuthenticator provides the configuration for a single JWT authenticator. diff --git a/pkg/apis/apiserver/v1alpha1/types.go b/pkg/apis/apiserver/v1alpha1/types.go index fc75c464a..214ef4e4f 100644 --- a/pkg/apis/apiserver/v1alpha1/types.go +++ b/pkg/apis/apiserver/v1alpha1/types.go @@ -185,6 +185,25 @@ type AuthenticationConfiguration struct { // "": "username" // } JWT []JWTAuthenticator `json:"jwt"` + + // If present --anonymous-auth must not be set + Anonymous *AnonymousAuthConfig `json:"anonymous,omitempty"` +} + +// AnonymousAuthConfig provides the configuration for the anonymous authenticator. +type AnonymousAuthConfig struct { + Enabled bool `json:"enabled"` + + // If set, anonymous auth is only allowed if the request meets one of the + // conditions. + Conditions []AnonymousAuthCondition `json:"conditions,omitempty"` +} + +// AnonymousAuthCondition describes the condition under which anonymous auth +// should be enabled. +type AnonymousAuthCondition struct { + // Path for which anonymous auth is enabled. + Path string `json:"path"` } // JWTAuthenticator provides the configuration for a single JWT authenticator. diff --git a/pkg/apis/apiserver/v1alpha1/zz_generated.conversion.go b/pkg/apis/apiserver/v1alpha1/zz_generated.conversion.go index 9ee1ef8a4..3a6c66c3a 100644 --- a/pkg/apis/apiserver/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/apiserver/v1alpha1/zz_generated.conversion.go @@ -57,6 +57,26 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*AnonymousAuthCondition)(nil), (*apiserver.AnonymousAuthCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(a.(*AnonymousAuthCondition), b.(*apiserver.AnonymousAuthCondition), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*apiserver.AnonymousAuthCondition)(nil), (*AnonymousAuthCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_apiserver_AnonymousAuthCondition_To_v1alpha1_AnonymousAuthCondition(a.(*apiserver.AnonymousAuthCondition), b.(*AnonymousAuthCondition), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*AnonymousAuthConfig)(nil), (*apiserver.AnonymousAuthConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(a.(*AnonymousAuthConfig), b.(*apiserver.AnonymousAuthConfig), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*apiserver.AnonymousAuthConfig)(nil), (*AnonymousAuthConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_apiserver_AnonymousAuthConfig_To_v1alpha1_AnonymousAuthConfig(a.(*apiserver.AnonymousAuthConfig), b.(*AnonymousAuthConfig), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*AuthenticationConfiguration)(nil), (*apiserver.AuthenticationConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_AuthenticationConfiguration_To_apiserver_AuthenticationConfiguration(a.(*AuthenticationConfiguration), b.(*apiserver.AuthenticationConfiguration), scope) }); err != nil { @@ -324,6 +344,48 @@ func Convert_apiserver_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPluginC return autoConvert_apiserver_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPluginConfiguration(in, out, s) } +func autoConvert_v1alpha1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(in *AnonymousAuthCondition, out *apiserver.AnonymousAuthCondition, s conversion.Scope) error { + out.Path = in.Path + return nil +} + +// Convert_v1alpha1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition is an autogenerated conversion function. +func Convert_v1alpha1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(in *AnonymousAuthCondition, out *apiserver.AnonymousAuthCondition, s conversion.Scope) error { + return autoConvert_v1alpha1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(in, out, s) +} + +func autoConvert_apiserver_AnonymousAuthCondition_To_v1alpha1_AnonymousAuthCondition(in *apiserver.AnonymousAuthCondition, out *AnonymousAuthCondition, s conversion.Scope) error { + out.Path = in.Path + return nil +} + +// Convert_apiserver_AnonymousAuthCondition_To_v1alpha1_AnonymousAuthCondition is an autogenerated conversion function. +func Convert_apiserver_AnonymousAuthCondition_To_v1alpha1_AnonymousAuthCondition(in *apiserver.AnonymousAuthCondition, out *AnonymousAuthCondition, s conversion.Scope) error { + return autoConvert_apiserver_AnonymousAuthCondition_To_v1alpha1_AnonymousAuthCondition(in, out, s) +} + +func autoConvert_v1alpha1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(in *AnonymousAuthConfig, out *apiserver.AnonymousAuthConfig, s conversion.Scope) error { + out.Enabled = in.Enabled + out.Conditions = *(*[]apiserver.AnonymousAuthCondition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_v1alpha1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig is an autogenerated conversion function. +func Convert_v1alpha1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(in *AnonymousAuthConfig, out *apiserver.AnonymousAuthConfig, s conversion.Scope) error { + return autoConvert_v1alpha1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(in, out, s) +} + +func autoConvert_apiserver_AnonymousAuthConfig_To_v1alpha1_AnonymousAuthConfig(in *apiserver.AnonymousAuthConfig, out *AnonymousAuthConfig, s conversion.Scope) error { + out.Enabled = in.Enabled + out.Conditions = *(*[]AnonymousAuthCondition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_apiserver_AnonymousAuthConfig_To_v1alpha1_AnonymousAuthConfig is an autogenerated conversion function. +func Convert_apiserver_AnonymousAuthConfig_To_v1alpha1_AnonymousAuthConfig(in *apiserver.AnonymousAuthConfig, out *AnonymousAuthConfig, s conversion.Scope) error { + return autoConvert_apiserver_AnonymousAuthConfig_To_v1alpha1_AnonymousAuthConfig(in, out, s) +} + func autoConvert_v1alpha1_AuthenticationConfiguration_To_apiserver_AuthenticationConfiguration(in *AuthenticationConfiguration, out *apiserver.AuthenticationConfiguration, s conversion.Scope) error { if in.JWT != nil { in, out := &in.JWT, &out.JWT @@ -336,6 +398,7 @@ func autoConvert_v1alpha1_AuthenticationConfiguration_To_apiserver_Authenticatio } else { out.JWT = nil } + out.Anonymous = (*apiserver.AnonymousAuthConfig)(unsafe.Pointer(in.Anonymous)) return nil } @@ -356,6 +419,7 @@ func autoConvert_apiserver_AuthenticationConfiguration_To_v1alpha1_Authenticatio } else { out.JWT = nil } + out.Anonymous = (*AnonymousAuthConfig)(unsafe.Pointer(in.Anonymous)) return nil } diff --git a/pkg/apis/apiserver/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/apiserver/v1alpha1/zz_generated.deepcopy.go index e618178bf..81b652254 100644 --- a/pkg/apis/apiserver/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/apiserver/v1alpha1/zz_generated.deepcopy.go @@ -78,6 +78,43 @@ func (in *AdmissionPluginConfiguration) DeepCopy() *AdmissionPluginConfiguration return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnonymousAuthCondition) DeepCopyInto(out *AnonymousAuthCondition) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnonymousAuthCondition. +func (in *AnonymousAuthCondition) DeepCopy() *AnonymousAuthCondition { + if in == nil { + return nil + } + out := new(AnonymousAuthCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnonymousAuthConfig) DeepCopyInto(out *AnonymousAuthConfig) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]AnonymousAuthCondition, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnonymousAuthConfig. +func (in *AnonymousAuthConfig) DeepCopy() *AnonymousAuthConfig { + if in == nil { + return nil + } + out := new(AnonymousAuthConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AuthenticationConfiguration) DeepCopyInto(out *AuthenticationConfiguration) { *out = *in @@ -89,6 +126,11 @@ func (in *AuthenticationConfiguration) DeepCopyInto(out *AuthenticationConfigura (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Anonymous != nil { + in, out := &in.Anonymous, &out.Anonymous + *out = new(AnonymousAuthConfig) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/apis/apiserver/v1beta1/types.go b/pkg/apis/apiserver/v1beta1/types.go index 00a55f7a9..570f3c468 100644 --- a/pkg/apis/apiserver/v1beta1/types.go +++ b/pkg/apis/apiserver/v1beta1/types.go @@ -156,6 +156,25 @@ type AuthenticationConfiguration struct { // "": "username" // } JWT []JWTAuthenticator `json:"jwt"` + + // If present --anonymous-auth must not be set + Anonymous *AnonymousAuthConfig `json:"anonymous,omitempty"` +} + +// AnonymousAuthConfig provides the configuration for the anonymous authenticator. +type AnonymousAuthConfig struct { + Enabled bool `json:"enabled"` + + // If set, anonymous auth is only allowed if the request meets one of the + // conditions. + Conditions []AnonymousAuthCondition `json:"conditions,omitempty"` +} + +// AnonymousAuthCondition describes the condition under which anonymous auth +// should be enabled. +type AnonymousAuthCondition struct { + // Path for which anonymous auth is enabled. + Path string `json:"path"` } // JWTAuthenticator provides the configuration for a single JWT authenticator. diff --git a/pkg/apis/apiserver/v1beta1/zz_generated.conversion.go b/pkg/apis/apiserver/v1beta1/zz_generated.conversion.go index 911a331f2..30ef049d4 100644 --- a/pkg/apis/apiserver/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/apiserver/v1beta1/zz_generated.conversion.go @@ -37,6 +37,26 @@ func init() { // RegisterConversions adds conversion functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*AnonymousAuthCondition)(nil), (*apiserver.AnonymousAuthCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(a.(*AnonymousAuthCondition), b.(*apiserver.AnonymousAuthCondition), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*apiserver.AnonymousAuthCondition)(nil), (*AnonymousAuthCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_apiserver_AnonymousAuthCondition_To_v1beta1_AnonymousAuthCondition(a.(*apiserver.AnonymousAuthCondition), b.(*AnonymousAuthCondition), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*AnonymousAuthConfig)(nil), (*apiserver.AnonymousAuthConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(a.(*AnonymousAuthConfig), b.(*apiserver.AnonymousAuthConfig), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*apiserver.AnonymousAuthConfig)(nil), (*AnonymousAuthConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_apiserver_AnonymousAuthConfig_To_v1beta1_AnonymousAuthConfig(a.(*apiserver.AnonymousAuthConfig), b.(*AnonymousAuthConfig), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*AuthenticationConfiguration)(nil), (*apiserver.AuthenticationConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_AuthenticationConfiguration_To_apiserver_AuthenticationConfiguration(a.(*AuthenticationConfiguration), b.(*apiserver.AuthenticationConfiguration), scope) }); err != nil { @@ -260,6 +280,48 @@ func RegisterConversions(s *runtime.Scheme) error { return nil } +func autoConvert_v1beta1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(in *AnonymousAuthCondition, out *apiserver.AnonymousAuthCondition, s conversion.Scope) error { + out.Path = in.Path + return nil +} + +// Convert_v1beta1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition is an autogenerated conversion function. +func Convert_v1beta1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(in *AnonymousAuthCondition, out *apiserver.AnonymousAuthCondition, s conversion.Scope) error { + return autoConvert_v1beta1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(in, out, s) +} + +func autoConvert_apiserver_AnonymousAuthCondition_To_v1beta1_AnonymousAuthCondition(in *apiserver.AnonymousAuthCondition, out *AnonymousAuthCondition, s conversion.Scope) error { + out.Path = in.Path + return nil +} + +// Convert_apiserver_AnonymousAuthCondition_To_v1beta1_AnonymousAuthCondition is an autogenerated conversion function. +func Convert_apiserver_AnonymousAuthCondition_To_v1beta1_AnonymousAuthCondition(in *apiserver.AnonymousAuthCondition, out *AnonymousAuthCondition, s conversion.Scope) error { + return autoConvert_apiserver_AnonymousAuthCondition_To_v1beta1_AnonymousAuthCondition(in, out, s) +} + +func autoConvert_v1beta1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(in *AnonymousAuthConfig, out *apiserver.AnonymousAuthConfig, s conversion.Scope) error { + out.Enabled = in.Enabled + out.Conditions = *(*[]apiserver.AnonymousAuthCondition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_v1beta1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig is an autogenerated conversion function. +func Convert_v1beta1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(in *AnonymousAuthConfig, out *apiserver.AnonymousAuthConfig, s conversion.Scope) error { + return autoConvert_v1beta1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(in, out, s) +} + +func autoConvert_apiserver_AnonymousAuthConfig_To_v1beta1_AnonymousAuthConfig(in *apiserver.AnonymousAuthConfig, out *AnonymousAuthConfig, s conversion.Scope) error { + out.Enabled = in.Enabled + out.Conditions = *(*[]AnonymousAuthCondition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_apiserver_AnonymousAuthConfig_To_v1beta1_AnonymousAuthConfig is an autogenerated conversion function. +func Convert_apiserver_AnonymousAuthConfig_To_v1beta1_AnonymousAuthConfig(in *apiserver.AnonymousAuthConfig, out *AnonymousAuthConfig, s conversion.Scope) error { + return autoConvert_apiserver_AnonymousAuthConfig_To_v1beta1_AnonymousAuthConfig(in, out, s) +} + func autoConvert_v1beta1_AuthenticationConfiguration_To_apiserver_AuthenticationConfiguration(in *AuthenticationConfiguration, out *apiserver.AuthenticationConfiguration, s conversion.Scope) error { if in.JWT != nil { in, out := &in.JWT, &out.JWT @@ -272,6 +334,7 @@ func autoConvert_v1beta1_AuthenticationConfiguration_To_apiserver_Authentication } else { out.JWT = nil } + out.Anonymous = (*apiserver.AnonymousAuthConfig)(unsafe.Pointer(in.Anonymous)) return nil } @@ -292,6 +355,7 @@ func autoConvert_apiserver_AuthenticationConfiguration_To_v1beta1_Authentication } else { out.JWT = nil } + out.Anonymous = (*AnonymousAuthConfig)(unsafe.Pointer(in.Anonymous)) return nil } diff --git a/pkg/apis/apiserver/v1beta1/zz_generated.deepcopy.go b/pkg/apis/apiserver/v1beta1/zz_generated.deepcopy.go index 7da9db927..0d78e51a9 100644 --- a/pkg/apis/apiserver/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/apiserver/v1beta1/zz_generated.deepcopy.go @@ -25,6 +25,43 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnonymousAuthCondition) DeepCopyInto(out *AnonymousAuthCondition) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnonymousAuthCondition. +func (in *AnonymousAuthCondition) DeepCopy() *AnonymousAuthCondition { + if in == nil { + return nil + } + out := new(AnonymousAuthCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnonymousAuthConfig) DeepCopyInto(out *AnonymousAuthConfig) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]AnonymousAuthCondition, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnonymousAuthConfig. +func (in *AnonymousAuthConfig) DeepCopy() *AnonymousAuthConfig { + if in == nil { + return nil + } + out := new(AnonymousAuthConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AuthenticationConfiguration) DeepCopyInto(out *AuthenticationConfiguration) { *out = *in @@ -36,6 +73,11 @@ func (in *AuthenticationConfiguration) DeepCopyInto(out *AuthenticationConfigura (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Anonymous != nil { + in, out := &in.Anonymous, &out.Anonymous + *out = new(AnonymousAuthConfig) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/apis/apiserver/validation/validation.go b/pkg/apis/apiserver/validation/validation.go index 471eb4a74..fb148f2c9 100644 --- a/pkg/apis/apiserver/validation/validation.go +++ b/pkg/apis/apiserver/validation/validation.go @@ -78,6 +78,15 @@ func ValidateAuthenticationConfiguration(c *api.AuthenticationConfiguration, dis } } + if c.Anonymous != nil { + if !utilfeature.DefaultFeatureGate.Enabled(features.AnonymousAuthConfigurableEndpoints) { + allErrs = append(allErrs, field.Forbidden(field.NewPath("anonymous"), "anonymous is not supported when when AnonymousAuthConfigurableEnpoints feature gate is disabled")) + } + if !c.Anonymous.Enabled && len(c.Anonymous.Conditions) > 0 { + allErrs = append(allErrs, field.Invalid(field.NewPath("anonymous", "conditions"), c.Anonymous.Conditions, "enabled should be set to true when conditions are defined")) + } + } + return allErrs } diff --git a/pkg/apis/apiserver/zz_generated.deepcopy.go b/pkg/apis/apiserver/zz_generated.deepcopy.go index b88c47c67..6439e8220 100644 --- a/pkg/apis/apiserver/zz_generated.deepcopy.go +++ b/pkg/apis/apiserver/zz_generated.deepcopy.go @@ -100,6 +100,43 @@ func (in *AdmissionPluginConfiguration) DeepCopy() *AdmissionPluginConfiguration return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnonymousAuthCondition) DeepCopyInto(out *AnonymousAuthCondition) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnonymousAuthCondition. +func (in *AnonymousAuthCondition) DeepCopy() *AnonymousAuthCondition { + if in == nil { + return nil + } + out := new(AnonymousAuthCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnonymousAuthConfig) DeepCopyInto(out *AnonymousAuthConfig) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]AnonymousAuthCondition, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnonymousAuthConfig. +func (in *AnonymousAuthConfig) DeepCopy() *AnonymousAuthConfig { + if in == nil { + return nil + } + out := new(AnonymousAuthConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AuthenticationConfiguration) DeepCopyInto(out *AuthenticationConfiguration) { *out = *in @@ -111,6 +148,11 @@ func (in *AuthenticationConfiguration) DeepCopyInto(out *AuthenticationConfigura (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Anonymous != nil { + in, out := &in.Anonymous, &out.Anonymous + *out = new(AnonymousAuthConfig) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/authentication/authenticatorfactory/delegating.go b/pkg/authentication/authenticatorfactory/delegating.go index 8e568c7e4..76ef44732 100644 --- a/pkg/authentication/authenticatorfactory/delegating.go +++ b/pkg/authentication/authenticatorfactory/delegating.go @@ -21,6 +21,7 @@ import ( "time" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apiserver/pkg/apis/apiserver" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/group" "k8s.io/apiserver/pkg/authentication/request/anonymous" @@ -39,7 +40,7 @@ import ( // DelegatingAuthenticatorConfig is the minimal configuration needed to create an authenticator // built to delegate authentication to a kube API server type DelegatingAuthenticatorConfig struct { - Anonymous bool + Anonymous *apiserver.AnonymousAuthConfig // TokenAccessReviewClient is a client to do token review. It can be nil. Then every token is ignored. TokenAccessReviewClient authenticationclient.AuthenticationV1Interface @@ -112,15 +113,15 @@ func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.Secur } if len(authenticators) == 0 { - if c.Anonymous { - return anonymous.NewAuthenticator(), &securityDefinitions, nil + if c.Anonymous != nil && c.Anonymous.Enabled { + return anonymous.NewAuthenticator(c.Anonymous.Conditions), &securityDefinitions, nil } - return nil, nil, errors.New("No authentication method configured") + return nil, nil, errors.New("no authentication method configured") } authenticator := group.NewAuthenticatedGroupAdder(unionauth.New(authenticators...)) - if c.Anonymous { - authenticator = unionauth.NewFailOnError(authenticator, anonymous.NewAuthenticator()) + if c.Anonymous != nil && c.Anonymous.Enabled { + authenticator = unionauth.NewFailOnError(authenticator, anonymous.NewAuthenticator(c.Anonymous.Conditions)) } return authenticator, &securityDefinitions, nil } diff --git a/pkg/authentication/request/anonymous/anonymous.go b/pkg/authentication/request/anonymous/anonymous.go index f9177d151..8a8810435 100644 --- a/pkg/authentication/request/anonymous/anonymous.go +++ b/pkg/authentication/request/anonymous/anonymous.go @@ -19,25 +19,44 @@ package anonymous import ( "net/http" + "k8s.io/apiserver/pkg/apis/apiserver" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/user" ) const ( - anonymousUser = user.Anonymous - + anonymousUser = user.Anonymous unauthenticatedGroup = user.AllUnauthenticated ) -func NewAuthenticator() authenticator.Request { - return authenticator.RequestFunc(func(req *http.Request) (*authenticator.Response, bool, error) { - auds, _ := authenticator.AudiencesFrom(req.Context()) - return &authenticator.Response{ - User: &user.DefaultInfo{ - Name: anonymousUser, - Groups: []string{unauthenticatedGroup}, - }, - Audiences: auds, - }, true, nil - }) +type Authenticator struct { + allowedPaths map[string]bool +} + +func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) { + if len(a.allowedPaths) > 0 && !a.allowedPaths[req.URL.Path] { + return nil, false, nil + } + + auds, _ := authenticator.AudiencesFrom(req.Context()) + return &authenticator.Response{ + User: &user.DefaultInfo{ + Name: anonymousUser, + Groups: []string{unauthenticatedGroup}, + }, + Audiences: auds, + }, true, nil +} + +// NewAuthenticator returns a new anonymous authenticator. +// When conditions is empty all requests are authenticated as anonymous. +// When conditions are non-empty only those requests that match the at-least one +// condition are authenticated as anonymous. +func NewAuthenticator(conditions []apiserver.AnonymousAuthCondition) authenticator.Request { + allowedPaths := make(map[string]bool) + for _, c := range conditions { + allowedPaths[c.Path] = true + } + + return &Authenticator{allowedPaths: allowedPaths} } diff --git a/pkg/authentication/request/anonymous/anonymous_test.go b/pkg/authentication/request/anonymous/anonymous_test.go index 494ab6097..5fcc60221 100644 --- a/pkg/authentication/request/anonymous/anonymous_test.go +++ b/pkg/authentication/request/anonymous/anonymous_test.go @@ -18,15 +18,16 @@ package anonymous import ( "net/http" + "net/url" "testing" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apiserver/pkg/authentication/authenticator" + "k8s.io/apiserver/pkg/apis/apiserver" "k8s.io/apiserver/pkg/authentication/user" ) func TestAnonymous(t *testing.T) { - var a authenticator.Request = NewAuthenticator() + a := NewAuthenticator(nil) r, ok, err := a.AuthenticateRequest(&http.Request{}) if err != nil { t.Fatalf("Unexpected error %v", err) @@ -41,3 +42,85 @@ func TestAnonymous(t *testing.T) { t.Fatalf("Expected group %s, got %v", user.AllUnauthenticated, r.User.GetGroups()) } } + +func TestAnonymousRestricted(t *testing.T) { + a := NewAuthenticator([]apiserver.AnonymousAuthCondition{ + { + Path: "/healthz", + }, + { + Path: "/readyz", + }, + { + Path: "/livez", + }, + }) + + testCases := []struct { + desc string + path string + want user.DefaultInfo + wantAllowed bool + }{ + { + desc: "/healthz", + path: "https://123.123.123.123/healthz", + want: user.DefaultInfo{ + Name: anonymousUser, + Groups: []string{unauthenticatedGroup}, + }, + wantAllowed: true, + }, + { + desc: "/readyz", + path: "https://123.123.123.123/readyz", + want: user.DefaultInfo{ + Name: anonymousUser, + Groups: []string{unauthenticatedGroup}, + }, + wantAllowed: true, + }, + { + desc: "/livez", + path: "https://123.123.123.123/livez", + want: user.DefaultInfo{ + Name: anonymousUser, + Groups: []string{unauthenticatedGroup}, + }, + wantAllowed: true, + }, + { + desc: "/api", + path: "https://123.123.123.123/api", + wantAllowed: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + u, err := url.Parse(tc.path) + if err != nil { + t.Fatal(err) + } + r, allowed, err := a.AuthenticateRequest(&http.Request{URL: u}) + if err != nil { + t.Fatal(err) + } + + if tc.wantAllowed != allowed { + t.Fatalf("want allowed: %v, got allowed: %v", tc.wantAllowed, allowed) + } + + if !tc.wantAllowed { + return + } + + if r.User.GetName() != tc.want.Name { + t.Fatalf("Expected username %s, got %s", user.Anonymous, r.User.GetName()) + } + if !sets.NewString(r.User.GetGroups()...).Equal(sets.NewString(tc.want.Groups...)) { + t.Fatalf("Expected group %s, got %v", tc.want.Groups, r.User.GetGroups()) + } + }) + } +} diff --git a/pkg/endpoints/filters/authentication_test.go b/pkg/endpoints/filters/authentication_test.go index f6fcb5ad7..2ef45a6be 100644 --- a/pkg/endpoints/filters/authentication_test.go +++ b/pkg/endpoints/filters/authentication_test.go @@ -487,7 +487,7 @@ func TestUnauthenticatedHTTP2ClientConnectionClose(t *testing.T) { case "error": return nil, false, errors.New("authn err") case "anonymous": - return anonymous.NewAuthenticator().AuthenticateRequest(r) + return anonymous.NewAuthenticator(nil).AuthenticateRequest(r) case "anonymous_group": return &authenticator.Response{User: &user.DefaultInfo{Groups: []string{user.AllUnauthenticated}}}, true, nil default: diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 8448e36ec..de27f413a 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -53,6 +53,13 @@ const ( // caching with ETags containing all APIResources known to the apiserver. AggregatedDiscoveryEndpoint featuregate.Feature = "AggregatedDiscoveryEndpoint" + // owner: @vinayakankugoyal + // kep: https://kep.k8s.io/4633 + // alpha: v1.31 + // + // Allows us to enable anonymous auth for only certain apiserver endpoints. + AnonymousAuthConfigurableEndpoints featuregate.Feature = "AnonymousAuthConfigurableEndpoints" + // owner: @smarterclayton // alpha: v1.8 // beta: v1.9 @@ -340,6 +347,8 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate // available throughout Kubernetes binaries. var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ + AnonymousAuthConfigurableEndpoints: {Default: false, PreRelease: featuregate.Alpha}, + AggregatedDiscoveryEndpoint: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33 AdmissionWebhookMatchConditions: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33 diff --git a/pkg/server/options/authentication.go b/pkg/server/options/authentication.go index e9a61d30b..c40e4cf43 100644 --- a/pkg/server/options/authentication.go +++ b/pkg/server/options/authentication.go @@ -26,6 +26,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apiserver/pkg/apis/apiserver" "k8s.io/apiserver/pkg/authentication/authenticatorfactory" "k8s.io/apiserver/pkg/authentication/request/headerrequest" "k8s.io/apiserver/pkg/server" @@ -222,8 +223,8 @@ type DelegatingAuthenticationOptions struct { // CustomRoundTripperFn allows for specifying a middleware function for custom HTTP behaviour for the authentication webhook client. CustomRoundTripperFn transport.WrapperFunc - // DisableAnonymous gives user an option to disable Anonymous authentication. - DisableAnonymous bool + // Anonymous gives user an option to enable/disable Anonymous authentication. + Anonymous *apiserver.AnonymousAuthConfig } func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions { @@ -238,6 +239,7 @@ func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions { }, WebhookRetryBackoff: DefaultAuthWebhookRetryBackoff(), TokenRequestTimeout: 10 * time.Second, + Anonymous: &apiserver.AnonymousAuthConfig{Enabled: true}, } } @@ -305,7 +307,7 @@ func (s *DelegatingAuthenticationOptions) ApplyTo(authenticationInfo *server.Aut } cfg := authenticatorfactory.DelegatingAuthenticatorConfig{ - Anonymous: !s.DisableAnonymous, + Anonymous: &apiserver.AnonymousAuthConfig{Enabled: true}, CacheTTL: s.CacheTTL, WebhookRetryBackoff: s.WebhookRetryBackoff, TokenAccessReviewTimeout: s.TokenRequestTimeout,