provides DynamicRequestHeaderController that combines DynamicCAFromConfigMapController and RequestHeaderAuthRequestController into one controller
the unified controller will dynamically fill RequestHeaderConfig struct Kubernetes-commit: cb4b4cb5a6ffdf1c7f199e644a8b5cac2367d504
This commit is contained in:
		
							parent
							
								
									259bedd4a2
								
							
						
					
					
						commit
						07cdc792bb
					
				|  | @ -26,6 +26,7 @@ import ( | |||
| 	"k8s.io/apimachinery/pkg/api/equality" | ||||
| 	"k8s.io/apimachinery/pkg/api/errors" | ||||
| 	metav1 "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" | ||||
| 	coreinformers "k8s.io/client-go/informers/core/v1" | ||||
|  | @ -41,6 +42,16 @@ const ( | |||
| 	authenticationRoleName = "extension-apiserver-authentication-reader" | ||||
| ) | ||||
| 
 | ||||
| // RequestHeaderAuthRequestProvider a provider that knows how to dynamically fill parts of RequestHeaderConfig struct
 | ||||
| type RequestHeaderAuthRequestProvider interface { | ||||
| 	UsernameHeaders() []string | ||||
| 	GroupHeaders() []string | ||||
| 	ExtraHeaderPrefixes() []string | ||||
| 	AllowedClientNames() []string | ||||
| } | ||||
| 
 | ||||
| var _ RequestHeaderAuthRequestProvider = &RequestHeaderAuthRequestController{} | ||||
| 
 | ||||
| type requestHeaderBundle struct { | ||||
| 	UsernameHeaders     []string | ||||
| 	GroupHeaders        []string | ||||
|  | @ -48,7 +59,7 @@ type requestHeaderBundle struct { | |||
| 	AllowedClientNames  []string | ||||
| } | ||||
| 
 | ||||
| // RequestHeaderAuthRequestController a controller that exposes a set of methods for dynamically filling RequestHeaderConfig struct.
 | ||||
| // RequestHeaderAuthRequestController a controller that exposes a set of methods for dynamically filling parts of RequestHeaderConfig struct.
 | ||||
| // The methods are sourced from the config map which is being monitored by this controller.
 | ||||
| // The controller is primed from the server at the construction time for components that don't want to dynamically react to changes
 | ||||
| // in the config map.
 | ||||
|  | @ -59,6 +70,7 @@ type RequestHeaderAuthRequestController struct { | |||
| 	configmapNamespace string | ||||
| 
 | ||||
| 	configmapLister         corev1listers.ConfigMapNamespaceLister | ||||
| 	configmapInformer       cache.SharedIndexInformer | ||||
| 	configmapInformerSynced cache.InformerSynced | ||||
| 
 | ||||
| 	queue workqueue.RateLimitingInterface | ||||
|  | @ -77,7 +89,6 @@ func NewRequestHeaderAuthRequestController( | |||
| 	cmName string, | ||||
| 	cmNamespace string, | ||||
| 	client kubernetes.Interface, | ||||
| 	cmInformer coreinformers.ConfigMapInformer, | ||||
| 	usernameHeadersKey, groupHeadersKey, extraHeaderPrefixesKey, allowedClientNamesKey string) (*RequestHeaderAuthRequestController, error) { | ||||
| 	c := &RequestHeaderAuthRequestController{ | ||||
| 		name: "RequestHeaderAuthRequestController", | ||||
|  | @ -98,7 +109,12 @@ func NewRequestHeaderAuthRequestController( | |||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	cmInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ | ||||
| 	// we construct our own informer because we need such a small subset of the information available.  Just one namespace.
 | ||||
| 	c.configmapInformer = coreinformers.NewFilteredConfigMapInformer(client, c.configmapNamespace, 12*time.Hour, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, func(listOptions *metav1.ListOptions) { | ||||
| 		listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", c.configmapName).String() | ||||
| 	}) | ||||
| 
 | ||||
| 	c.configmapInformer.AddEventHandler(cache.FilteringResourceEventHandler{ | ||||
| 		FilterFunc: func(obj interface{}) bool { | ||||
| 			if cast, ok := obj.(*corev1.ConfigMap); ok { | ||||
| 				return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace | ||||
|  | @ -125,8 +141,8 @@ func NewRequestHeaderAuthRequestController( | |||
| 		}, | ||||
| 	}) | ||||
| 
 | ||||
| 	c.configmapLister = cmInformer.Lister().ConfigMaps(c.configmapNamespace) | ||||
| 	c.configmapInformerSynced = cmInformer.Informer().HasSynced | ||||
| 	c.configmapLister = corev1listers.NewConfigMapLister(c.configmapInformer.GetIndexer()).ConfigMaps(c.configmapNamespace) | ||||
| 	c.configmapInformerSynced = c.configmapInformer.HasSynced | ||||
| 
 | ||||
| 	return c, nil | ||||
| } | ||||
|  | @ -155,6 +171,8 @@ func (c *RequestHeaderAuthRequestController) Run(workers int, stopCh <-chan stru | |||
| 	klog.Infof("Starting %s", c.name) | ||||
| 	defer klog.Infof("Shutting down %s", c.name) | ||||
| 
 | ||||
| 	go c.configmapInformer.Run(stopCh) | ||||
| 
 | ||||
| 	// wait for caches to fill before starting your work
 | ||||
| 	if !cache.WaitForNamedCacheSync(c.name, stopCh, c.configmapInformerSynced) { | ||||
| 		return | ||||
|  | @ -224,6 +242,7 @@ func (c *RequestHeaderAuthRequestController) syncConfigMap(configMap *corev1.Con | |||
| 	} | ||||
| 	if hasChanged { | ||||
| 		c.exportedRequestHeaderBundle.Store(newRequestHeaderBundle) | ||||
| 		klog.V(2).Infof("Loaded a new request header values for %v", c.name) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
|  | @ -17,8 +17,6 @@ limitations under the License. | |||
| package options | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | @ -27,7 +25,6 @@ import ( | |||
| 
 | ||||
| 	"github.com/spf13/pflag" | ||||
| 
 | ||||
| 	"k8s.io/apimachinery/pkg/api/errors" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apiserver/pkg/authentication/authenticatorfactory" | ||||
| 	"k8s.io/apiserver/pkg/authentication/request/headerrequest" | ||||
|  | @ -330,66 +327,23 @@ const ( | |||
| 	// by --requestheader-username-headers. This is created in the cluster by the kube-apiserver.
 | ||||
| 	// "WARNING: generally do not depend on authorization being already done for incoming requests.")
 | ||||
| 	authenticationConfigMapName = "extension-apiserver-authentication" | ||||
| 	authenticationRoleName      = "extension-apiserver-authentication-reader" | ||||
| ) | ||||
| 
 | ||||
| func (s *DelegatingAuthenticationOptions) createRequestHeaderConfig(client kubernetes.Interface) (*authenticatorfactory.RequestHeaderConfig, error) { | ||||
| 	requestHeaderCAProvider, err := dynamiccertificates.NewDynamicCAFromConfigMapController("client-ca", authenticationConfigMapNamespace, authenticationConfigMapName, "requestheader-client-ca-file", client) | ||||
| 	dynamicRequestHeaderProvider, err := newDynamicRequestHeaderController(client) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to create request header authentication config: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	authConfigMap, err := client.CoreV1().ConfigMaps(authenticationConfigMapNamespace).Get(context.TODO(), 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 nil, err | ||||
| 	case err != nil: | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	usernameHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-username-headers"]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	groupHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-group-headers"]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	extraHeaderPrefixes, err := deserializeStrings(authConfigMap.Data["requestheader-extra-headers-prefix"]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	allowedNames, err := deserializeStrings(authConfigMap.Data["requestheader-allowed-names"]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &authenticatorfactory.RequestHeaderConfig{ | ||||
| 		CAContentProvider:   requestHeaderCAProvider, | ||||
| 		UsernameHeaders:     headerrequest.StaticStringSlice(usernameHeaders), | ||||
| 		GroupHeaders:        headerrequest.StaticStringSlice(groupHeaders), | ||||
| 		ExtraHeaderPrefixes: headerrequest.StaticStringSlice(extraHeaderPrefixes), | ||||
| 		AllowedClientNames:  headerrequest.StaticStringSlice(allowedNames), | ||||
| 		CAContentProvider:   dynamicRequestHeaderProvider, | ||||
| 		UsernameHeaders:     headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.UsernameHeaders)), | ||||
| 		GroupHeaders:        headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.GroupHeaders)), | ||||
| 		ExtraHeaderPrefixes: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.ExtraHeaderPrefixes)), | ||||
| 		AllowedClientNames:  headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.AllowedClientNames)), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func deserializeStrings(in string) ([]string, error) { | ||||
| 	if len(in) == 0 { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	var ret []string | ||||
| 	if err := json.Unmarshal([]byte(in), &ret); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return ret, nil | ||||
| } | ||||
| 
 | ||||
| // getClient returns a Kubernetes clientset. If s.RemoteKubeConfigFileOptional is true, nil will be returned
 | ||||
| // if no kubeconfig is specified by the user and the in-cluster config is not found.
 | ||||
| func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, error) { | ||||
|  |  | |||
|  | @ -0,0 +1,62 @@ | |||
| package options | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"k8s.io/apiserver/pkg/authentication/request/headerrequest" | ||||
| 	"k8s.io/apiserver/pkg/server/dynamiccertificates" | ||||
| 	"k8s.io/client-go/kubernetes" | ||||
| ) | ||||
| 
 | ||||
| var _ dynamiccertificates.ControllerRunner = &DynamicRequestHeaderController{} | ||||
| var _ dynamiccertificates.Notifier = &DynamicRequestHeaderController{} | ||||
| var _ dynamiccertificates.CAContentProvider = &DynamicRequestHeaderController{} | ||||
| 
 | ||||
| var _ headerrequest.RequestHeaderAuthRequestProvider = &DynamicRequestHeaderController{} | ||||
| 
 | ||||
| // DynamicRequestHeaderController combines DynamicCAFromConfigMapController and RequestHeaderAuthRequestController
 | ||||
| // into one controller for dynamically filling RequestHeaderConfig struct
 | ||||
| type DynamicRequestHeaderController struct { | ||||
| 	*dynamiccertificates.ConfigMapCAController | ||||
| 	*headerrequest.RequestHeaderAuthRequestController | ||||
| } | ||||
| 
 | ||||
| // newDynamicRequestHeaderController creates a new controller that implements DynamicRequestHeaderController
 | ||||
| func newDynamicRequestHeaderController(client kubernetes.Interface) (*DynamicRequestHeaderController, error) { | ||||
| 	requestHeaderCAController, err := dynamiccertificates.NewDynamicCAFromConfigMapController( | ||||
| 		"client-ca", | ||||
| 		authenticationConfigMapNamespace, | ||||
| 		authenticationConfigMapName, | ||||
| 		"requestheader-client-ca-file", | ||||
| 		client) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to create DynamicCAFromConfigMap controller: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	requestHeaderAuthRequestController, err := headerrequest.NewRequestHeaderAuthRequestController( | ||||
| 		authenticationConfigMapName, | ||||
| 		authenticationConfigMapNamespace, | ||||
| 		client, | ||||
| 		"requestheader-username-headers", | ||||
| 		"requestheader-group-headers", | ||||
| 		"requestheader-extra-headers-prefix", | ||||
| 		"requestheader-allowed-names", | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to create RequestHeaderAuthRequest controller: %v", err) | ||||
| 	} | ||||
| 	return &DynamicRequestHeaderController{ | ||||
| 		ConfigMapCAController:              requestHeaderCAController, | ||||
| 		RequestHeaderAuthRequestController: requestHeaderAuthRequestController, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *DynamicRequestHeaderController) RunOnce() error { | ||||
| 	return c.ConfigMapCAController.RunOnce() | ||||
| } | ||||
| 
 | ||||
| func (c *DynamicRequestHeaderController) Run(workers int, stopCh <-chan struct{}) { | ||||
| 	go c.ConfigMapCAController.Run(workers, stopCh) | ||||
| 	go c.RequestHeaderAuthRequestController.Run(workers, stopCh) | ||||
| 	<-stopCh | ||||
| } | ||||
		Loading…
	
		Reference in New Issue