Skip to content

Commit cb4b4cb

Browse files
committed
provides DynamicRequestHeaderController that combines DynamicCAFromConfigMapController and RequestHeaderAuthRequestController into one controller
the unified controller will dynamically fill RequestHeaderConfig struct
1 parent 6e0211b commit cb4b4cb

File tree

3 files changed

+92
-57
lines changed

3 files changed

+92
-57
lines changed

staging/src/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader_controller.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"k8s.io/apimachinery/pkg/api/equality"
2727
"k8s.io/apimachinery/pkg/api/errors"
2828
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/apimachinery/pkg/fields"
2930
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3031
"k8s.io/apimachinery/pkg/util/wait"
3132
coreinformers "k8s.io/client-go/informers/core/v1"
@@ -41,14 +42,24 @@ const (
4142
authenticationRoleName = "extension-apiserver-authentication-reader"
4243
)
4344

45+
// RequestHeaderAuthRequestProvider a provider that knows how to dynamically fill parts of RequestHeaderConfig struct
46+
type RequestHeaderAuthRequestProvider interface {
47+
UsernameHeaders() []string
48+
GroupHeaders() []string
49+
ExtraHeaderPrefixes() []string
50+
AllowedClientNames() []string
51+
}
52+
53+
var _ RequestHeaderAuthRequestProvider = &RequestHeaderAuthRequestController{}
54+
4455
type requestHeaderBundle struct {
4556
UsernameHeaders []string
4657
GroupHeaders []string
4758
ExtraHeaderPrefixes []string
4859
AllowedClientNames []string
4960
}
5061

