requestheaders: add a "requestheader-uid-headers" flag and wire it up

This commit is contained in:
Stanislav Láznička 2023-02-16 11:28:50 +01:00
parent 3b86da0c0c
commit 7fabd06c2b
15 changed files with 187 additions and 19 deletions

View File

@ -430,6 +430,7 @@ func TestAddFlags(t *testing.T) {
ClientCert: apiserveroptions.ClientCertAuthenticationOptions{}, ClientCert: apiserveroptions.ClientCertAuthenticationOptions{},
RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{ RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{
UsernameHeaders: []string{"x-remote-user"}, UsernameHeaders: []string{"x-remote-user"},
UIDHeaders: []string{"x-remote-uid"},
GroupHeaders: []string{"x-remote-group"}, GroupHeaders: []string{"x-remote-group"},
ExtraHeaderPrefixes: []string{"x-remote-extra-"}, ExtraHeaderPrefixes: []string{"x-remote-extra-"},
}, },

View File

@ -110,6 +110,7 @@ func (config Config) New(serverLifecycle context.Context) (authenticator.Request
config.RequestHeaderConfig.CAContentProvider.VerifyOptions, config.RequestHeaderConfig.CAContentProvider.VerifyOptions,
config.RequestHeaderConfig.AllowedClientNames, config.RequestHeaderConfig.AllowedClientNames,
config.RequestHeaderConfig.UsernameHeaders, config.RequestHeaderConfig.UsernameHeaders,
config.RequestHeaderConfig.UIDHeaders,
config.RequestHeaderConfig.GroupHeaders, config.RequestHeaderConfig.GroupHeaders,
config.RequestHeaderConfig.ExtraHeaderPrefixes, config.RequestHeaderConfig.ExtraHeaderPrefixes,
) )

View File

