Merge pull request #85004 from deads2k/dynamic-agg-cert

dynamic reload cluster authentication info for aggregated API servers

Kubernetes-commit: 02af1dd62c4842e20e2ee7337edf032327b1c8ed
This commit is contained in:
Kubernetes Publisher 2019-11-13 14:50:54 -08:00
commit acb34b1bc7
9 changed files with 364 additions and 136 deletions

2
Godeps/Godeps.json generated
View File

@ -584,7 +584,7 @@
},
{
"ImportPath": "k8s.io/client-go",
"Rev": "74d7a2e0ebca"
"Rev": "1f4f5fa64a6c"
},
{
"ImportPath": "k8s.io/component-base",

4
go.mod
View File

@ -44,7 +44,7 @@ require (
gotest.tools v2.2.0+incompatible // indirect
k8s.io/api v0.0.0-20191114100036-40f4bbc2b486
k8s.io/apimachinery v0.0.0-20191114095528-3db02fd2eea7
k8s.io/client-go v0.0.0-20191114100700-74d7a2e0ebca
k8s.io/client-go v0.0.0-20191114100703-1f4f5fa64a6c
k8s.io/component-base v0.0.0-20191114102135-42a5d5b2565c
k8s.io/klog v1.0.0
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
@ -58,6 +58,6 @@ replace (
golang.org/x/tools => golang.org/x/tools v0.0.0-20190821162956-65e3620a7ae7
k8s.io/api => k8s.io/api v0.0.0-20191114100036-40f4bbc2b486
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191114095528-3db02fd2eea7
k8s.io/client-go => k8s.io/client-go v0.0.0-20191114100700-74d7a2e0ebca
k8s.io/client-go => k8s.io/client-go v0.0.0-20191114100703-1f4f5fa64a6c
k8s.io/component-base => k8s.io/component-base v0.0.0-20191114102135-42a5d5b2565c
)

2
go.sum
View File

@ -348,7 +348,7 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20191114100036-40f4bbc2b486/go.mod h1:IM5ceavki8HjRhUlaRYP3oGw0J/hbXKiKiSqS5AR728=
k8s.io/apimachinery v0.0.0-20191114095528-3db02fd2eea7/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s=
k8s.io/client-go v0.0.0-20191114100700-74d7a2e0ebca/go.mod h1:NWDcvX+oAkmke5fjHiErMvbgGGoaHcZcxGNYutZ3MtQ=
k8s.io/client-go v0.0.0-20191114100703-1f4f5fa64a6c/go.mod h1:SI++Xl/YwtfdRxOuhN04ry6Hl5PyasXDGmBBZnzofBo=
k8s.io/component-base v0.0.0-20191114102135-42a5d5b2565c/go.mod h1:rwIfg3coOPWGYSmJnTp7yw1QVOB/ncA32pwgawNSR2Q=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=

View File

@ -0,0 +1,277 @@
/*
Copyright 2019 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 dynamiccertificates
import (
"bytes"
"crypto/x509"
"fmt"
"sync/atomic"
"time"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
corev1informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog"
)
// ConfigMapCAController provies a CAContentProvider that can dynamically react to configmap changes
// It also fulfills the authenticator interface to provide verifyoptions
type ConfigMapCAController struct {
name string
configmapLister corev1listers.ConfigMapLister
configmapNamespace string
configmapName string
configmapKey string
// configMapInformer is tracked so that we can start these on Run
configMapInformer cache.SharedIndexInformer
// caBundle is a caBundleAndVerifier that contains the last read, non-zero length content of the file
caBundle atomic.Value
listeners []Listener
queue workqueue.RateLimitingInterface
// preRunCaches are the caches to sync before starting the work of this control loop
preRunCaches []cache.InformerSynced
}
var _ Notifier = &ConfigMapCAController{}
var _ CAContentProvider = &ConfigMapCAController{}
var _ ControllerRunner = &ConfigMapCAController{}
// NewDynamicCAFromConfigMapController returns a CAContentProvider based on a configmap that automatically reloads content.
// It is near-realtime via an informer.
func NewDynamicCAFromConfigMapController(purpose, namespace, name, key string, kubeClient kubernetes.Interface) (*ConfigMapCAController, error) {
if len(purpose) == 0 {
return nil, fmt.Errorf("missing purpose for ca bundle")
}
if len(namespace) == 0 {
return nil, fmt.Errorf("missing namespace for ca bundle")
}
if len(name) == 0 {
return nil, fmt.Errorf("missing name for ca bundle")
}
if len(key) == 0 {
return nil, fmt.Errorf("missing key for ca bundle")
}
caContentName := fmt.Sprintf("%s::%s::%s::%s", purpose, namespace, name, key)
// we construct our own informer because we need such a small subset of the information available. Just one namespace.
uncastConfigmapInformer := corev1informers.NewFilteredConfigMapInformer(kubeClient, namespace, 12*time.Hour, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, func(listOptions *v1.ListOptions) {
listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", name).String()
})
configmapLister := corev1listers.NewConfigMapLister(uncastConfigmapInformer.GetIndexer())
c := &ConfigMapCAController{
name: caContentName,
configmapNamespace: namespace,
configmapName: name,
configmapKey: key,
configmapLister: configmapLister,
configMapInformer: uncastConfigmapInformer,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), fmt.Sprintf("DynamicConfigMapCABundle-%s", purpose)),
preRunCaches: []cache.InformerSynced{uncastConfigmapInformer.HasSynced},
}
if err := c.loadCABundle(); err != nil {
// don't fail, but do print out a message
klog.Warningf("unable to load initial CA bundle for: %q due to: %s", c.name, err)
}
uncastConfigmapInformer.AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
if cast, ok := obj.(*corev1.ConfigMap); ok {
return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace
}
if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok {
if cast, ok := tombstone.Obj.(*corev1.ConfigMap); ok {
return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace
}
}
return true // always return true just in case. The checks are fairly cheap
},
Handler: cache.ResourceEventHandlerFuncs{
// we have a filter, so any time we're called, we may as well queue. We only ever check one configmap
// so we don't have to be choosy about our key.
AddFunc: func(obj interface{}) {
c.queue.Add(c.keyFn())
},
UpdateFunc: func(oldObj, newObj interface{}) {
c.queue.Add(c.keyFn())
},
DeleteFunc: func(obj interface{}) {
c.queue.Add(c.keyFn())
},
},
})
return c, nil
}
func (c *ConfigMapCAController) keyFn() string {
// this format matches DeletionHandlingMetaNamespaceKeyFunc for our single key
return c.configmapNamespace + "/" + c.configmapName
}
// AddListener adds a listener to be notified when the CA content changes.
func (c *ConfigMapCAController) AddListener(listener Listener) {
c.listeners = append(c.listeners, listener)
}
// loadCABundle determines the next set of content for the file.
func (c *ConfigMapCAController) loadCABundle() error {
configMap, err := c.configmapLister.ConfigMaps(c.configmapNamespace).Get(c.configmapName)
if err != nil {
return err
}
caBundle := configMap.Data[c.configmapKey]
if len(caBundle) == 0 {
return fmt.Errorf("missing content for CA bundle %q", c.Name())
}
// check to see if we have a change. If the values are the same, do nothing.
if !c.hasCAChanged([]byte(caBundle)) {
return nil
}
caBundleAndVerifier, err := newCABundleAndVerifier(c.Name(), []byte(caBundle))
if err != nil {
return err
}
c.caBundle.Store(caBundleAndVerifier)
for _, listener := range c.listeners {
listener.Enqueue()
}
return nil
}
// hasCAChanged returns true if the caBundle is different than the current.
func (c *ConfigMapCAController) hasCAChanged(caBundle []byte) bool {
uncastExisting := c.caBundle.Load()
if uncastExisting == nil {
return true
}
// check to see if we have a change. If the values are the same, do nothing.
existing, ok := uncastExisting.(*caBundleAndVerifier)
if !ok {
return true
}
if !bytes.Equal(existing.caBundle, caBundle) {
return true
}
return false
}
// RunOnce runs a single sync loop
func (c *ConfigMapCAController) RunOnce() error {
// Ignore the error when running once because when using a dynamically loaded ca file, because we think it's better to have nothing for
// a brief time than completely crash. If crashing is necessary, higher order logic like a healthcheck and cause failures.
_ = c.loadCABundle()
return nil
}
// Run starts the kube-apiserver and blocks until stopCh is closed.
func (c *ConfigMapCAController) Run(workers int, stopCh <-chan struct{}) {
defer utilruntime.HandleCrash()
defer c.queue.ShutDown()
klog.Infof("Starting %s", c.name)
defer klog.Infof("Shutting down %s", c.name)
// we have a personal informer that is narrowly scoped, start it.
go c.configMapInformer.Run(stopCh)
// wait for your secondary caches to fill before starting your work
if !cache.WaitForNamedCacheSync(c.name, stopCh, c.preRunCaches...) {
return
}
// doesn't matter what workers say, only start one.
go wait.Until(c.runWorker, time.Second, stopCh)
// start timer that rechecks every minute, just in case. this also serves to prime the controller quickly.
_ = wait.PollImmediateUntil(FileRefreshDuration, func() (bool, error) {
c.queue.Add(workItemKey)
return false, nil
}, stopCh)
<-stopCh
}
func (c *ConfigMapCAController) runWorker() {
for c.processNextWorkItem() {
}
}
func (c *ConfigMapCAController) processNextWorkItem() bool {
dsKey, quit := c.queue.Get()
if quit {
return false
}
defer c.queue.Done(dsKey)
err := c.loadCABundle()
if err == nil {
c.queue.Forget(dsKey)
return true
}
utilruntime.HandleError(fmt.Errorf("%v failed with : %v", dsKey, err))
c.queue.AddRateLimited(dsKey)
return true
}
// Name is just an identifier
func (c *ConfigMapCAController) Name() string {
return c.name
}
// CurrentCABundleContent provides ca bundle byte content
func (c *ConfigMapCAController) CurrentCABundleContent() []byte {
uncastObj := c.caBundle.Load()
if uncastObj == nil {
return nil // this can happen if we've been unable load data from the apiserver for some reason
}
return c.caBundle.Load().(*caBundleAndVerifier).caBundle
}
// VerifyOptions provides verifyoptions compatible with authenticators
func (c *ConfigMapCAController) VerifyOptions() (x509.VerifyOptions, bool) {
uncastObj := c.caBundle.Load()
if uncastObj == nil {
// This can happen if we've been unable load data from the apiserver for some reason.
// In this case, we should not accept any connections on the basis of this ca bundle.
return x509.VerifyOptions{}, false
}
return uncastObj.(*caBundleAndVerifier).verifyOptions, true
}

View File

@ -103,10 +103,9 @@ func (c *DynamicServingCertificateController) newTLSContent() (*dynamicCertifica
if c.clientCA != nil {
currClientCABundle := c.clientCA.CurrentCABundleContent()
// don't remove all content. The value was configured at one time, so continue using that.
if len(currClientCABundle) == 0 {
return nil, fmt.Errorf("not loading an empty client ca bundle from %q", c.clientCA.Name())
}
// we allow removing all client ca bundles because the server is still secure when this happens. it just means
// that there isn't a hint to clients about which client-cert to used. this happens when there is no client-ca
// yet known for authentication, which can happen in aggregated apiservers and some kube-apiserver deployment modes.
newContent.clientCA = caBundleContent{caBundle: currClientCABundle}
}
@ -152,7 +151,7 @@ func (c *DynamicServingCertificateController) syncCerts() error {
newClientCAPool := x509.NewCertPool()
newClientCAs, err := cert.ParseCertsPEM(newContent.clientCA.caBundle)
if err != nil {
return fmt.Errorf("unable to load client CA file: %v", err)
return fmt.Errorf("unable to load client CA file %q: %v", string(newContent.clientCA.caBundle), err)
}
for i, cert := range newClientCAs {
klog.V(2).Infof("loaded client CA [%d/%q]: %s", i, c.clientCA.Name(), GetHumanCertDetail(cert))

View File

@ -99,12 +99,6 @@ func TestNewStaticCertKeyContent(t *testing.T) {
sniCerts: []sniCertKeyContent{{certKeyContent: certKeyContent{cert: serverCert, key: serverKey}, sniNames: []string{"foo"}}},
},
},
{
name: "missingCA",
clientCA: &staticCAContent{name: "test-ca", caBundle: &caBundleAndVerifier{caBundle: []byte("")}},
expected: nil,
expectedErr: `not loading an empty client ca bundle from "test-ca"`,
},
{
name: "nil",
expected: &dynamicCertificateContent{clientCA: caBundleContent{}, servingCert: certKeyContent{}},

View File

@ -48,7 +48,9 @@ func (c unionCAContent) Name() string {
func (c unionCAContent) CurrentCABundleContent() []byte {
caBundles := [][]byte{}
for _, curr := range c {
caBundles = append(caBundles, curr.CurrentCABundleContent())
if currCABytes := curr.CurrentCABundleContent(); len(currCABytes) > 0 {
caBundles = append(caBundles, []byte(strings.TrimSpace(string(currCABytes))))
}
}
return bytes.Join(caBundles, []byte("\n"))

View File

@ -19,7 +19,6 @@ package options
import (
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"time"
@ -27,7 +26,6 @@ import (
"github.com/spf13/pflag"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
@ -251,38 +249,63 @@ func (s *DelegatingAuthenticationOptions) ApplyTo(authenticationInfo *server.Aut
cfg.TokenAccessReviewClient = client.AuthenticationV1().TokenReviews()
}
// look into configmaps/external-apiserver-authentication for missing authn info
if !s.SkipInClusterLookup {
err := s.lookupMissingConfigInCluster(client)
// get the clientCA information
clientCAFileSpecified := len(s.ClientCert.ClientCA) > 0
var clientCAProvider dynamiccertificates.CAContentProvider
if clientCAFileSpecified {
clientCAProvider, err = s.ClientCert.GetClientCAContentProvider()
if err != nil {
if s.TolerateInClusterLookupFailure {
klog.Warningf("Error looking up in-cluster authentication configuration: %v", err)
klog.Warningf("Continuing without authentication configuration. This may treat all requests as anonymous.")
klog.Warningf("To require authentication configuration lookup to succeed, set --authentication-tolerate-lookup-failure=false")
} else {
return err
return fmt.Errorf("unable to load client CA file %q: %v", s.ClientCert.ClientCA, err)
}
cfg.ClientCertificateCAContentProvider = clientCAProvider
if err = authenticationInfo.ApplyClientCert(cfg.ClientCertificateCAContentProvider, servingInfo); err != nil {
return fmt.Errorf("unable to assign client CA file: %v", err)
}
} else if !s.SkipInClusterLookup {
if client == nil {
klog.Warningf("No authentication-kubeconfig provided in order to lookup client-ca-file in configmap/%s in %s, so client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
} else {
clientCAProvider, err = dynamiccertificates.NewDynamicCAFromConfigMapController("client-ca", authenticationConfigMapNamespace, authenticationConfigMapName, "client-ca-file", client)
if err != nil {
return fmt.Errorf("unable to load configmap based client CA file: %v", err)
}
cfg.ClientCertificateCAContentProvider = clientCAProvider
if err = authenticationInfo.ApplyClientCert(cfg.ClientCertificateCAContentProvider, servingInfo); err != nil {
return fmt.Errorf("unable to assign configmap based client CA file: %v", err)
}
}
}
requestHeaderCAFileSpecified := len(s.RequestHeader.ClientCAFile) > 0
var requestHeaderConfig *authenticatorfactory.RequestHeaderConfig
if requestHeaderCAFileSpecified {
requestHeaderConfig, err = s.RequestHeader.ToAuthenticationRequestHeaderConfig()
if err != nil {
return fmt.Errorf("unable to create request header authentication config: %v", err)
}
} else if !s.SkipInClusterLookup {
if client == nil {
klog.Warningf("No authentication-kubeconfig provided in order to lookup requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
} else {
requestHeaderConfig, err = s.createRequestHeaderConfig(client)
if err != nil {
if s.TolerateInClusterLookupFailure {
klog.Warningf("Error looking up in-cluster authentication configuration: %v", err)
klog.Warningf("Continuing without authentication configuration. This may treat all requests as anonymous.")
klog.Warningf("To require authentication configuration lookup to succeed, set --authentication-tolerate-lookup-failure=false")
} else {
return fmt.Errorf("unable to load configmap based request-header-client-ca-file: %v", err)
}
}
}
}
// configure AuthenticationInfo config
cfg.ClientCertificateCAContentProvider, err = s.ClientCert.GetClientCAContentProvider()
if err != nil {
return fmt.Errorf("unable to load client CA file: %v", err)
}
if cfg.ClientCertificateCAContentProvider != nil {
if err = authenticationInfo.ApplyClientCert(cfg.ClientCertificateCAContentProvider, servingInfo); err != nil {
return fmt.Errorf("unable to load client CA file: %v", err)
}
}
cfg.RequestHeaderConfig, err = s.RequestHeader.ToAuthenticationRequestHeaderConfig()
if err != nil {
return fmt.Errorf("unable to create request header authentication config: %v", err)
}
if cfg.RequestHeaderConfig != nil {
if requestHeaderConfig != nil {
cfg.RequestHeaderConfig = requestHeaderConfig
if err = authenticationInfo.ApplyClientCert(cfg.RequestHeaderConfig.CAContentProvider, servingInfo); err != nil {
return fmt.Errorf("unable to load client CA file: %v", err)
return fmt.Errorf("unable to load request-header-client-ca-file: %v", err)
}
}
@ -310,97 +333,26 @@ const (
authenticationRoleName = "extension-apiserver-authentication-reader"
)
func (s *DelegatingAuthenticationOptions) lookupMissingConfigInCluster(client kubernetes.Interface) error {
if len(s.ClientCert.ClientCA) > 0 && len(s.RequestHeader.ClientCAFile) > 0 {
return nil
}
if client == nil {
if len(s.ClientCert.ClientCA) == 0 {
klog.Warningf("No authentication-kubeconfig provided in order to lookup client-ca-file in configmap/%s in %s, so client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
}
if len(s.RequestHeader.ClientCAFile) == 0 {
klog.Warningf("No authentication-kubeconfig provided in order to lookup requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
}
return nil
func (s *DelegatingAuthenticationOptions) createRequestHeaderConfig(client kubernetes.Interface) (*authenticatorfactory.RequestHeaderConfig, error) {
requestHeaderCAProvider, err := dynamiccertificates.NewDynamicCAFromConfigMapController("client-ca", authenticationConfigMapNamespace, authenticationConfigMapName, "requestheader-client-ca-file", client)
if err != nil {
return nil, fmt.Errorf("unable to create request header authentication config: %v", err)
}
authConfigMap, err := client.CoreV1().ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
switch {
case errors.IsNotFound(err):
// ignore, authConfigMap is nil now
return nil, nil
case errors.IsForbidden(err):
klog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+
"'kubectl create rolebinding -n %s ROLEBINDING_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName)
return err
return nil, err
case err != nil:
return err
}
if len(s.ClientCert.ClientCA) == 0 {
if authConfigMap != nil {
opt, err := inClusterClientCA(authConfigMap)
if err != nil {
return err
}
if opt != nil {
s.ClientCert = *opt
}
}
if len(s.ClientCert.ClientCA) == 0 {
klog.Warningf("Cluster doesn't provide client-ca-file in configmap/%s in %s, so client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
}
}
if len(s.RequestHeader.ClientCAFile) == 0 {
if authConfigMap != nil {
opt, err := inClusterRequestHeader(authConfigMap)
if err != nil {
return err
}
if opt != nil {
s.RequestHeader = *opt
}
}
if len(s.RequestHeader.ClientCAFile) == 0 {
klog.Warningf("Cluster doesn't provide requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
}
}
return nil
}
func inClusterClientCA(authConfigMap *v1.ConfigMap) (*ClientCertAuthenticationOptions, error) {
clientCA, ok := authConfigMap.Data["client-ca-file"]
if !ok {
// not having a client-ca is fine, return nil
return nil, nil
}
clientCAProvider, err := dynamiccertificates.NewStaticCAContent("client-ca-file", []byte(clientCA))
if err != nil {
return nil, err
}
return &ClientCertAuthenticationOptions{
ClientCA: "",
CAContentProvider: clientCAProvider,
}, nil
}
func inClusterRequestHeader(authConfigMap *v1.ConfigMap) (*RequestHeaderAuthenticationOptions, error) {
requestHeaderCA, ok := authConfigMap.Data["requestheader-client-ca-file"]
if !ok {
// not having a requestheader-client-ca is fine, return nil
return nil, nil
}
f, err := ioutil.TempFile("", "requestheader-client-ca-file")
if err != nil {
return nil, err
}
if err := ioutil.WriteFile(f.Name(), []byte(requestHeaderCA), 0600); err != nil {
return nil, err
}
usernameHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-username-headers"])
if err != nil {
return nil, err
@ -418,12 +370,12 @@ func inClusterRequestHeader(authConfigMap *v1.ConfigMap) (*RequestHeaderAuthenti
return nil, err
}
return &RequestHeaderAuthenticationOptions{
UsernameHeaders: usernameHeaders,
GroupHeaders: groupHeaders,
ExtraHeaderPrefixes: extraHeaderPrefixes,
ClientCAFile: f.Name(),
AllowedNames: allowedNames,
return &authenticatorfactory.RequestHeaderConfig{
CAContentProvider: requestHeaderCAProvider,
UsernameHeaders: headerrequest.StaticStringSlice(usernameHeaders),
GroupHeaders: headerrequest.StaticStringSlice(groupHeaders),
ExtraHeaderPrefixes: headerrequest.StaticStringSlice(extraHeaderPrefixes),
AllowedClientNames: headerrequest.StaticStringSlice(allowedNames),
}, nil
}

View File

@ -81,17 +81,19 @@ func (s *SecureServingInfo) tlsConfig(stopCh <-chan struct{}) (*tls.Config, erro
}
// start controllers if possible
if controller, ok := s.ClientCA.(dynamiccertificates.ControllerRunner); ok {
// runonce to be sure that we have a value.
// runonce to try to prime data. If this fails, it's ok because we fail closed.
// Files are required to be populated already, so this is for convenience.
if err := controller.RunOnce(); err != nil {
return nil, err
klog.Warningf("Initial population of client CA failed: %v", err)
}
go controller.Run(1, stopCh)
}
if controller, ok := s.Cert.(dynamiccertificates.ControllerRunner); ok {
// runonce to be sure that we have a value.
// runonce to try to prime data. If this fails, it's ok because we fail closed.
// Files are required to be populated already, so this is for convenience.
if err := controller.RunOnce(); err != nil {
return nil, err
klog.Warningf("Initial population of default serving certificate failed: %v", err)
}
go controller.Run(1, stopCh)
@ -102,18 +104,20 @@ func (s *SecureServingInfo) tlsConfig(stopCh <-chan struct{}) (*tls.Config, erro
}
if controller, ok := sniCert.(dynamiccertificates.ControllerRunner); ok {
// runonce to be sure that we have a value.
// runonce to try to prime data. If this fails, it's ok because we fail closed.
// Files are required to be populated already, so this is for convenience.
if err := controller.RunOnce(); err != nil {
return nil, err
klog.Warningf("Initial population of SNI serving certificate failed: %v", err)
}
go controller.Run(1, stopCh)
}
}
// runonce to be sure that we have a value.
// runonce to try to prime data. If this fails, it's ok because we fail closed.
// Files are required to be populated already, so this is for convenience.
if err := dynamicCertificateController.RunOnce(); err != nil {
return nil, err
klog.Warningf("Initial population of dynamic certificates failed: %v", err)
}
go dynamicCertificateController.Run(1, stopCh)