51-
// RequestHeaderAuthRequestController a controller that exposes a set of methods for dynamically filling RequestHeaderConfig struct.
62+
// RequestHeaderAuthRequestController a controller that exposes a set of methods for dynamically filling parts of RequestHeaderConfig struct.
5263
// The methods are sourced from the config map which is being monitored by this controller.
5364
// The controller is primed from the server at the construction time for components that don't want to dynamically react to changes
5465
// in the config map.
@@ -59,6 +70,7 @@ type RequestHeaderAuthRequestController struct {
5970
configmapNamespace string
6071

6172
configmapLister corev1listers.ConfigMapNamespaceLister
73+
configmapInformer cache.SharedIndexInformer
6274
configmapInformerSynced cache.InformerSynced
6375

6476
queue workqueue.RateLimitingInterface
@@ -77,7 +89,6 @@ func NewRequestHeaderAuthRequestController(
7789
cmName string,
7890
cmNamespace string,
7991
client kubernetes.Interface,
80-
cmInformer coreinformers.ConfigMapInformer,
8192
usernameHeadersKey, groupHeadersKey, extraHeaderPrefixesKey, allowedClientNamesKey string) (*RequestHeaderAuthRequestController, error) {
8293
c := &RequestHeaderAuthRequestController{
8394
name: "RequestHeaderAuthRequestController",
@@ -98,7 +109,12 @@ func NewRequestHeaderAuthRequestController(
98109
return nil, err
99110
}
100111

101-
cmInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{
112+
// we construct our own informer because we need such a small subset of the information available. Just one namespace.
113+
c.configmapInformer = coreinformers.NewFilteredConfigMapInformer(client, c.configmapNamespace, 12*time.Hour, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, func(listOptions *metav1.ListOptions) {
114+
listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", c.configmapName).String()
115+
})
116+
117+
c.configmapInformer.AddEventHandler(cache.FilteringResourceEventHandler{
102118
FilterFunc: func(obj interface{}) bool {
103119
if cast, ok := obj.(*corev1.ConfigMap); ok {
104120
return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace
@@ -125,8 +141,8 @@ func NewRequestHeaderAuthRequestController(
125141
},
126142
})
127143

128-
c.configmapLister = cmInformer.Lister().ConfigMaps(c.configmapNamespace)
129-
c.configmapInformerSynced = cmInformer.Informer().HasSynced
144+
c.configmapLister = corev1listers.NewConfigMapLister(c.configmapInformer.GetIndexer()).ConfigMaps(c.configmapNamespace)
145+
c.configmapInformerSynced = c.configmapInformer.HasSynced
130146

131147
return c, nil
132148
}
@@ -155,6 +171,8 @@ func (c *RequestHeaderAuthRequestController) Run(workers int, stopCh <-chan stru
155171
klog.Infof("Starting %s", c.name)
156172
defer klog.Infof("Shutting down %s", c.name)
157173

174+
go c.configmapInformer.Run(stopCh)
175+
158176
// wait for caches to fill before starting your work
159177
if !cache.WaitForNamedCacheSync(c.name, stopCh, c.configmapInformerSynced) {
160178
return
@@ -224,6 +242,7 @@ func (c *RequestHeaderAuthRequestController) syncConfigMap(configMap *corev1.Con
224242
}
225243
if hasChanged {
226244
c.exportedRequestHeaderBundle.Store(newRequestHeaderBundle)
245+
klog.V(2).Infof("Loaded a new request header values for %v", c.name)
227246
}
228247
return nil
229248
}

staging/src/k8s.io/apiserver/pkg/server/options/authentication.go

Lines changed: 6 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ limitations under the License.
1717
package options
1818

1919
import (
20-
"context"
21-
"encoding/json"
2220
"fmt"
2321
"strings"
2422
"time"
@@ -27,7 +25,6 @@ import (
2725

2826
"github.com/spf13/pflag"
2927

30-
"k8s.io/apimachinery/pkg/api/errors"
3128
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3229
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
3330
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
@@ -330,66 +327,23 @@ const (
330327
// by --requestheader-username-headers. This is created in the cluster by the kube-apiserver.
331328
// "WARNING: generally do not depend on authorization being already done for incoming requests.")
332329
authenticationConfigMapName = "extension-apiserver-authentication"
333-
authenticationRoleName = "extension-apiserver-authentication-reader"
334330
)
335331

336332
func (s *DelegatingAuthenticationOptions) createRequestHeaderConfig(client kubernetes.Interface) (*authenticatorfactory.RequestHeaderConfig, error) {
337-
requestHeaderCAProvider, err := dynamiccertificates.NewDynamicCAFromConfigMapController("client-ca", authenticationConfigMapNamespace, authenticationConfigMapName, "requestheader-client-ca-file", client)
333+
dynamicRequestHeaderProvider, err := newDynamicRequestHeaderController(client)
338334
if err != nil {
339335
return nil, fmt.Errorf("unable to create request header authentication config: %v", err)
340336
}
341337

342-
authConfigMap, err := client.CoreV1().ConfigMaps(authenticationConfigMapNamespace).Get(context.TODO(), authenticationConfigMapName, metav1.GetOptions{})
343-
switch {
344-
case errors.IsNotFound(err):
345-
// ignore, authConfigMap is nil now
346-
return nil, nil
347-
case errors.IsForbidden(err):
348-
klog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+
349-
"'kubectl create rolebinding -n %s ROLEBINDING_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
350-
authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName)
351-
return nil, err
352-
case err != nil:
353-
return nil, err
354-
}
355-
356-
usernameHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-username-headers"])
357-
if err != nil {
358-
return nil, err
359-
}
360-
groupHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-group-headers"])
361-
if err != nil {
362-
return nil, err
363-
}
364-
extraHeaderPrefixes, err := deserializeStrings(authConfigMap.Data["requestheader-extra-headers-prefix"])
365-
if err != nil {
366-
return nil, err
367-
}
368-
allowedNames, err := deserializeStrings(authConfigMap.Data["requestheader-allowed-names"])
369-
if err != nil {
370-
return nil, err
371-
}
372-
373338
return &authenticatorfactory.RequestHeaderConfig{
374-
CAContentProvider: requestHeaderCAProvider,
375-
UsernameHeaders: headerrequest.StaticStringSlice(usernameHeaders),
376-
GroupHeaders: headerrequest.StaticStringSlice(groupHeaders),
377-
ExtraHeaderPrefixes: headerrequest.StaticStringSlice(extraHeaderPrefixes),
378-
AllowedClientNames: headerrequest.StaticStringSlice(allowedNames),
339+
CAContentProvider: dynamicRequestHeaderProvider,
340+
UsernameHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.UsernameHeaders)),
341+
GroupHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.GroupHeaders)),
342+
ExtraHeaderPrefixes: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.ExtraHeaderPrefixes)),
343+
AllowedClientNames: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.AllowedClientNames)),
379344
}, nil
380345
}
381346

