mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-10 12:32:03 +00:00
requestheaders: add a "requestheader-uid-headers" flag and wire it up
This commit is contained in:
parent
3b86da0c0c
commit
7fabd06c2b
@ -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-"},
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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"},
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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),
|
||||||
},
|
},
|
||||||
|
@ -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)),
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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-"},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user