Skip to content

Commit 8876563

Browse files
authored
Merge pull request kubernetes#90360 from tnozicka/fix-client-ca-reload
Fix client-ca dynamic reload in apiserver
2 parents 66e3c66 + b22a170 commit 8876563

File tree

8 files changed

+278
-120
lines changed

8 files changed

+278
-120
lines changed

cmd/kube-apiserver/app/BUILD

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ go_library(
1313
"//cmd/kube-apiserver/app/options:go_default_library",
1414
"//pkg/api/legacyscheme:go_default_library",
1515
"//pkg/capabilities:go_default_library",
16-
"//pkg/controller/serviceaccount:go_default_library",
1716
"//pkg/features:go_default_library",
1817
"//pkg/generated/openapi:go_default_library",
1918
"//pkg/kubeapiserver:go_default_library",
@@ -29,7 +28,6 @@ go_library(
2928
"//pkg/registry/cachesize:go_default_library",
3029
"//pkg/registry/rbac/rest:go_default_library",
3130
"//pkg/serviceaccount:go_default_library",
32-
"//plugin/pkg/auth/authenticator/token/bootstrap:go_default_library",
3331
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1:go_default_library",
3432
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
3533
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver:go_default_library",
@@ -43,7 +41,6 @@ go_library(
4341
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
4442
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
4543
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
46-
"//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
4744
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
4845
"//staging/src/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library",
4946
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
@@ -76,12 +73,17 @@ go_library(
7673
"//staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1:go_default_library",
7774
"//staging/src/k8s.io/kube-aggregator/pkg/client/informers/externalversions/apiregistration/v1:go_default_library",
7875
"//staging/src/k8s.io/kube-aggregator/pkg/controllers/autoregister:go_default_library",
79-
"//vendor/github.com/go-openapi/spec:go_default_library",
8076
"//vendor/github.com/spf13/cobra:go_default_library",
8177
"//vendor/k8s.io/klog:go_default_library",
8278
],
8379
)
8480

81+
go_test(
82+
name = "go_default_test",
83+
srcs = ["server_test.go"],
84+
embed = [":go_default_library"],
85+
)
86+
8587
filegroup(
8688
name = "package-srcs",
8789
srcs = glob(["**"]),
@@ -99,9 +101,3 @@ filegroup(
99101
tags = ["automanaged"],
100102
visibility = ["//visibility:public"],
101103
)
102-
103-
go_test(
104-
name = "go_default_test",
105-
srcs = ["server_test.go"],
106-
embed = [":go_default_library"],
107-
)

cmd/kube-apiserver/app/server.go

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,14 @@ import (
3030
"strings"
3131
"time"
3232

33-
"github.com/go-openapi/spec"
3433
"github.com/spf13/cobra"
3534

3635
extensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
37-
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3836
utilerrors "k8s.io/apimachinery/pkg/util/errors"
3937
utilnet "k8s.io/apimachinery/pkg/util/net"
4038
"k8s.io/apimachinery/pkg/util/sets"
4139
utilwait "k8s.io/apimachinery/pkg/util/wait"
4240
"k8s.io/apiserver/pkg/admission"
43-
"k8s.io/apiserver/pkg/authentication/authenticator"
4441
"k8s.io/apiserver/pkg/authorization/authorizer"
4542
openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
4643
genericfeatures "k8s.io/apiserver/pkg/features"
@@ -70,7 +67,6 @@ import (
7067
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
7168
"k8s.io/kubernetes/pkg/api/legacyscheme"
7269
"k8s.io/kubernetes/pkg/capabilities"
73-
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
7470
"k8s.io/kubernetes/pkg/features"
7571
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
7672
"k8s.io/kubernetes/pkg/kubeapiserver"
@@ -85,7 +81,6 @@ import (
8581
"k8s.io/kubernetes/pkg/registry/cachesize"
8682
rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest"
8783
"k8s.io/kubernetes/pkg/serviceaccount"
88-
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap"
8984
)
9085

9186
const (
@@ -440,9 +435,6 @@ func buildGenericConfig(
440435
if lastErr = s.SecureServing.ApplyTo(&genericConfig.SecureServing, &genericConfig.LoopbackClientConfig); lastErr != nil {
441436
return
442437
}
443-
if lastErr = s.Authentication.ApplyTo(genericConfig); lastErr != nil {
444-
return
445-
}
446438
if lastErr = s.Features.ApplyTo(genericConfig); lastErr != nil {
447439
return
448440
}
@@ -498,9 +490,8 @@ func buildGenericConfig(
498490
}
499491
versionedInformers = clientgoinformers.NewSharedInformerFactory(clientgoExternalClient, 10*time.Minute)
500492

501-
genericConfig.Authentication.Authenticator, genericConfig.OpenAPIConfig.SecurityDefinitions, err = BuildAuthenticator(s, genericConfig.EgressSelector, clientgoExternalClient, versionedInformers)
502-
if err != nil {
503-
lastErr = fmt.Errorf("invalid authentication config: %v", err)
493+
// Authentication.ApplyTo requires already applied OpenAPIConfig and EgressSelector if present
494+
if lastErr = s.Authentication.ApplyTo(&genericConfig.Authentication, genericConfig.SecureServing, genericConfig.EgressSelector, genericConfig.OpenAPIConfig, clientgoExternalClient, versionedInformers); lastErr != nil {
504495
return
505496
}
506497

@@ -559,35 +550,6 @@ func buildGenericConfig(
559550
return
560551
}
561552

562-
// BuildAuthenticator constructs the authenticator
563-
func BuildAuthenticator(s *options.ServerRunOptions, EgressSelector *egressselector.EgressSelector, extclient clientgoclientset.Interface, versionedInformer clientgoinformers.SharedInformerFactory) (authenticator.Request, *spec.SecurityDefinitions, error) {
564-
authenticatorConfig, err := s.Authentication.ToAuthenticationConfig()
565-
if err != nil {
566-
return nil, nil, err
567-
}
568-
if s.Authentication.ServiceAccounts.Lookup || utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) {
569-
authenticatorConfig.ServiceAccountTokenGetter = serviceaccountcontroller.NewGetterFromClient(
570-
extclient,
571-
versionedInformer.Core().V1().Secrets().Lister(),
572-
versionedInformer.Core().V1().ServiceAccounts().Lister(),
573-
versionedInformer.Core().V1().Pods().Lister(),
574-
)
575-
}
576-
authenticatorConfig.BootstrapTokenAuthenticator = bootstrap.NewTokenAuthenticator(
577-
versionedInformer.Core().V1().Secrets().Lister().Secrets(v1.NamespaceSystem),
578-
)
579-
580-
if EgressSelector != nil {
581-
egressDialer, err := EgressSelector.Lookup(egressselector.Master.AsNetworkContext())
582-
if err != nil {
583-
return nil, nil, err
584-
}
585-
authenticatorConfig.CustomDial = egressDialer
586-
}
587-
588-
return authenticatorConfig.New()
589-
}
590-
591553
// BuildAuthorizer constructs the authorizer
592554
func BuildAuthorizer(s *options.ServerRunOptions, EgressSelector *egressselector.EgressSelector, versionedInformers clientgoinformers.SharedInformerFactory) (authorizer.Authorizer, authorizer.RuleResolver, error) {
593555
authorizationConfig := s.Authorization.ToAuthorizationConfig(versionedInformers)

cmd/kubelet/app/auth.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ import (
3636
)
3737

3838
// BuildAuth creates an authenticator, an authorizer, and a matching authorizer attributes getter compatible with the kubelet's needs
39-
func BuildAuth(nodeName types.NodeName, client clientset.Interface, config kubeletconfig.KubeletConfiguration) (server.AuthInterface, error) {
39+
// It returns AuthInterface, a run method to start internal controllers (like cert reloading) and error.
40+
func BuildAuth(nodeName types.NodeName, client clientset.Interface, config kubeletconfig.KubeletConfiguration) (server.AuthInterface, func(<-chan struct{}), error) {
4041
// Get clients, if provided
4142
var (
4243
tokenClient authenticationclient.TokenReviewInterface
@@ -47,47 +48,55 @@ func BuildAuth(nodeName types.NodeName, client clientset.Interface, config kubel
4748
sarClient = client.AuthorizationV1().SubjectAccessReviews()
4849
}
4950

50-
authenticator, err := BuildAuthn(tokenClient, config.Authentication)
51+
authenticator, runAuthenticatorCAReload, err := BuildAuthn(tokenClient, config.Authentication)
5152
if err != nil {
52-
return nil, err
53+
return nil, nil, err
5354
}
5455

5556
attributes := server.NewNodeAuthorizerAttributesGetter(nodeName)
5657

5758
authorizer, err := BuildAuthz(sarClient, config.Authorization)
5859
if err != nil {
59-
return nil, err
60+
return nil, nil, err
6061
}
6162

62-
return server.NewKubeletAuth(authenticator, attributes, authorizer), nil
63+
return server.NewKubeletAuth(authenticator, attributes, authorizer), runAuthenticatorCAReload, nil
6364
}
6465

6566
// BuildAuthn creates an authenticator compatible with the kubelet's needs
66-
func BuildAuthn(client authenticationclient.TokenReviewInterface, authn kubeletconfig.KubeletAuthentication) (authenticator.Request, error) {
67-
var clientCertificateCAContentProvider authenticatorfactory.CAContentProvider
67+
func BuildAuthn(client authenticationclient.TokenReviewInterface, authn kubeletconfig.KubeletAuthentication) (authenticator.Request, func(<-chan struct{}), error) {
68+
var dynamicCAContentFromFile *dynamiccertificates.DynamicFileCAContent
6869
var err error
6970
if len(authn.X509.ClientCAFile) > 0 {
70-
clientCertificateCAContentProvider, err = dynamiccertificates.NewDynamicCAContentFromFile("client-ca-bundle", authn.X509.ClientCAFile)
71+
dynamicCAContentFromFile, err = dynamiccertificates.NewDynamicCAContentFromFile("client-ca-bundle", authn.X509.ClientCAFile)
7172
if err != nil {
72-
return nil, err
73+
return nil, nil, err
7374
}
7475
}
7576

7677
authenticatorConfig := authenticatorfactory.DelegatingAuthenticatorConfig{
7778
Anonymous: authn.Anonymous.Enabled,
7879
CacheTTL: authn.Webhook.CacheTTL.Duration,
79-
ClientCertificateCAContentProvider: clientCertificateCAContentProvider,
80+
ClientCertificateCAContentProvider: dynamicCAContentFromFile,
8081
}
8182

8283
if authn.Webhook.Enabled {
8384
if client == nil {
84-
return nil, errors.New("no client provided, cannot use webhook authentication")
85+
return nil, nil, errors.New("no client provided, cannot use webhook authentication")
8586
}
8687
authenticatorConfig.TokenAccessReviewClient = client
8788
}
8889

8990
authenticator, _, err := authenticatorConfig.New()
90-
return authenticator, err
91+
if err != nil {
92+
return nil, nil, err
93+
}
94+
95+
return authenticator, func(stopCh <-chan struct{}) {
96+
if dynamicCAContentFromFile != nil {
97+
go dynamicCAContentFromFile.Run(1, stopCh)
98+
}
99+
}, err
91100
}
92101

93102
// BuildAuthz creates an authorizer compatible with the kubelet's needs

cmd/kubelet/app/server.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,11 +599,12 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate f
599599
}
600600

601601
if kubeDeps.Auth == nil {
602-
auth, err := BuildAuth(nodeName, kubeDeps.KubeClient, s.KubeletConfiguration)
602+
auth, runAuthenticatorCAReload, err := BuildAuth(nodeName, kubeDeps.KubeClient, s.KubeletConfiguration)
603603
if err != nil {
604604
return err
605605
}
606606
kubeDeps.Auth = auth
607+
runAuthenticatorCAReload(stopCh)
607608
}
608609

609610
var cgroupRoots []string

pkg/kubeapiserver/options/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ go_library(
1414
importpath = "k8s.io/kubernetes/pkg/kubeapiserver/options",
1515
visibility = ["//visibility:public"],
1616
deps = [
17+
"//pkg/controller/serviceaccount:go_default_library",
1718
"//pkg/features:go_default_library",
1819
"//pkg/kubeapiserver/authenticator:go_default_library",
1920
"//pkg/kubeapiserver/authorizer:go_default_library",
@@ -50,6 +51,8 @@ go_library(
5051
"//plugin/pkg/admission/storage/persistentvolume/resize:go_default_library",
5152
"//plugin/pkg/admission/storage/storageclass/setdefault:go_default_library",
5253
"//plugin/pkg/admission/storage/storageobjectinuseprotection:go_default_library",
54+
"//plugin/pkg/auth/authenticator/token/bootstrap:go_default_library",
55+
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
5356
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
5457
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
5558
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
@@ -58,14 +61,17 @@ go_library(
5861
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating:go_default_library",
5962
"//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
6063
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
64+
"//staging/src/k8s.io/apiserver/pkg/server/egressselector:go_default_library",
6165
"//staging/src/k8s.io/apiserver/pkg/server/options:go_default_library",
6266
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
6367
"//staging/src/k8s.io/client-go/informers:go_default_library",
68+
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
6469
"//staging/src/k8s.io/client-go/rest:go_default_library",
6570
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
6671
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
6772
"//vendor/github.com/spf13/pflag:go_default_library",
6873
"//vendor/k8s.io/klog:go_default_library",
74+
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
6975
],
7076
)
7177

pkg/kubeapiserver/options/authentication.go

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,24 @@ import (
2424
"time"
2525

2626
"github.com/spf13/pflag"
27-
"k8s.io/klog"
2827

28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2929
"k8s.io/apimachinery/pkg/util/sets"
3030
"k8s.io/apiserver/pkg/authentication/authenticator"
3131
genericapiserver "k8s.io/apiserver/pkg/server"
32+
"k8s.io/apiserver/pkg/server/egressselector"
3233
genericoptions "k8s.io/apiserver/pkg/server/options"
3334
utilfeature "k8s.io/apiserver/pkg/util/feature"
35+
"k8s.io/client-go/informers"
36+
"k8s.io/client-go/kubernetes"
3437
cliflag "k8s.io/component-base/cli/flag"
38+
"k8s.io/klog"
39+
openapicommon "k8s.io/kube-openapi/pkg/common"
40+
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
3541
"k8s.io/kubernetes/pkg/features"
3642
kubeauthenticator "k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
3743
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
44+
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap"
3845
)
3946

4047
type BuiltInAuthenticationOptions struct {
@@ -406,35 +413,60 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() (kubeauthenticat
406413
return ret, nil
407414
}
408415

409-
func (o *BuiltInAuthenticationOptions) ApplyTo(c *genericapiserver.Config) error {
416+
// ApplyTo requires already applied OpenAPIConfig and EgressSelector if present.
417+
func (o *BuiltInAuthenticationOptions) ApplyTo(authInfo *genericapiserver.AuthenticationInfo, secureServing *genericapiserver.SecureServingInfo, egressSelector *egressselector.EgressSelector, openAPIConfig *openapicommon.Config, extclient kubernetes.Interface, versionedInformer informers.SharedInformerFactory) error {
410418
if o == nil {
411419
return nil
412420
}
413421

414-
if o.ClientCert != nil {
415-
clientCertificateCAContentProvider, err := o.ClientCert.GetClientCAContentProvider()
416-
if err != nil {
422+
if openAPIConfig == nil {
423+
return errors.New("uninitialized OpenAPIConfig")
424+
}
425+
426+
authenticatorConfig, err := o.ToAuthenticationConfig()
427+
if err != nil {
428+
return err
429+
}
430+
431+
if authenticatorConfig.ClientCAContentProvider != nil {
432+
if err = authInfo.ApplyClientCert(authenticatorConfig.ClientCAContentProvider, secureServing); err != nil {
417433
return fmt.Errorf("unable to load client CA file: %v", err)
418434
}
419-
if err = c.Authentication.ApplyClientCert(clientCertificateCAContentProvider, c.SecureServing); err != nil {
435+
}
436+
if authenticatorConfig.RequestHeaderConfig != nil && authenticatorConfig.RequestHeaderConfig.CAContentProvider != nil {
437+
if err = authInfo.ApplyClientCert(authenticatorConfig.RequestHeaderConfig.CAContentProvider, secureServing); err != nil {
420438
return fmt.Errorf("unable to load client CA file: %v", err)
421439
}
422440
}
423-
if o.RequestHeader != nil {
424-
requestHeaderConfig, err := o.RequestHeader.ToAuthenticationRequestHeaderConfig()
441+
442+
authInfo.APIAudiences = o.APIAudiences
443+
if o.ServiceAccounts != nil && o.ServiceAccounts.Issuer != "" && len(o.APIAudiences) == 0 {
444+
authInfo.APIAudiences = authenticator.Audiences{o.ServiceAccounts.Issuer}
445+
}
446+
447+
if o.ServiceAccounts.Lookup || utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) {
448+
authenticatorConfig.ServiceAccountTokenGetter = serviceaccountcontroller.NewGetterFromClient(
449+
extclient,
450+
versionedInformer.Core().V1().Secrets().Lister(),
451+
versionedInformer.Core().V1().ServiceAccounts().Lister(),
452+
versionedInformer.Core().V1().Pods().Lister(),
453+
)
454+
}
455+
authenticatorConfig.BootstrapTokenAuthenticator = bootstrap.NewTokenAuthenticator(
456+
versionedInformer.Core().V1().Secrets().Lister().Secrets(metav1.NamespaceSystem),
457+
)
458+
459+
if egressSelector != nil {
460+
egressDialer, err := egressSelector.Lookup(egressselector.Master.AsNetworkContext())
425461
if err != nil {
426-
return fmt.Errorf("unable to create request header authentication config: %v", err)
427-
}
428-
if requestHeaderConfig != nil {
429-
if err = c.Authentication.ApplyClientCert(requestHeaderConfig.CAContentProvider, c.SecureServing); err != nil {
430-
return fmt.Errorf("unable to load client CA file: %v", err)
431-
}
462+
return err
432463
}
464+
authenticatorConfig.CustomDial = egressDialer
433465
}
434466

435-
c.Authentication.APIAudiences = o.APIAudiences
436-
if o.ServiceAccounts != nil && o.ServiceAccounts.Issuer != "" && len(o.APIAudiences) == 0 {
437-
c.Authentication.APIAudiences = authenticator.Audiences{o.ServiceAccounts.Issuer}
467+
authInfo.Authenticator, openAPIConfig.SecurityDefinitions, err = authenticatorConfig.New()
468+
if err != nil {
469+
return err
438470
}
439471

440472
return nil

test/integration/apiserver/certreload/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ go_test(
99
tags = ["integration"],
1010
deps = [
1111
"//cmd/kube-apiserver/app/options:go_default_library",
12+
"//staging/src/k8s.io/api/authorization/v1:go_default_library",
1213
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
1314
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
1415
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
1516
"//staging/src/k8s.io/apiserver/pkg/server/dynamiccertificates:go_default_library",
1617
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
18+
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
19+
"//staging/src/k8s.io/client-go/rest:go_default_library",
1720
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
1821
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
1922
"//test/integration/framework:go_default_library",

0 commit comments

Comments
 (0)