382-
func deserializeStrings(in string) ([]string, error) {
383-
if len(in) == 0 {
384-
return nil, nil
385-
}
386-
var ret []string
387-
if err := json.Unmarshal([]byte(in), &ret); err != nil {
388-
return nil, err
389-
}
390-
return ret, nil
391-
}
392-
393347
// getClient returns a Kubernetes clientset. If s.RemoteKubeConfigFileOptional is true, nil will be returned
394348
// if no kubeconfig is specified by the user and the in-cluster config is not found.
395349
func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, error) {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package options
2+
3+
import (
4+
"fmt"
5+
6+
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
7+
"k8s.io/apiserver/pkg/server/dynamiccertificates"
8+
"k8s.io/client-go/kubernetes"
9+
)
10+
11+
var _ dynamiccertificates.ControllerRunner = &DynamicRequestHeaderController{}
12+
var _ dynamiccertificates.Notifier = &DynamicRequestHeaderController{}
13+
var _ dynamiccertificates.CAContentProvider = &DynamicRequestHeaderController{}
14+
15+
var _ headerrequest.RequestHeaderAuthRequestProvider = &DynamicRequestHeaderController{}
16+
17+
// DynamicRequestHeaderController combines DynamicCAFromConfigMapController and RequestHeaderAuthRequestController
18+
// into one controller for dynamically filling RequestHeaderConfig struct
19+
type DynamicRequestHeaderController struct {
20+
*dynamiccertificates.ConfigMapCAController
21+
*headerrequest.RequestHeaderAuthRequestController
22+
}
23+
24+
// newDynamicRequestHeaderController creates a new controller that implements DynamicRequestHeaderController
25+
func newDynamicRequestHeaderController(client kubernetes.Interface) (*DynamicRequestHeaderController, error) {
26+
requestHeaderCAController, err := dynamiccertificates.NewDynamicCAFromConfigMapController(
27+
"client-ca",
28+
authenticationConfigMapNamespace,
29+
authenticationConfigMapName,
30+
"requestheader-client-ca-file",
31+
client)
32+
if err != nil {
33+
return nil, fmt.Errorf("unable to create DynamicCAFromConfigMap controller: %v", err)
34+
}
35+
36+
requestHeaderAuthRequestController, err := headerrequest.NewRequestHeaderAuthRequestController(
37+
authenticationConfigMapName,
38+
authenticationConfigMapNamespace,
39+
client,
40+
"requestheader-username-headers",
41+
"requestheader-group-headers",
42+
"requestheader-extra-headers-prefix",
43+
"requestheader-allowed-names",
44+
)
45+
if err != nil {
46+
return nil, fmt.Errorf("unable to create RequestHeaderAuthRequest controller: %v", err)
47+
}
48+
return &DynamicRequestHeaderController{
49+
ConfigMapCAController: requestHeaderCAController,
50+
RequestHeaderAuthRequestController: requestHeaderAuthRequestController,
51+
}, nil
52+
}
53+
54+
func (c *DynamicRequestHeaderController) RunOnce() error {
55+
return c.ConfigMapCAController.RunOnce()
56+
}
57+
58+
func (c *DynamicRequestHeaderController) Run(workers int, stopCh <-chan struct{}) {
59+
go c.ConfigMapCAController.Run(workers, stopCh)
60+
go c.RequestHeaderAuthRequestController.Run(workers, stopCh)
61+
<-stopCh
62+
}

0 commit comments

Comments
 (0)