@ -303,6 +303,7 @@ func TestToAuthenticationConfig(t *testing.T) {
}, },
RequestHeader: &apiserveroptions.RequestHeaderAuthenticationOptions{ RequestHeader: &apiserveroptions.RequestHeaderAuthenticationOptions{
UsernameHeaders: []string{"x-remote-user"}, UsernameHeaders: []string{"x-remote-user"},
UIDHeaders: []string{"x-remote-uid"},
GroupHeaders: []string{"x-remote-group"}, GroupHeaders: []string{"x-remote-group"},
ExtraHeaderPrefixes: []string{"x-remote-extra-"}, ExtraHeaderPrefixes: []string{"x-remote-extra-"},
ClientCAFile: "testdata/root.pem", ClientCAFile: "testdata/root.pem",
@ -352,6 +353,7 @@ func TestToAuthenticationConfig(t *testing.T) {
RequestHeaderConfig: &authenticatorfactory.RequestHeaderConfig{ RequestHeaderConfig: &authenticatorfactory.RequestHeaderConfig{
UsernameHeaders: headerrequest.StaticStringSlice{"x-remote-user"}, UsernameHeaders: headerrequest.StaticStringSlice{"x-remote-user"},
UIDHeaders: headerrequest.StaticStringSlice{"x-remote-uid"},
GroupHeaders: headerrequest.StaticStringSlice{"x-remote-group"}, GroupHeaders: headerrequest.StaticStringSlice{"x-remote-group"},
ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"x-remote-extra-"}, ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"x-remote-extra-"},
CAContentProvider: nil, // this is nil because you can't compare functions CAContentProvider: nil, // this is nil because you can't compare functions
@ -397,6 +399,7 @@ func TestBuiltInAuthenticationOptionsAddFlags(t *testing.T) {
"--client-ca-file=client-cacert", "--client-ca-file=client-cacert",
"--requestheader-client-ca-file=testdata/root.pem", "--requestheader-client-ca-file=testdata/root.pem",
"--requestheader-username-headers=x-remote-user-custom", "--requestheader-username-headers=x-remote-user-custom",
"--requestheader-uid-headers=x-remote-uid-custom",
"--requestheader-group-headers=x-remote-group-custom", "--requestheader-group-headers=x-remote-group-custom",
"--requestheader-allowed-names=kube-aggregator", "--requestheader-allowed-names=kube-aggregator",
"--service-account-key-file=cert", "--service-account-key-file=cert",
@ -430,6 +433,7 @@ func TestBuiltInAuthenticationOptionsAddFlags(t *testing.T) {
RequestHeader: &apiserveroptions.RequestHeaderAuthenticationOptions{ RequestHeader: &apiserveroptions.RequestHeaderAuthenticationOptions{
ClientCAFile: "testdata/root.pem", ClientCAFile: "testdata/root.pem",
UsernameHeaders: []string{"x-remote-user-custom"}, UsernameHeaders: []string{"x-remote-user-custom"},
UIDHeaders: []string{"x-remote-uid-custom"},
GroupHeaders: []string{"x-remote-group-custom"}, GroupHeaders: []string{"x-remote-group-custom"},
AllowedNames: []string{"kube-aggregator"}, AllowedNames: []string{"kube-aggregator"},
}, },

View File

@ -77,6 +77,7 @@ func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.Secur
c.RequestHeaderConfig.CAContentProvider.VerifyOptions, c.RequestHeaderConfig.CAContentProvider.VerifyOptions,
c.RequestHeaderConfig.AllowedClientNames, c.RequestHeaderConfig.AllowedClientNames,
c.RequestHeaderConfig.UsernameHeaders, c.RequestHeaderConfig.UsernameHeaders,
c.RequestHeaderConfig.UIDHeaders,
c.RequestHeaderConfig.GroupHeaders, c.RequestHeaderConfig.GroupHeaders,
c.RequestHeaderConfig.ExtraHeaderPrefixes, c.RequestHeaderConfig.ExtraHeaderPrefixes,
) )

View File

@ -24,6 +24,8 @@ import (
type RequestHeaderConfig struct { type RequestHeaderConfig struct {
// UsernameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins. // UsernameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins.
UsernameHeaders headerrequest.StringSliceProvider UsernameHeaders headerrequest.StringSliceProvider
// UsernameHeaders are the headers to check (in order, case-insensitively) for an identity UID. The first header with a value wins.
UIDHeaders headerrequest.StringSliceProvider
// GroupHeaders are the headers to check (case-insensitively) for a group names. All values will be used. // GroupHeaders are the headers to check (case-insensitively) for a group names. All values will be used.
GroupHeaders headerrequest.StringSliceProvider GroupHeaders headerrequest.StringSliceProvider
// ExtraHeaderPrefixes are the head prefixes to check (case-insentively) for filling in // ExtraHeaderPrefixes are the head prefixes to check (case-insentively) for filling in

View File

@ -53,6 +53,9 @@ type requestHeaderAuthRequestHandler struct {
// nameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins. // nameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins.
nameHeaders StringSliceProvider nameHeaders StringSliceProvider
// nameHeaders are the headers to check (in order, case-insensitively) for an identity UID. The first header with a value wins.
uidHeaders StringSliceProvider
// groupHeaders are the headers to check (case-insensitively) for group membership. All values of all headers will be added. // groupHeaders are the headers to check (case-insensitively) for group membership. All values of all headers will be added.
groupHeaders StringSliceProvider groupHeaders StringSliceProvider
@ -61,11 +64,15 @@ type requestHeaderAuthRequestHandler struct {
extraHeaderPrefixes StringSliceProvider extraHeaderPrefixes StringSliceProvider
} }
func New(nameHeaders, groupHeaders, extraHeaderPrefixes []string) (authenticator.Request, error) { func New(nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes []string) (authenticator.Request, error) {
trimmedNameHeaders, err := trimHeaders(nameHeaders...) trimmedNameHeaders, err := trimHeaders(nameHeaders...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
trimmedUIDHeaders, err := trimHeaders(uidHeaders...)
if err != nil {
return nil, err
}
trimmedGroupHeaders, err := trimHeaders(groupHeaders...) trimmedGroupHeaders, err := trimHeaders(groupHeaders...)
if err != nil { if err != nil {
return nil, err return nil, err
@ -77,14 +84,16 @@ func New(nameHeaders, groupHeaders, extraHeaderPrefixes []string) (authenticator
return NewDynamic( return NewDynamic(
StaticStringSlice(trimmedNameHeaders), StaticStringSlice(trimmedNameHeaders),
StaticStringSlice(trimmedUIDHeaders),
StaticStringSlice(trimmedGroupHeaders), StaticStringSlice(trimmedGroupHeaders),
StaticStringSlice(trimmedExtraHeaderPrefixes), StaticStringSlice(trimmedExtraHeaderPrefixes),
), nil ), nil
} }
func NewDynamic(nameHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) authenticator.Request { func NewDynamic(nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) authenticator.Request {
return &requestHeaderAuthRequestHandler{ return &requestHeaderAuthRequestHandler{
nameHeaders: nameHeaders, nameHeaders: nameHeaders,
uidHeaders: uidHeaders,
groupHeaders: groupHeaders, groupHeaders: groupHeaders,
extraHeaderPrefixes: extraHeaderPrefixes, extraHeaderPrefixes: extraHeaderPrefixes,
} }
@ -103,8 +112,8 @@ func trimHeaders(headerNames ...string) ([]string, error) {
return ret, nil return ret, nil
} }
func NewDynamicVerifyOptionsSecure(verifyOptionFn x509request.VerifyOptionFunc, proxyClientNames, nameHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) authenticator.Request { func NewDynamicVerifyOptionsSecure(verifyOptionFn x509request.VerifyOptionFunc, proxyClientNames, nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) authenticator.Request {
headerAuthenticator := NewDynamic(nameHeaders, groupHeaders, extraHeaderPrefixes) headerAuthenticator := NewDynamic(nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes)
return x509request.NewDynamicCAVerifier(verifyOptionFn, headerAuthenticator, proxyClientNames) return x509request.NewDynamicCAVerifier(verifyOptionFn, headerAuthenticator, proxyClientNames)
} }
@ -114,25 +123,30 @@ func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request)
if len(name) == 0 { if len(name) == 0 {
return nil, false, nil return nil, false, nil
} }
uid := headerValue(req.Header, a.uidHeaders.Value())
groups := allHeaderValues(req.Header, a.groupHeaders.Value()) groups := allHeaderValues(req.Header, a.groupHeaders.Value())
extra := newExtra(req.Header, a.extraHeaderPrefixes.Value()) extra := newExtra(req.Header, a.extraHeaderPrefixes.Value())
// clear headers used for authentication // clear headers used for authentication
ClearAuthenticationHeaders(req.Header, a.nameHeaders, a.groupHeaders, a.extraHeaderPrefixes) ClearAuthenticationHeaders(req.Header, a.nameHeaders, a.uidHeaders, a.groupHeaders, a.extraHeaderPrefixes)
return &authenticator.Response{ return &authenticator.Response{
User: &user.DefaultInfo{ User: &user.DefaultInfo{
Name: name, Name: name,
UID: uid,
Groups: groups, Groups: groups,
Extra: extra, Extra: extra,
}, },
}, true, nil }, true, nil
} }
func ClearAuthenticationHeaders(h http.Header, nameHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) { func ClearAuthenticationHeaders(h http.Header, nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) {
for _, headerName := range nameHeaders.Value() { for _, headerName := range nameHeaders.Value() {
h.Del(headerName) h.Del(headerName)
} }
for _, headerName := range uidHeaders.Value() {
h.Del(headerName)
}
for _, headerName := range groupHeaders.Value() { for _, headerName := range groupHeaders.Value() {
h.Del(headerName) h.Del(headerName)
} }

View File

@ -45,6 +45,7 @@ const (
// RequestHeaderAuthRequestProvider a provider that knows how to dynamically fill parts of RequestHeaderConfig struct // RequestHeaderAuthRequestProvider a provider that knows how to dynamically fill parts of RequestHeaderConfig struct
type RequestHeaderAuthRequestProvider interface { type RequestHeaderAuthRequestProvider interface {
UsernameHeaders() []string UsernameHeaders() []string
UIDHeaders() []string
GroupHeaders() []string GroupHeaders() []string
ExtraHeaderPrefixes() []string ExtraHeaderPrefixes() []string
AllowedClientNames() []string AllowedClientNames() []string
@ -54,6 +55,7 @@ var _ RequestHeaderAuthRequestProvider = &RequestHeaderAuthRequestController{}
type requestHeaderBundle struct { type requestHeaderBundle struct {
UsernameHeaders []string UsernameHeaders []string
UIDHeaders []string
GroupHeaders []string GroupHeaders []string
ExtraHeaderPrefixes []string ExtraHeaderPrefixes []string
AllowedClientNames []string AllowedClientNames []string
@ -80,6 +82,7 @@ type RequestHeaderAuthRequestController struct {
exportedRequestHeaderBundle atomic.Value exportedRequestHeaderBundle atomic.Value
usernameHeadersKey string usernameHeadersKey string
uidHeadersKey string
groupHeadersKey string groupHeadersKey string
extraHeaderPrefixesKey string extraHeaderPrefixesKey string
allowedClientNamesKey string allowedClientNamesKey string
@ -90,7 +93,7 @@ func NewRequestHeaderAuthRequestController(
cmName string, cmName string,
cmNamespace string, cmNamespace string,
client kubernetes.Interface, client kubernetes.Interface,
usernameHeadersKey, groupHeadersKey, extraHeaderPrefixesKey, allowedClientNamesKey string) *RequestHeaderAuthRequestController { usernameHeadersKey, uidHeadersKey, groupHeadersKey, extraHeaderPrefixesKey, allowedClientNamesKey string) *RequestHeaderAuthRequestController {
c := &RequestHeaderAuthRequestController{ c := &RequestHeaderAuthRequestController{
name: "RequestHeaderAuthRequestController", name: "RequestHeaderAuthRequestController",
@ -100,6 +103,7 @@ func NewRequestHeaderAuthRequestController(
configmapNamespace: cmNamespace, configmapNamespace: cmNamespace,
usernameHeadersKey: usernameHeadersKey, usernameHeadersKey: usernameHeadersKey,
uidHeadersKey: uidHeadersKey,
groupHeadersKey: groupHeadersKey, groupHeadersKey: groupHeadersKey,
extraHeaderPrefixesKey: extraHeaderPrefixesKey, extraHeaderPrefixesKey: extraHeaderPrefixesKey,
allowedClientNamesKey: allowedClientNamesKey, allowedClientNamesKey: allowedClientNamesKey,
@ -152,6 +156,10 @@ func (c *RequestHeaderAuthRequestController) UsernameHeaders() []string {
return c.loadRequestHeaderFor(c.usernameHeadersKey) return c.loadRequestHeaderFor(c.usernameHeadersKey)
} }
func (c *RequestHeaderAuthRequestController) UIDHeaders() []string {
return c.loadRequestHeaderFor(c.uidHeadersKey)
}
func (c *RequestHeaderAuthRequestController) GroupHeaders() []string { func (c *RequestHeaderAuthRequestController) GroupHeaders() []string {
return c.loadRequestHeaderFor(c.groupHeadersKey) return c.loadRequestHeaderFor(c.groupHeadersKey)
} }
@ -278,6 +286,11 @@ func (c *RequestHeaderAuthRequestController) getRequestHeaderBundleFromConfigMap
return nil, err return nil, err
} }
uidHeaderCurrentValue, err := deserializeStrings(cm.Data[c.uidHeadersKey])
if err != nil {
return nil, err
}
groupHeadersCurrentValue, err := deserializeStrings(cm.Data[c.groupHeadersKey]) groupHeadersCurrentValue, err := deserializeStrings(cm.Data[c.groupHeadersKey])
if err != nil { if err != nil {
return nil, err return nil, err
@ -296,6 +309,7 @@ func (c *RequestHeaderAuthRequestController) getRequestHeaderBundleFromConfigMap
return &requestHeaderBundle{ return &requestHeaderBundle{
UsernameHeaders: usernameHeaderCurrentValue, UsernameHeaders: usernameHeaderCurrentValue,
UIDHeaders: uidHeaderCurrentValue,
GroupHeaders: groupHeadersCurrentValue, GroupHeaders: groupHeadersCurrentValue,
ExtraHeaderPrefixes: extraHeaderPrefixesCurrentValue, ExtraHeaderPrefixes: extraHeaderPrefixesCurrentValue,
AllowedClientNames: allowedClientNamesCurrentValue, AllowedClientNames: allowedClientNamesCurrentValue,
@ -312,6 +326,8 @@ func (c *RequestHeaderAuthRequestController) loadRequestHeaderFor(key string) []
switch key { switch key {
case c.usernameHeadersKey: case c.usernameHeadersKey:
return headerBundle.UsernameHeaders return headerBundle.UsernameHeaders
case c.uidHeadersKey:
return headerBundle.UIDHeaders
case c.groupHeadersKey: case c.groupHeadersKey:
return headerBundle.GroupHeaders return headerBundle.GroupHeaders
case c.extraHeaderPrefixesKey: case c.extraHeaderPrefixesKey:

View File

@ -19,10 +19,10 @@ package headerrequest
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"k8s.io/apimachinery/pkg/api/equality"
"testing" "testing"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
corev1listers "k8s.io/client-go/listers/core/v1" corev1listers "k8s.io/client-go/listers/core/v1"
@ -34,6 +34,7 @@ const (
defConfigMapNamespace = "kube-system" defConfigMapNamespace = "kube-system"
defUsernameHeadersKey = "user-key" defUsernameHeadersKey = "user-key"
defUIDHeadersKey = "uid-key"
defGroupHeadersKey = "group-key" defGroupHeadersKey = "group-key"
defExtraHeaderPrefixesKey = "extra-key" defExtraHeaderPrefixesKey = "extra-key"
defAllowedClientNamesKey = "names-key" defAllowedClientNamesKey = "names-key"
@ -41,6 +42,7 @@ const (
type expectedHeadersHolder struct { type expectedHeadersHolder struct {
usernameHeaders []string usernameHeaders []string
uidHeaders []string
groupHeaders []string groupHeaders []string
extraHeaderPrefixes []string extraHeaderPrefixes []string
allowedClientNames []string allowedClientNames []string
@ -55,9 +57,10 @@ func TestRequestHeaderAuthRequestController(t *testing.T) {
}{ }{
{ {
name: "happy-path: headers values are populated form a config map", name: "happy-path: headers values are populated form a config map",
cm: defaultConfigMap(t, []string{"user-val"}, []string{"group-val"}, []string{"extra-val"}, []string{"names-val"}), cm: defaultConfigMap(t, []string{"user-val"}, []string{"uid-val"}, []string{"group-val"}, []string{"extra-val"}, []string{"names-val"}),
expectedHeader: expectedHeadersHolder{ expectedHeader: expectedHeadersHolder{
usernameHeaders: []string{"user-val"}, usernameHeaders: []string{"user-val"},
uidHeaders: []string{"uid-val"},
groupHeaders: []string{"group-val"}, groupHeaders: []string{"group-val"},
extraHeaderPrefixes: []string{"extra-val"}, extraHeaderPrefixes: []string{"extra-val"},
allowedClientNames: []string{"names-val"}, allowedClientNames: []string{"names-val"},
@ -66,7 +69,7 @@ func TestRequestHeaderAuthRequestController(t *testing.T) {
{ {
name: "passing an empty config map doesn't break the controller", name: "passing an empty config map doesn't break the controller",
cm: func() *corev1.ConfigMap { cm: func() *corev1.ConfigMap {
c := defaultConfigMap(t, nil, nil, nil, nil) c := defaultConfigMap(t, nil, nil, nil, nil, nil)
c.Data = map[string]string{} c.Data = map[string]string{}
return c return c
}(), }(),
@ -74,7 +77,7 @@ func TestRequestHeaderAuthRequestController(t *testing.T) {
{ {
name: "an invalid config map produces an error", name: "an invalid config map produces an error",
cm: func() *corev1.ConfigMap { cm: func() *corev1.ConfigMap {
c := defaultConfigMap(t, nil, nil, nil, nil) c := defaultConfigMap(t, nil, nil, nil, nil, nil)
c.Data = map[string]string{ c.Data = map[string]string{
defUsernameHeadersKey: "incorrect-json-array", defUsernameHeadersKey: "incorrect-json-array",
} }
@ -119,9 +122,10 @@ func TestRequestHeaderAuthRequestControllerPreserveState(t *testing.T) {
}{ }{
{ {
name: "scenario 1: headers values are populated form a config map", name: "scenario 1: headers values are populated form a config map",
cm: defaultConfigMap(t, []string{"user-val"}, []string{"group-val"}, []string{"extra-val"}, []string{"names-val"}), cm: defaultConfigMap(t, []string{"user-val"}, []string{"uid-val"}, []string{"group-val"}, []string{"extra-val"}, []string{"names-val"}),
expectedHeader: expectedHeadersHolder{ expectedHeader: expectedHeadersHolder{
usernameHeaders: []string{"user-val"}, usernameHeaders: []string{"user-val"},
uidHeaders: []string{"uid-val"},
groupHeaders: []string{"group-val"}, groupHeaders: []string{"group-val"},
extraHeaderPrefixes: []string{"extra-val"}, extraHeaderPrefixes: []string{"extra-val"},
allowedClientNames: []string{"names-val"}, allowedClientNames: []string{"names-val"},
@ -130,7 +134,7 @@ func TestRequestHeaderAuthRequestControllerPreserveState(t *testing.T) {
{ {
name: "scenario 2: an invalid config map produces an error but doesn't destroy the state (scenario 1)", name: "scenario 2: an invalid config map produces an error but doesn't destroy the state (scenario 1)",
cm: func() *corev1.ConfigMap { cm: func() *corev1.ConfigMap {
c := defaultConfigMap(t, nil, nil, nil, nil) c := defaultConfigMap(t, nil, nil, nil, nil, nil)
c.Data = map[string]string{ c.Data = map[string]string{
defUsernameHeadersKey: "incorrect-json-array", defUsernameHeadersKey: "incorrect-json-array",
} }
@ -139,6 +143,7 @@ func TestRequestHeaderAuthRequestControllerPreserveState(t *testing.T) {
expectErr: true, expectErr: true,
expectedHeader: expectedHeadersHolder{ expectedHeader: expectedHeadersHolder{
usernameHeaders: []string{"user-val"}, usernameHeaders: []string{"user-val"},
uidHeaders: []string{"uid-val"},
groupHeaders: []string{"group-val"}, groupHeaders: []string{"group-val"},
extraHeaderPrefixes: []string{"extra-val"}, extraHeaderPrefixes: []string{"extra-val"},
allowedClientNames: []string{"names-val"}, allowedClientNames: []string{"names-val"},
@ -146,9 +151,10 @@ func TestRequestHeaderAuthRequestControllerPreserveState(t *testing.T) {
}, },
{ {
name: "scenario 3: some headers values have changed (prev set by scenario 1)", name: "scenario 3: some headers values have changed (prev set by scenario 1)",
cm: defaultConfigMap(t, []string{"user-val"}, []string{"group-val-scenario-3"}, []string{"extra-val"}, []string{"names-val"}), cm: defaultConfigMap(t, []string{"user-val"}, []string{"uid-val"}, []string{"group-val-scenario-3"}, []string{"extra-val"}, []string{"names-val"}),
expectedHeader: expectedHeadersHolder{ expectedHeader: expectedHeadersHolder{
usernameHeaders: []string{"user-val"}, usernameHeaders: []string{"user-val"},
uidHeaders: []string{"uid-val"},
groupHeaders: []string{"group-val-scenario-3"}, groupHeaders: []string{"group-val-scenario-3"},
extraHeaderPrefixes: []string{"extra-val"}, extraHeaderPrefixes: []string{"extra-val"},
allowedClientNames: []string{"names-val"}, allowedClientNames: []string{"names-val"},
@ -156,9 +162,10 @@ func TestRequestHeaderAuthRequestControllerPreserveState(t *testing.T) {
}, },
{ {
name: "scenario 4: all headers values have changed (prev set by scenario 3)", name: "scenario 4: all headers values have changed (prev set by scenario 3)",
cm: defaultConfigMap(t, []string{"user-val-scenario-4"}, []string{"group-val-scenario-4"}, []string{"extra-val-scenario-4"}, []string{"names-val-scenario-4"}), cm: defaultConfigMap(t, []string{"user-val-scenario-4"}, []string{"uid-val-scenario-4"}, []string{"group-val-scenario-4"}, []string{"extra-val-scenario-4"}, []string{"names-val-scenario-4"}),
expectedHeader: expectedHeadersHolder{ expectedHeader: expectedHeadersHolder{
usernameHeaders: []string{"user-val-scenario-4"}, usernameHeaders: []string{"user-val-scenario-4"},
uidHeaders: []string{"uid-val-scenario-4"},
groupHeaders: []string{"group-val-scenario-4"}, groupHeaders: []string{"group-val-scenario-4"},
extraHeaderPrefixes: []string{"extra-val-scenario-4"}, extraHeaderPrefixes: []string{"extra-val-scenario-4"},
allowedClientNames: []string{"names-val-scenario-4"}, allowedClientNames: []string{"names-val-scenario-4"},
@ -204,9 +211,10 @@ func TestRequestHeaderAuthRequestControllerSyncOnce(t *testing.T) {
}{ }{
{ {
name: "headers values are populated form a config map", name: "headers values are populated form a config map",
cm: defaultConfigMap(t, []string{"user-val"}, []string{"group-val"}, []string{"extra-val"}, []string{"names-val"}), cm: defaultConfigMap(t, []string{"user-val"}, []string{"uid-val"}, []string{"group-val"}, []string{"extra-val"}, []string{"names-val"}),
expectedHeader: expectedHeadersHolder{ expectedHeader: expectedHeadersHolder{
usernameHeaders: []string{"user-val"}, usernameHeaders: []string{"user-val"},
uidHeaders: []string{"uid-val"},
groupHeaders: []string{"group-val"}, groupHeaders: []string{"group-val"},
extraHeaderPrefixes: []string{"extra-val"}, extraHeaderPrefixes: []string{"extra-val"},
allowedClientNames: []string{"names-val"}, allowedClientNames: []string{"names-val"},
@ -238,7 +246,7 @@ func TestRequestHeaderAuthRequestControllerSyncOnce(t *testing.T) {
} }
} }
func defaultConfigMap(t *testing.T, usernameHeaderVal, groupHeadersVal, extraHeaderPrefixesVal, allowedClientNamesVal []string) *corev1.ConfigMap { func defaultConfigMap(t *testing.T, usernameHeaderVal, uidHeaderVal, groupHeadersVal, extraHeaderPrefixesVal, allowedClientNamesVal []string) *corev1.ConfigMap {
encode := func(val []string) string { encode := func(val []string) string {
encodedVal, err := json.Marshal(val) encodedVal, err := json.Marshal(val)
if err != nil { if err != nil {
@ -253,6 +261,7 @@ func defaultConfigMap(t *testing.T, usernameHeaderVal, groupHeadersVal, extraHea
}, },
Data: map[string]string{ Data: map[string]string{
defUsernameHeadersKey: encode(usernameHeaderVal), defUsernameHeadersKey: encode(usernameHeaderVal),
defUIDHeadersKey: encode(uidHeaderVal),
defGroupHeadersKey: encode(groupHeadersVal), defGroupHeadersKey: encode(groupHeadersVal),
defExtraHeaderPrefixesKey: encode(extraHeaderPrefixesVal), defExtraHeaderPrefixesKey: encode(extraHeaderPrefixesVal),
defAllowedClientNamesKey: encode(allowedClientNamesVal), defAllowedClientNamesKey: encode(allowedClientNamesVal),
@ -265,6 +274,7 @@ func newDefaultTarget() *RequestHeaderAuthRequestController {
configmapName: defConfigMapName, configmapName: defConfigMapName,
configmapNamespace: defConfigMapNamespace, configmapNamespace: defConfigMapNamespace,
usernameHeadersKey: defUsernameHeadersKey, usernameHeadersKey: defUsernameHeadersKey,
uidHeadersKey: defUIDHeadersKey,
groupHeadersKey: defGroupHeadersKey, groupHeadersKey: defGroupHeadersKey,
extraHeaderPrefixesKey: defExtraHeaderPrefixesKey, extraHeaderPrefixesKey: defExtraHeaderPrefixesKey,
allowedClientNamesKey: defAllowedClientNamesKey, allowedClientNamesKey: defAllowedClientNamesKey,

View File

@ -29,6 +29,7 @@ import (
func TestRequestHeader(t *testing.T) { func TestRequestHeader(t *testing.T) {
testcases := map[string]struct { testcases := map[string]struct {
nameHeaders []string nameHeaders []string
uidHeaders []string
groupHeaders []string groupHeaders []string
extraPrefixHeaders []string extraPrefixHeaders []string
requestHeaders http.Header requestHeaders http.Header
@ -128,13 +129,66 @@ func TestRequestHeader(t *testing.T) {
}, },
expectedOk: true, expectedOk: true,
}, },
"uid none": {
nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-Uid"},
requestHeaders: http.Header{
"X-Remote-User": {"Bob"},
},
expectedUser: &user.DefaultInfo{
Name: "Bob",
UID: "",
Groups: []string{},
Extra: map[string][]string{},
},
expectedOk: true,
},
"uid exact match": {
nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-Uid"},
requestHeaders: http.Header{
"X-Remote-User": {"Bob"},
// The keys in http.Header MUST be http.CanonicalHeaderKey.
// Hence X-Remote-Uid-1 instead of X-Remote-UID-1.
"X-Remote-Uid-1": {"8f5ea9d1-a5ed-4d02-80a2-26709216350b"},
"X-Remote-Uid-2": {"c7644180-c774-4a9b-81e5-3eef76f087ab"},
},
finalHeaders: http.Header{
"X-Remote-Uid-1": {"8f5ea9d1-a5ed-4d02-80a2-26709216350b"},
"X-Remote-Uid-2": {"c7644180-c774-4a9b-81e5-3eef76f087ab"},
},
expectedUser: &user.DefaultInfo{
Name: "Bob",
UID: "",
Groups: []string{},
Extra: map[string][]string{},
},
expectedOk: true,
},
"uid first match": {
nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-Uid-1", "X-Remote-Uid-2"},
requestHeaders: http.Header{
"X-Remote-User": {"Bob"},
"X-Remote-Uid-1": {"8f5ea9d1-a5ed-4d02-80a2-26709216350b"},
"X-Remote-Uid-2": {"c7644180-c774-4a9b-81e5-3eef76f087ab"},
},
expectedUser: &user.DefaultInfo{
Name: "Bob",
UID: "8f5ea9d1-a5ed-4d02-80a2-26709216350b",
Groups: []string{},
Extra: map[string][]string{},
},
expectedOk: true,
},
"extra prefix matches case-insensitive": { "extra prefix matches case-insensitive": {
nameHeaders: []string{"X-Remote-User"}, nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-UID"},
groupHeaders: []string{"X-Remote-Group-1", "X-Remote-Group-2"}, groupHeaders: []string{"X-Remote-Group-1", "X-Remote-Group-2"},
extraPrefixHeaders: []string{"X-Remote-Extra-1-", "X-Remote-Extra-2-"}, extraPrefixHeaders: []string{"X-Remote-Extra-1-", "X-Remote-Extra-2-"},
requestHeaders: http.Header{ requestHeaders: http.Header{
"X-Remote-User": {"Bob"}, "X-Remote-User": {"Bob"},
"X-Remote-Uid": {"2ca80fb0-60ea-4ecf-951c-89af843b0402"},
"X-Remote-Group-1": {"one-a", "one-b"}, "X-Remote-Group-1": {"one-a", "one-b"},
"X-Remote-Group-2": {"two-a", "two-b"}, "X-Remote-Group-2": {"two-a", "two-b"},
"X-Remote-extra-1-key1": {"alfa", "bravo"}, "X-Remote-extra-1-key1": {"alfa", "bravo"},
@ -146,6 +200,7 @@ func TestRequestHeader(t *testing.T) {
}, },
expectedUser: &user.DefaultInfo{ expectedUser: &user.DefaultInfo{
Name: "Bob", Name: "Bob",
UID: "2ca80fb0-60ea-4ecf-951c-89af843b0402",
Groups: []string{"one-a", "one-b", "two-a", "two-b"}, Groups: []string{"one-a", "one-b", "two-a", "two-b"},
Extra: map[string][]string{ Extra: map[string][]string{
"key1": {"alfa", "bravo", "echo", "foxtrot"}, "key1": {"alfa", "bravo", "echo", "foxtrot"},
@ -191,10 +246,12 @@ func TestRequestHeader(t *testing.T) {
"escaped extra keys": { "escaped extra keys": {
nameHeaders: []string{"X-Remote-User"}, nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-Uid"},
groupHeaders: []string{"X-Remote-Group"}, groupHeaders: []string{"X-Remote-Group"},
extraPrefixHeaders: []string{"X-Remote-Extra-"}, extraPrefixHeaders: []string{"X-Remote-Extra-"},
requestHeaders: http.Header{ requestHeaders: http.Header{
"X-Remote-User": {"Bob"}, "X-Remote-User": {"Bob"},
"X-Remote-Uid": {"2ca80fb0-60ea-4ecf-951c-89af843b0402"},
"X-Remote-Group": {"one-a", "one-b"}, "X-Remote-Group": {"one-a", "one-b"},
"X-Remote-Extra-Alpha": {"alphabetical"}, "X-Remote-Extra-Alpha": {"alphabetical"},
"X-Remote-Extra-Alph4num3r1c": {"alphanumeric"}, "X-Remote-Extra-Alph4num3r1c": {"alphanumeric"},
@ -206,6 +263,7 @@ func TestRequestHeader(t *testing.T) {
}, },
expectedUser: &user.DefaultInfo{ expectedUser: &user.DefaultInfo{
Name: "Bob", Name: "Bob",
UID: "2ca80fb0-60ea-4ecf-951c-89af843b0402",
Groups: []string{"one-a", "one-b"}, Groups: []string{"one-a", "one-b"},
Extra: map[string][]string{ Extra: map[string][]string{
"alpha": {"alphabetical"}, "alpha": {"alphabetical"},
@ -223,7 +281,7 @@ func TestRequestHeader(t *testing.T) {
for k, testcase := range testcases { for k, testcase := range testcases {
t.Run(k, func(t *testing.T) { t.Run(k, func(t *testing.T) {
auth, err := New(testcase.nameHeaders, testcase.groupHeaders, testcase.extraPrefixHeaders) auth, err := New(testcase.nameHeaders, testcase.uidHeaders, testcase.groupHeaders, testcase.extraPrefixHeaders)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -54,6 +54,7 @@ func withAuthentication(handler http.Handler, auth authenticator.Request, failed
} }
standardRequestHeaderConfig := &authenticatorfactory.RequestHeaderConfig{ standardRequestHeaderConfig := &authenticatorfactory.RequestHeaderConfig{
UsernameHeaders: headerrequest.StaticStringSlice{"X-Remote-User"}, UsernameHeaders: headerrequest.StaticStringSlice{"X-Remote-User"},
UIDHeaders: headerrequest.StaticStringSlice{"X-Remote-Uid"},
GroupHeaders: headerrequest.StaticStringSlice{"X-Remote-Group"}, GroupHeaders: headerrequest.StaticStringSlice{"X-Remote-Group"},
ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"X-Remote-Extra-"}, ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"X-Remote-Extra-"},
} }
@ -90,6 +91,7 @@ func withAuthentication(handler http.Handler, auth authenticator.Request, failed
headerrequest.ClearAuthenticationHeaders( headerrequest.ClearAuthenticationHeaders(
req.Header, req.Header,
standardRequestHeaderConfig.UsernameHeaders, standardRequestHeaderConfig.UsernameHeaders,
standardRequestHeaderConfig.UIDHeaders,
standardRequestHeaderConfig.GroupHeaders, standardRequestHeaderConfig.GroupHeaders,
standardRequestHeaderConfig.ExtraHeaderPrefixes, standardRequestHeaderConfig.ExtraHeaderPrefixes,
) )
@ -99,6 +101,7 @@ func withAuthentication(handler http.Handler, auth authenticator.Request, failed
headerrequest.ClearAuthenticationHeaders( headerrequest.ClearAuthenticationHeaders(
req.Header, req.Header,
requestHeaderConfig.UsernameHeaders, requestHeaderConfig.UsernameHeaders,
requestHeaderConfig.UIDHeaders,
requestHeaderConfig.GroupHeaders, requestHeaderConfig.GroupHeaders,
requestHeaderConfig.ExtraHeaderPrefixes, requestHeaderConfig.ExtraHeaderPrefixes,
) )

View File

@ -285,6 +285,7 @@ func TestAuthenticateRequestError(t *testing.T) {
func TestAuthenticateRequestClearHeaders(t *testing.T) { func TestAuthenticateRequestClearHeaders(t *testing.T) {
testcases := map[string]struct { testcases := map[string]struct {
nameHeaders []string nameHeaders []string
uidHeaders []string
groupHeaders []string groupHeaders []string
extraPrefixHeaders []string extraPrefixHeaders []string
requestHeaders http.Header requestHeaders http.Header
@ -334,13 +335,39 @@ func TestAuthenticateRequestClearHeaders(t *testing.T) {
"X-Remote-Group": {"Users"}, "X-Remote-Group": {"Users"},
}, },
}, },
"uid none": {
nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-Uid"},
requestHeaders: http.Header{
"X-Remote-User": {"Alice"},
},
},
"uid all matches": {
nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-Uid-1", "X-Remote-Uid-2"},
requestHeaders: http.Header{
"X-Remote-User": {"Alice"},
"X-Remote-Uid-1": {"one"},
"X-Remote-Uid-2": {"two", "three"},
},
},
"uid case-insensitive": {
nameHeaders: []string{"X-Remote-USER"},
uidHeaders: []string{"X-REMOTE-UID-1"},
requestHeaders: http.Header{
"X-Remote-User": {"Alice"},
"X-Remote-Uid-1": {"one"},
},
},
"extra prefix matches case-insensitive": { "extra prefix matches case-insensitive": {
nameHeaders: []string{"X-Remote-User"}, nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-Uid-1"},
groupHeaders: []string{"X-Remote-Group-1", "X-Remote-Group-2"}, groupHeaders: []string{"X-Remote-Group-1", "X-Remote-Group-2"},
extraPrefixHeaders: []string{"X-Remote-Extra-1-", "X-Remote-Extra-2-"}, extraPrefixHeaders: []string{"X-Remote-Extra-1-", "X-Remote-Extra-2-"},
requestHeaders: http.Header{ requestHeaders: http.Header{
"X-Remote-User": {"Bob"}, "X-Remote-User": {"Bob"},
"X-Remote-Uid-1": {"bobs-uid"},
"X-Remote-Group-1": {"one-a", "one-b"}, "X-Remote-Group-1": {"one-a", "one-b"},
"X-Remote-Group-2": {"two-a", "two-b"}, "X-Remote-Group-2": {"two-a", "two-b"},
"X-Remote-extra-1-key1": {"alfa", "bravo"}, "X-Remote-extra-1-key1": {"alfa", "bravo"},
@ -354,12 +381,15 @@ func TestAuthenticateRequestClearHeaders(t *testing.T) {
"extra prefix matches case-insensitive with unrelated headers": { "extra prefix matches case-insensitive with unrelated headers": {
nameHeaders: []string{"X-Remote-User"}, nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-Uid"},
groupHeaders: []string{"X-Remote-Group-1", "X-Remote-Group-2"}, groupHeaders: []string{"X-Remote-Group-1", "X-Remote-Group-2"},
extraPrefixHeaders: []string{"X-Remote-Extra-1-", "X-Remote-Extra-2-"}, extraPrefixHeaders: []string{"X-Remote-Extra-1-", "X-Remote-Extra-2-"},
requestHeaders: http.Header{ requestHeaders: http.Header{
"X-Group-Remote": {"snorlax"}, // unrelated header "X-Group-Remote": {"snorlax"}, // unrelated header
"X-Group-Bear": {"panda"}, // another unrelated header "X-Group-Bear": {"panda"}, // another unrelated header
"X-Uid-Remote": {"bobs-unrelated-uid"},
"X-Remote-User": {"Bob"}, "X-Remote-User": {"Bob"},
"X-Remote-Uid": {"bobs-uid"},
"X-Remote-Group-1": {"one-a", "one-b"}, "X-Remote-Group-1": {"one-a", "one-b"},
"X-Remote-Group-2": {"two-a", "two-b"}, "X-Remote-Group-2": {"two-a", "two-b"},
"X-Remote-extra-1-key1": {"alfa", "bravo"}, "X-Remote-extra-1-key1": {"alfa", "bravo"},
@ -372,15 +402,18 @@ func TestAuthenticateRequestClearHeaders(t *testing.T) {
finalHeaders: http.Header{ finalHeaders: http.Header{
"X-Group-Remote": {"snorlax"}, "X-Group-Remote": {"snorlax"},
"X-Group-Bear": {"panda"}, "X-Group-Bear": {"panda"},
"X-Uid-Remote": {"bobs-unrelated-uid"},
}, },
}, },
"custom config but request contains standard headers": { "custom config but request contains standard headers": {
nameHeaders: []string{"foo"}, nameHeaders: []string{"foo"},
uidHeaders: []string{"footoo"},
groupHeaders: []string{"bar"}, groupHeaders: []string{"bar"},
extraPrefixHeaders: []string{"baz"}, extraPrefixHeaders: []string{"baz"},
requestHeaders: http.Header{ requestHeaders: http.Header{
"X-Remote-User": {"Bob"}, "X-Remote-User": {"Bob"},
"X-Remote-Uid": {"bobs-uid"},
"X-Remote-Group-1": {"one-a", "one-b"}, "X-Remote-Group-1": {"one-a", "one-b"},
"X-Remote-Group-2": {"two-a", "two-b"}, "X-Remote-Group-2": {"two-a", "two-b"},
"X-Remote-extra-1-key1": {"alfa", "bravo"}, "X-Remote-extra-1-key1": {"alfa", "bravo"},
@ -398,10 +431,12 @@ func TestAuthenticateRequestClearHeaders(t *testing.T) {
"custom config but request contains standard and custom headers": { "custom config but request contains standard and custom headers": {
nameHeaders: []string{"one"}, nameHeaders: []string{"one"},
uidHeaders: []string{"onetoo"},
groupHeaders: []string{"two"}, groupHeaders: []string{"two"},
extraPrefixHeaders: []string{"three-"}, extraPrefixHeaders: []string{"three-"},
requestHeaders: http.Header{ requestHeaders: http.Header{
"X-Remote-User": {"Bob"}, "X-Remote-User": {"Bob"},
"X-Remote-Uid": {"bobs-uid"},
"X-Remote-Group-3": {"one-a", "one-b"}, "X-Remote-Group-3": {"one-a", "one-b"},
"X-Remote-Group-4": {"two-a", "two-b"}, "X-Remote-Group-4": {"two-a", "two-b"},
"X-Remote-extra-1-key1": {"alfa", "bravo"}, "X-Remote-extra-1-key1": {"alfa", "bravo"},
@ -422,10 +457,12 @@ func TestAuthenticateRequestClearHeaders(t *testing.T) {
"escaped extra keys": { "escaped extra keys": {
nameHeaders: []string{"X-Remote-User"}, nameHeaders: []string{"X-Remote-User"},
uidHeaders: []string{"X-Remote-Uid"},
groupHeaders: []string{"X-Remote-Group"}, groupHeaders: []string{"X-Remote-Group"},
extraPrefixHeaders: []string{"X-Remote-Extra-"}, extraPrefixHeaders: []string{"X-Remote-Extra-"},
requestHeaders: http.Header{ requestHeaders: http.Header{
"X-Remote-User": {"Bob"}, "X-Remote-User": {"Bob"},
"X-Remote-Uid": {"bobs-uid"},
"X-Remote-Group": {"one-a", "one-b"}, "X-Remote-Group": {"one-a", "one-b"},
"X-Remote-Extra-Alpha": {"alphabetical"}, "X-Remote-Extra-Alpha": {"alphabetical"},
"X-Remote-Extra-Alph4num3r1c": {"alphanumeric"}, "X-Remote-Extra-Alph4num3r1c": {"alphanumeric"},
@ -455,6 +492,7 @@ func TestAuthenticateRequestClearHeaders(t *testing.T) {
nil, nil,
&authenticatorfactory.RequestHeaderConfig{ &authenticatorfactory.RequestHeaderConfig{
UsernameHeaders: headerrequest.StaticStringSlice(testcase.nameHeaders), UsernameHeaders: headerrequest.StaticStringSlice(testcase.nameHeaders),
UIDHeaders: headerrequest.StaticStringSlice(testcase.uidHeaders),
GroupHeaders: headerrequest.StaticStringSlice(testcase.groupHeaders), GroupHeaders: headerrequest.StaticStringSlice(testcase.groupHeaders),
ExtraHeaderPrefixes: headerrequest.StaticStringSlice(testcase.extraPrefixHeaders), ExtraHeaderPrefixes: headerrequest.StaticStringSlice(testcase.extraPrefixHeaders),
}, },

View File

@ -56,6 +56,7 @@ type RequestHeaderAuthenticationOptions struct {
ClientCAFile string ClientCAFile string
UsernameHeaders []string UsernameHeaders []string
UIDHeaders []string
GroupHeaders []string GroupHeaders []string
ExtraHeaderPrefixes []string ExtraHeaderPrefixes []string
AllowedNames []string AllowedNames []string
@ -67,6 +68,9 @@ func (s *RequestHeaderAuthenticationOptions) Validate() []error {
if err := checkForWhiteSpaceOnly("requestheader-username-headers", s.UsernameHeaders...); err != nil { if err := checkForWhiteSpaceOnly("requestheader-username-headers", s.UsernameHeaders...); err != nil {
allErrors = append(allErrors, err) allErrors = append(allErrors, err)
} }
if err := checkForWhiteSpaceOnly("requestheader-uid-headers", s.UIDHeaders...); err != nil {
allErrors = append(allErrors, err)
}
if err := checkForWhiteSpaceOnly("requestheader-group-headers", s.GroupHeaders...); err != nil { if err := checkForWhiteSpaceOnly("requestheader-group-headers", s.GroupHeaders...); err != nil {
allErrors = append(allErrors, err) allErrors = append(allErrors, err)
} }
@ -80,6 +84,10 @@ func (s *RequestHeaderAuthenticationOptions) Validate() []error {
if len(s.UsernameHeaders) > 0 && !caseInsensitiveHas(s.UsernameHeaders, "X-Remote-User") { if len(s.UsernameHeaders) > 0 && !caseInsensitiveHas(s.UsernameHeaders, "X-Remote-User") {
klog.Warningf("--requestheader-username-headers is set without specifying the standard X-Remote-User header - API aggregation will not work") klog.Warningf("--requestheader-username-headers is set without specifying the standard X-Remote-User header - API aggregation will not work")
} }
if len(s.UIDHeaders) > 0 && !caseInsensitiveHas(s.UIDHeaders, "X-Remote-Uid") {
// this was added later and so we are able to error out
allErrors = append(allErrors, fmt.Errorf("--requestheader-uid-headers is set without specifying the standard X-Remote-Uid header - API aggregation will not work"))
}
if len(s.GroupHeaders) > 0 && !caseInsensitiveHas(s.GroupHeaders, "X-Remote-Group") { if len(s.GroupHeaders) > 0 && !caseInsensitiveHas(s.GroupHeaders, "X-Remote-Group") {
klog.Warningf("--requestheader-group-headers is set without specifying the standard X-Remote-Group header - API aggregation will not work") klog.Warningf("--requestheader-group-headers is set without specifying the standard X-Remote-Group header - API aggregation will not work")
} }
@ -117,6 +125,9 @@ func (s *RequestHeaderAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringSliceVar(&s.UsernameHeaders, "requestheader-username-headers", s.UsernameHeaders, ""+ fs.StringSliceVar(&s.UsernameHeaders, "requestheader-username-headers", s.UsernameHeaders, ""+
"List of request headers to inspect for usernames. X-Remote-User is common.") "List of request headers to inspect for usernames. X-Remote-User is common.")
fs.StringSliceVar(&s.UIDHeaders, "requestheader-uid-headers", s.UIDHeaders, ""+
"List of request headers to inspect for UIDs. X-Remote-Uid is suggested.")
fs.StringSliceVar(&s.GroupHeaders, "requestheader-group-headers", s.GroupHeaders, ""+ fs.StringSliceVar(&s.GroupHeaders, "requestheader-group-headers", s.GroupHeaders, ""+
"List of request headers to inspect for groups. X-Remote-Group is suggested.") "List of request headers to inspect for groups. X-Remote-Group is suggested.")
@ -148,6 +159,7 @@ func (s *RequestHeaderAuthenticationOptions) ToAuthenticationRequestHeaderConfig
return &authenticatorfactory.RequestHeaderConfig{ return &authenticatorfactory.RequestHeaderConfig{
UsernameHeaders: headerrequest.StaticStringSlice(s.UsernameHeaders), UsernameHeaders: headerrequest.StaticStringSlice(s.UsernameHeaders),
UIDHeaders: headerrequest.StaticStringSlice(s.UIDHeaders),
GroupHeaders: headerrequest.StaticStringSlice(s.GroupHeaders), GroupHeaders: headerrequest.StaticStringSlice(s.GroupHeaders),
ExtraHeaderPrefixes: headerrequest.StaticStringSlice(s.ExtraHeaderPrefixes), ExtraHeaderPrefixes: headerrequest.StaticStringSlice(s.ExtraHeaderPrefixes),
CAContentProvider: caBundleProvider, CAContentProvider: caBundleProvider,
@ -234,6 +246,7 @@ func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions {
ClientCert: ClientCertAuthenticationOptions{}, ClientCert: ClientCertAuthenticationOptions{},
RequestHeader: RequestHeaderAuthenticationOptions{ RequestHeader: RequestHeaderAuthenticationOptions{
UsernameHeaders: []string{"x-remote-user"}, UsernameHeaders: []string{"x-remote-user"},
UIDHeaders: []string{"x-remote-uid"},
GroupHeaders: []string{"x-remote-group"}, GroupHeaders: []string{"x-remote-group"},
ExtraHeaderPrefixes: []string{"x-remote-extra-"}, ExtraHeaderPrefixes: []string{"x-remote-extra-"},
}, },
@ -423,6 +436,7 @@ func (s *DelegatingAuthenticationOptions) createRequestHeaderConfig(client kuber
return &authenticatorfactory.RequestHeaderConfig{ return &authenticatorfactory.RequestHeaderConfig{
CAContentProvider: dynamicRequestHeaderProvider, CAContentProvider: dynamicRequestHeaderProvider,
UsernameHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.UsernameHeaders)), UsernameHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.UsernameHeaders)),
UIDHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.UIDHeaders)),
GroupHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.GroupHeaders)), GroupHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.GroupHeaders)),
ExtraHeaderPrefixes: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.ExtraHeaderPrefixes)), ExtraHeaderPrefixes: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.ExtraHeaderPrefixes)),
AllowedClientNames: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.AllowedClientNames)), AllowedClientNames: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.AllowedClientNames)),

View File

@ -55,6 +55,7 @@ func newDynamicRequestHeaderController(client kubernetes.Interface) (*DynamicReq
authenticationConfigMapNamespace, authenticationConfigMapNamespace,
client, client,
"requestheader-username-headers", "requestheader-username-headers",
"requestheader-uid-headers",
"requestheader-group-headers", "requestheader-group-headers",
"requestheader-extra-headers-prefix", "requestheader-extra-headers-prefix",
"requestheader-allowed-names", "requestheader-allowed-names",

View File

@ -39,6 +39,7 @@ func TestToAuthenticationRequestHeaderConfig(t *testing.T) {
name: "test when ClientCAFile is nil", name: "test when ClientCAFile is nil",
testOptions: &RequestHeaderAuthenticationOptions{ testOptions: &RequestHeaderAuthenticationOptions{
UsernameHeaders: headerrequest.StaticStringSlice{"x-remote-user"}, UsernameHeaders: headerrequest.StaticStringSlice{"x-remote-user"},
UIDHeaders: headerrequest.StaticStringSlice{"x-remote-uid"},
GroupHeaders: headerrequest.StaticStringSlice{"x-remote-group"}, GroupHeaders: headerrequest.StaticStringSlice{"x-remote-group"},
ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"x-remote-extra-"}, ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"x-remote-extra-"},
AllowedNames: headerrequest.StaticStringSlice{"kube-aggregator"}, AllowedNames: headerrequest.StaticStringSlice{"kube-aggregator"},
@ -49,12 +50,14 @@ func TestToAuthenticationRequestHeaderConfig(t *testing.T) {
testOptions: &RequestHeaderAuthenticationOptions{ testOptions: &RequestHeaderAuthenticationOptions{
ClientCAFile: "testdata/root.pem", ClientCAFile: "testdata/root.pem",
UsernameHeaders: headerrequest.StaticStringSlice{"x-remote-user"}, UsernameHeaders: headerrequest.StaticStringSlice{"x-remote-user"},
UIDHeaders: headerrequest.StaticStringSlice{"x-remote-uid"},
GroupHeaders: headerrequest.StaticStringSlice{"x-remote-group"}, GroupHeaders: headerrequest.StaticStringSlice{"x-remote-group"},
ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"x-remote-extra-"}, ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"x-remote-extra-"},
AllowedNames: headerrequest.StaticStringSlice{"kube-aggregator"}, AllowedNames: headerrequest.StaticStringSlice{"kube-aggregator"},
}, },
expectConfig: &authenticatorfactory.RequestHeaderConfig{ expectConfig: &authenticatorfactory.RequestHeaderConfig{
UsernameHeaders: headerrequest.StaticStringSlice{"x-remote-user"}, UsernameHeaders: headerrequest.StaticStringSlice{"x-remote-user"},
UIDHeaders: headerrequest.StaticStringSlice{"x-remote-uid"},
GroupHeaders: headerrequest.StaticStringSlice{"x-remote-group"}, GroupHeaders: headerrequest.StaticStringSlice{"x-remote-group"},
ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"x-remote-extra-"}, ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"x-remote-extra-"},
CAContentProvider: nil, // this is nil because you can't compare functions CAContentProvider: nil, // this is nil because you can't compare functions

View File

@ -133,6 +133,7 @@ func TestDefaultFlags(t *testing.T) {
ClientCert: apiserveroptions.ClientCertAuthenticationOptions{}, ClientCert: apiserveroptions.ClientCertAuthenticationOptions{},
RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{ RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{
UsernameHeaders: []string{"x-remote-user"}, UsernameHeaders: []string{"x-remote-user"},
UIDHeaders: []string{"x-remote-uid"},
GroupHeaders: []string{"x-remote-group"}, GroupHeaders: []string{"x-remote-group"},
ExtraHeaderPrefixes: []string{"x-remote-extra-"}, ExtraHeaderPrefixes: []string{"x-remote-extra-"},
}, },
@ -293,6 +294,7 @@ func TestAddFlags(t *testing.T) {
ClientCert: apiserveroptions.ClientCertAuthenticationOptions{}, ClientCert: apiserveroptions.ClientCertAuthenticationOptions{},
RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{ RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{
UsernameHeaders: []string{"x-remote-user"}, UsernameHeaders: []string{"x-remote-user"},
UIDHeaders: []string{"x-remote-uid"},
GroupHeaders: []string{"x-remote-group"}, GroupHeaders: []string{"x-remote-group"},
ExtraHeaderPrefixes: []string{"x-remote-extra-"}, ExtraHeaderPrefixes: []string{"x-remote-extra-"},
}, },