Skip to content

Commit 522e2e5

Browse files
authored
Merge pull request kubernetes#124917 from vinayakankugoyal/kep4633
KEP-4633: Only allow anonymous auth for configured endpoints.
2 parents bec82cc + 5e6a493 commit 522e2e5

File tree

25 files changed

+830
-65
lines changed

25 files changed

+830
-65
lines changed

cmd/kube-apiserver/app/options/options_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,7 @@ func TestAddFlags(t *testing.T) {
256256
EnableContentionProfiling: true,
257257
},
258258
Authentication: &kubeoptions.BuiltInAuthenticationOptions{
259-
Anonymous: &kubeoptions.AnonymousAuthenticationOptions{
260-
Allow: false,
261-
},
259+
Anonymous: s.Authentication.Anonymous,
262260
ClientCert: &apiserveroptions.ClientCertAuthenticationOptions{
263261
ClientCA: "/client-ca",
264262
},
@@ -348,7 +346,7 @@ func TestAddFlags(t *testing.T) {
348346
s.Authorization.AreLegacyFlagsSet = nil
349347

350348
if !reflect.DeepEqual(expected, s) {
351-
t.Errorf("Got different run options than expected.\nDifference detected on:\n%s", cmp.Diff(expected, s, cmpopts.IgnoreUnexported(admission.Plugins{}, kubeoptions.OIDCAuthenticationOptions{})))
349+
t.Errorf("Got different run options than expected.\nDifference detected on:\n%s", cmp.Diff(expected, s, cmpopts.IgnoreUnexported(admission.Plugins{}, kubeoptions.OIDCAuthenticationOptions{}, kubeoptions.AnonymousAuthenticationOptions{})))
352350
}
353351
testEffectiveVersion := s.GenericServerRunOptions.ComponentGlobalsRegistry.EffectiveVersionFor("test")
354352
if testEffectiveVersion.EmulationVersion().String() != "1.31" {

cmd/kube-controller-manager/app/options/options_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
eventv1 "k8s.io/api/events/v1"
3131
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3232
utilerrors "k8s.io/apimachinery/pkg/util/errors"
33+
"k8s.io/apiserver/pkg/apis/apiserver"
3334
apiserveroptions "k8s.io/apiserver/pkg/server/options"
3435
cpconfig "k8s.io/cloud-provider/config"
3536
serviceconfig "k8s.io/cloud-provider/controllers/service/config"
@@ -430,6 +431,7 @@ func TestAddFlags(t *testing.T) {
430431
ExtraHeaderPrefixes: []string{"x-remote-extra-"},
431432
},
432433
RemoteKubeConfigFileOptional: true,
434+
Anonymous: &apiserver.AnonymousAuthConfig{Enabled: true},
433435
},
434436
Authorization: &apiserveroptions.DelegatingAuthorizationOptions{
435437
AllowCacheTTL: 10 * time.Second,

cmd/kubelet/app/auth.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"reflect"
2424

2525
"k8s.io/apimachinery/pkg/types"
26+
"k8s.io/apiserver/pkg/apis/apiserver"
2627
"k8s.io/apiserver/pkg/authentication/authenticator"
2728
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
2829
"k8s.io/apiserver/pkg/authorization/authorizer"
@@ -77,7 +78,7 @@ func BuildAuthn(client authenticationclient.AuthenticationV1Interface, authn kub
7778
}
7879

7980
authenticatorConfig := authenticatorfactory.DelegatingAuthenticatorConfig{
80-
Anonymous: authn.Anonymous.Enabled,
81+
Anonymous: &apiserver.AnonymousAuthConfig{Enabled: authn.Anonymous.Enabled},
8182
CacheTTL: authn.Webhook.CacheTTL.Duration,
8283
ClientCertificateCAContentProvider: dynamicCAContentFromFile,
8384
}

pkg/controlplane/apiserver/options/options_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ import (
3838
"k8s.io/component-base/featuregate"
3939
"k8s.io/component-base/logs"
4040
"k8s.io/component-base/metrics"
41-
netutils "k8s.io/utils/net"
42-
4341
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
42+
netutils "k8s.io/utils/net"
4443
)
4544

4645
func TestAddFlags(t *testing.T) {
@@ -241,9 +240,7 @@ func TestAddFlags(t *testing.T) {
241240
EnableContentionProfiling: true,
242241
},
243242
Authentication: &kubeoptions.BuiltInAuthenticationOptions{
244-
Anonymous: &kubeoptions.AnonymousAuthenticationOptions{
245-
Allow: false,
246-
},
243+
Anonymous: s.Authentication.Anonymous,
247244
ClientCert: &apiserveroptions.ClientCertAuthenticationOptions{
248245
ClientCA: "/client-ca",
249246
},
@@ -302,7 +299,7 @@ func TestAddFlags(t *testing.T) {
302299
s.Authorization.AreLegacyFlagsSet = nil
303300

304301
if !reflect.DeepEqual(expected, s) {
305-
t.Errorf("Got different run options than expected.\nDifference detected on:\n%s", cmp.Diff(expected, s, cmpopts.IgnoreUnexported(admission.Plugins{}, kubeoptions.OIDCAuthenticationOptions{})))
302+
t.Errorf("Got different run options than expected.\nDifference detected on:\n%s", cmp.Diff(expected, s, cmpopts.IgnoreUnexported(admission.Plugins{}, kubeoptions.OIDCAuthenticationOptions{}, kubeoptions.AnonymousAuthenticationOptions{})))
306303
}
307304

308305
testEffectiveVersion := s.GenericServerRunOptions.ComponentGlobalsRegistry.EffectiveVersionFor("test")

pkg/features/kube_features.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
12061206

12071207
genericfeatures.AggregatedDiscoveryEndpoint: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33
12081208

1209+
genericfeatures.AnonymousAuthConfigurableEndpoints: {Default: false, PreRelease: featuregate.Alpha},
1210+
12091211
genericfeatures.APIListChunking: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32
12101212

12111213
genericfeatures.APIPriorityAndFairness: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.31

pkg/kubeapiserver/authenticator/config.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ import (
5555

5656
// Config contains the data on how to authenticate a request to the Kube API Server
5757
type Config struct {
58-
Anonymous bool
58+
// Anonymous holds the effective anonymous config, specified either via config file
59+
// (hoisted out of AuthenticationConfig) or via flags (constructed from flag-specified values).
60+
Anonymous apiserver.AnonymousAuthConfig
61+
5962
BootstrapToken bool
6063

6164
TokenAuthFile string
@@ -213,8 +216,8 @@ func (config Config) New(serverLifecycle context.Context) (authenticator.Request
213216
}
214217

215218
if len(authenticators) == 0 {
216-
if config.Anonymous {
217-
return anonymous.NewAuthenticator(), nil, &securityDefinitionsV2, securitySchemesV3, nil
219+
if config.Anonymous.Enabled {
220+
return anonymous.NewAuthenticator(config.Anonymous.Conditions), nil, &securityDefinitionsV2, securitySchemesV3, nil
218221
}
219222
return nil, nil, &securityDefinitionsV2, securitySchemesV3, nil
220223
}
@@ -223,10 +226,10 @@ func (config Config) New(serverLifecycle context.Context) (authenticator.Request
223226

224227
authenticator = group.NewAuthenticatedGroupAdder(authenticator)
225228

226-
if config.Anonymous {
229+
if config.Anonymous.Enabled {
227230
// If the authenticator chain returns an error, return an error (don't consider a bad bearer token
228231
// or invalid username/password combination anonymous).
229-
authenticator = union.NewFailOnError(authenticator, anonymous.NewAuthenticator())
232+
authenticator = union.NewFailOnError(authenticator, anonymous.NewAuthenticator(config.Anonymous.Conditions))
230233
}
231234

232235
return authenticator, updateAuthenticationConfig, &securityDefinitionsV2, securitySchemesV3, nil

pkg/kubeapiserver/options/authentication.go

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"net/url"
2424
"os"
25+
"reflect"
2526
"strings"
2627
"sync"
2728
"time"
@@ -32,6 +33,7 @@ import (
3233
"k8s.io/apimachinery/pkg/runtime"
3334
"k8s.io/apimachinery/pkg/runtime/serializer"
3435
"k8s.io/apimachinery/pkg/util/sets"
36+
"k8s.io/apimachinery/pkg/util/validation/field"
3537
"k8s.io/apimachinery/pkg/util/wait"
3638
"k8s.io/apiserver/pkg/apis/apiserver"
3739
"k8s.io/apiserver/pkg/apis/apiserver/install"
@@ -97,7 +99,8 @@ type BuiltInAuthenticationOptions struct {
9799

98100
// AnonymousAuthenticationOptions contains anonymous authentication options for API Server
99101
type AnonymousAuthenticationOptions struct {
100-
Allow bool
102+
Allow bool
103+
areFlagsSet func() bool
101104
}
102105

103106
// BootstrapTokenAuthenticationOptions contains bootstrap token authentication options for API Server
@@ -171,7 +174,10 @@ func (o *BuiltInAuthenticationOptions) WithAll() *BuiltInAuthenticationOptions {
171174

172175
// WithAnonymous set default value for anonymous authentication
173176
func (o *BuiltInAuthenticationOptions) WithAnonymous() *BuiltInAuthenticationOptions {
174-
o.Anonymous = &AnonymousAuthenticationOptions{Allow: true}
177+
o.Anonymous = &AnonymousAuthenticationOptions{
178+
Allow: true,
179+
areFlagsSet: func() bool { return false },
180+
}
175181
return o
176182
}
177183

@@ -296,6 +302,14 @@ func (o *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
296302
return
297303
}
298304

305+
fs.StringVar(&o.AuthenticationConfigFile, "authentication-config", o.AuthenticationConfigFile, ""+
306+
"File with Authentication Configuration to configure the JWT Token authenticator or the anonymous authenticator. "+
307+
"Note: This feature is in Alpha since v1.29."+
308+
"--feature-gate=StructuredAuthenticationConfiguration=true needs to be set for enabling this feature."+
309+
"This feature is mutually exclusive with the oidc-* flags."+
310+
"To configure anonymous authenticator you need to enable --feature-gate=AnonymousAuthConfigurableEndpoints."+
311+
"When you configure anonymous authenticator in the authentication config you cannot use the --anonymous-auth flag.")
312+
299313
fs.StringSliceVar(&o.APIAudiences, "api-audiences", o.APIAudiences, ""+
300314
"Identifiers of the API. The service account token authenticator will validate that "+
301315
"tokens used against the API are bound to at least one of these audiences. If the "+
@@ -307,6 +321,10 @@ func (o *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
307321
"Enables anonymous requests to the secure port of the API server. "+
308322
"Requests that are not rejected by another authentication method are treated as anonymous requests. "+
309323
"Anonymous requests have a username of system:anonymous, and a group name of system:unauthenticated.")
324+
325+
o.Anonymous.areFlagsSet = func() bool {
326+
return fs.Changed("anonymous-auth")
327+
}
310328
}
311329

312330
if o.BootstrapToken != nil {
@@ -360,12 +378,6 @@ func (o *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
360378
"If set, the claim is verified to be present in the ID Token with a matching value. "+
361379
"Repeat this flag to specify multiple claims.")
362380

363-
fs.StringVar(&o.AuthenticationConfigFile, "authentication-config", o.AuthenticationConfigFile, ""+
364-
"File with Authentication Configuration to configure the JWT Token authenticator. "+
365-
"Note: This feature is in Alpha since v1.29."+
366-
"--feature-gate=StructuredAuthenticationConfiguration=true needs to be set for enabling this feature."+
367-
"This feature is mutually exclusive with the oidc-* flags.")
368-
369381
o.OIDC.areFlagsConfigured = func() bool {
370382
return fs.Changed(oidcIssuerURLFlag) ||
371383
fs.Changed(oidcClientIDFlag) ||
@@ -454,10 +466,6 @@ func (o *BuiltInAuthenticationOptions) ToAuthenticationConfig() (kubeauthenticat
454466
TokenFailureCacheTTL: o.TokenFailureCacheTTL,
455467
}
456468

457-
if o.Anonymous != nil {
458-
ret.Anonymous = o.Anonymous.Allow
459-
}
460-
461469
if o.BootstrapToken != nil {
462470
ret.BootstrapToken = o.BootstrapToken.Enable
463471
}
@@ -471,12 +479,18 @@ func (o *BuiltInAuthenticationOptions) ToAuthenticationConfig() (kubeauthenticat
471479
}
472480

473481
// When the StructuredAuthenticationConfiguration feature is enabled and the authentication config file is provided,
474-
// load the authentication config from the file.
482+
// load the authentication config from the file, otherwise set up an empty configuration.
475483
if len(o.AuthenticationConfigFile) > 0 {
476484
var err error
477485
if ret.AuthenticationConfig, ret.AuthenticationConfigData, err = loadAuthenticationConfig(o.AuthenticationConfigFile); err != nil {
478486
return kubeauthenticator.Config{}, err
479487
}
488+
} else {
489+
ret.AuthenticationConfig = &apiserver.AuthenticationConfiguration{}
490+
}
491+
492+
// Set up JWT authenticators from config file or from flags
493+
if len(o.AuthenticationConfigFile) > 0 {
480494
// all known signing algs are allowed when using authentication config
481495
// TODO: what we really want to express is 'any alg is fine as long it matches a public key'
482496
ret.OIDCSigningAlgs = oidc.AllValidSigningAlgorithms()
@@ -534,20 +548,30 @@ func (o *BuiltInAuthenticationOptions) ToAuthenticationConfig() (kubeauthenticat
534548
jwtAuthenticator.ClaimValidationRules = claimValidationRules
535549
}
536550

537-
authConfig := &apiserver.AuthenticationConfiguration{
538-
JWT: []apiserver.JWTAuthenticator{jwtAuthenticator},
539-
}
551+
ret.AuthenticationConfig.JWT = []apiserver.JWTAuthenticator{jwtAuthenticator}
540552

541-
ret.AuthenticationConfig = authConfig
542553
ret.OIDCSigningAlgs = o.OIDC.SigningAlgs
543554
}
544555

545-
if ret.AuthenticationConfig != nil {
546-
if err := apiservervalidation.ValidateAuthenticationConfiguration(ret.AuthenticationConfig, ret.ServiceAccountIssuers).ToAggregate(); err != nil {
547-
return kubeauthenticator.Config{}, err
556+
// Set up anonymous authenticator from config file or flags
557+
if o.Anonymous != nil {
558+
switch {
559+
case ret.AuthenticationConfig.Anonymous != nil && o.Anonymous.areFlagsSet():
560+
// Flags and config file are mutually exclusive
561+
return kubeauthenticator.Config{}, field.Forbidden(field.NewPath("anonymous"), "--anonynous-auth flag cannot be set when anonymous field is configured in authentication configuration file")
562+
case ret.AuthenticationConfig.Anonymous != nil:
563+
// Use the config-file-specified values
564+
ret.Anonymous = *ret.AuthenticationConfig.Anonymous
565+
default:
566+
// Use the flag-specified values
567+
ret.Anonymous = apiserver.AnonymousAuthConfig{Enabled: o.Anonymous.Allow}
548568
}
549569
}
550570

571+
if err := apiservervalidation.ValidateAuthenticationConfiguration(ret.AuthenticationConfig, ret.ServiceAccountIssuers).ToAggregate(); err != nil {
572+
return kubeauthenticator.Config{}, err
573+
}
574+
551575
if o.RequestHeader != nil {
552576
var err error
553577
ret.RequestHeaderConfig, err = o.RequestHeader.ToAuthenticationRequestHeaderConfig()
@@ -683,6 +707,10 @@ func (o *BuiltInAuthenticationOptions) ApplyTo(
683707
authenticationconfigmetrics.RegisterMetrics()
684708
trackedAuthenticationConfigData := authenticatorConfig.AuthenticationConfigData
685709
var mu sync.Mutex
710+
711+
// ensure anonymous config doesn't change on reload
712+
originalFileAnonymousConfig := authenticatorConfig.AuthenticationConfig.DeepCopy().Anonymous
713+
686714
go filesystem.WatchUntil(
687715
ctx,
688716
time.Minute,
@@ -716,7 +744,11 @@ func (o *BuiltInAuthenticationOptions) ApplyTo(
716744
return
717745
}
718746

719-
if err := apiservervalidation.ValidateAuthenticationConfiguration(authConfig, authenticatorConfig.ServiceAccountIssuers).ToAggregate(); err != nil {
747+
validationErrs := apiservervalidation.ValidateAuthenticationConfiguration(authConfig, authenticatorConfig.ServiceAccountIssuers)
748+
if !reflect.DeepEqual(originalFileAnonymousConfig, authConfig.Anonymous) {
749+
validationErrs = append(validationErrs, field.Forbidden(field.NewPath("anonymous"), "changed from initial configuration file"))
750+
}
751+
if err := validationErrs.ToAggregate(); err != nil {
720752
klog.ErrorS(err, "failed to validate authentication config")
721753
authenticationconfigmetrics.RecordAuthenticationConfigAutomaticReloadFailure(apiServerID)
722754
// this config is not semantically valid and never will be, update the tracker so we stop retrying

0 commit comments

Comments
 (0)