mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 21:47:07 +00:00
provides DynamicRequestHeaderController that combines DynamicCAFromConfigMapController and RequestHeaderAuthRequestController into one controller
the unified controller will dynamically fill RequestHeaderConfig struct
This commit is contained in:
parent
6e0211b3d8
commit
cb4b4cb5a6
@ -26,6 +26,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
@ -41,6 +42,16 @@ const (
|
|||||||
authenticationRoleName = "extension-apiserver-authentication-reader"
|
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 {
|
type requestHeaderBundle struct {
|
||||||
UsernameHeaders []string
|
UsernameHeaders []string
|
||||||
GroupHeaders []string
|
GroupHeaders []string
|
||||||
@ -48,7 +59,7 @@ type requestHeaderBundle struct {
|
|||||||
AllowedClientNames []string
|
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 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
|
// 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.
|
// in the config map.
|
||||||
@ -59,6 +70,7 @@ type RequestHeaderAuthRequestController struct {
|
|||||||
configmapNamespace string
|
configmapNamespace string
|
||||||
|
|
||||||
configmapLister corev1listers.ConfigMapNamespaceLister
|
configmapLister corev1listers.ConfigMapNamespaceLister
|
||||||
|
configmapInformer cache.SharedIndexInformer
|
||||||
configmapInformerSynced cache.InformerSynced
|
configmapInformerSynced cache.InformerSynced
|
||||||
|
|
||||||
queue workqueue.RateLimitingInterface
|
queue workqueue.RateLimitingInterface
|
||||||
@ -77,7 +89,6 @@ func NewRequestHeaderAuthRequestController(
|
|||||||
cmName string,
|
cmName string,
|
||||||
cmNamespace string,
|
cmNamespace string,
|
||||||
client kubernetes.Interface,
|
client kubernetes.Interface,
|
||||||
cmInformer coreinformers.ConfigMapInformer,
|
|
||||||
usernameHeadersKey, groupHeadersKey, extraHeaderPrefixesKey, allowedClientNamesKey string) (*RequestHeaderAuthRequestController, error) {
|
usernameHeadersKey, groupHeadersKey, extraHeaderPrefixesKey, allowedClientNamesKey string) (*RequestHeaderAuthRequestController, error) {
|
||||||
c := &RequestHeaderAuthRequestController{
|
c := &RequestHeaderAuthRequestController{
|
||||||
name: "RequestHeaderAuthRequestController",
|
name: "RequestHeaderAuthRequestController",
|
||||||
@ -98,7 +109,12 @@ func NewRequestHeaderAuthRequestController(
|
|||||||
return nil, err
|
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 {
|
FilterFunc: func(obj interface{}) bool {
|
||||||
if cast, ok := obj.(*corev1.ConfigMap); ok {
|
if cast, ok := obj.(*corev1.ConfigMap); ok {
|
||||||
return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace
|
return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace
|
||||||
@ -125,8 +141,8 @@ func NewRequestHeaderAuthRequestController(
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
c.configmapLister = cmInformer.Lister().ConfigMaps(c.configmapNamespace)
|
c.configmapLister = corev1listers.NewConfigMapLister(c.configmapInformer.GetIndexer()).ConfigMaps(c.configmapNamespace)
|
||||||
c.configmapInformerSynced = cmInformer.Informer().HasSynced
|
c.configmapInformerSynced = c.configmapInformer.HasSynced
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
@ -155,6 +171,8 @@ func (c *RequestHeaderAuthRequestController) Run(workers int, stopCh <-chan stru
|
|||||||
klog.Infof("Starting %s", c.name)
|
klog.Infof("Starting %s", c.name)
|
||||||
defer klog.Infof("Shutting down %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
|
// wait for caches to fill before starting your work
|
||||||
if !cache.WaitForNamedCacheSync(c.name, stopCh, c.configmapInformerSynced) {
|
if !cache.WaitForNamedCacheSync(c.name, stopCh, c.configmapInformerSynced) {
|
||||||
return
|
return
|
||||||
@ -224,6 +242,7 @@ func (c *RequestHeaderAuthRequestController) syncConfigMap(configMap *corev1.Con
|
|||||||
}
|
}
|
||||||
if hasChanged {
|
if hasChanged {
|
||||||
c.exportedRequestHeaderBundle.Store(newRequestHeaderBundle)
|
c.exportedRequestHeaderBundle.Store(newRequestHeaderBundle)
|
||||||
|
klog.V(2).Infof("Loaded a new request header values for %v", c.name)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@ limitations under the License.
|
|||||||
package options
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -27,7 +25,6 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
||||||
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
"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.
|
// 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.")
|
// "WARNING: generally do not depend on authorization being already done for incoming requests.")
|
||||||
authenticationConfigMapName = "extension-apiserver-authentication"
|
authenticationConfigMapName = "extension-apiserver-authentication"
|
||||||
authenticationRoleName = "extension-apiserver-authentication-reader"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *DelegatingAuthenticationOptions) createRequestHeaderConfig(client kubernetes.Interface) (*authenticatorfactory.RequestHeaderConfig, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to create request header authentication config: %v", err)
|
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{
|
return &authenticatorfactory.RequestHeaderConfig{
|
||||||
CAContentProvider: requestHeaderCAProvider,
|
CAContentProvider: dynamicRequestHeaderProvider,
|
||||||
UsernameHeaders: headerrequest.StaticStringSlice(usernameHeaders),
|
UsernameHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.UsernameHeaders)),
|
||||||
GroupHeaders: headerrequest.StaticStringSlice(groupHeaders),
|
GroupHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.GroupHeaders)),
|
||||||
ExtraHeaderPrefixes: headerrequest.StaticStringSlice(extraHeaderPrefixes),
|
ExtraHeaderPrefixes: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.ExtraHeaderPrefixes)),
|
||||||
AllowedClientNames: headerrequest.StaticStringSlice(allowedNames),
|
AllowedClientNames: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.AllowedClientNames)),
|
||||||
}, nil
|
}, 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
|
// 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.
|
// if no kubeconfig is specified by the user and the in-cluster config is not found.
|
||||||
func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, error) {
|
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
Block a user