mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 21:17:23 +00:00
Merge pull request #129081 from stlaz/fg_remote_uid
featuregate UID in RequestHeader authenticator
This commit is contained in:
commit
1504f10e79
@ -35,7 +35,9 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
@ -262,10 +264,13 @@ func getConfigMapDataFor(authenticationInfo ClusterAuthenticationInfo) (map[stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data["requestheader-uid-headers"], err = jsonSerializeStringSlice(authenticationInfo.RequestHeaderUIDHeaders.Value())
|
if utilfeature.DefaultFeatureGate.Enabled(features.RemoteRequestHeaderUID) && len(authenticationInfo.RequestHeaderUIDHeaders.Value()) > 0 {
|
||||||
if err != nil {
|
data["requestheader-uid-headers"], err = jsonSerializeStringSlice(authenticationInfo.RequestHeaderUIDHeaders.Value())
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data["requestheader-group-headers"], err = jsonSerializeStringSlice(authenticationInfo.RequestHeaderGroupHeaders.Value())
|
data["requestheader-group-headers"], err = jsonSerializeStringSlice(authenticationInfo.RequestHeaderGroupHeaders.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -305,9 +310,12 @@ func getClusterAuthenticationInfoFor(data map[string]string) (ClusterAuthenticat
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ClusterAuthenticationInfo{}, err
|
return ClusterAuthenticationInfo{}, err
|
||||||
}
|
}
|
||||||
ret.RequestHeaderUIDHeaders, err = jsonDeserializeStringSlice(data["requestheader-uid-headers"])
|
|
||||||
if err != nil {
|
if utilfeature.DefaultFeatureGate.Enabled(features.RemoteRequestHeaderUID) {
|
||||||
return ClusterAuthenticationInfo{}, err
|
ret.RequestHeaderUIDHeaders, err = jsonDeserializeStringSlice(data["requestheader-uid-headers"])
|
||||||
|
if err != nil {
|
||||||
|
return ClusterAuthenticationInfo{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if caBundle := data["requestheader-client-ca-file"]; len(caBundle) > 0 {
|
if caBundle := data["requestheader-client-ca-file"]; len(caBundle) > 0 {
|
||||||
|
@ -30,11 +30,14 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/dump"
|
"k8s.io/apimachinery/pkg/util/dump"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"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"
|
||||||
clienttesting "k8s.io/client-go/testing"
|
clienttesting "k8s.io/client-go/testing"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -95,6 +98,7 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
preexistingObjs []runtime.Object
|
preexistingObjs []runtime.Object
|
||||||
expectedConfigMaps map[string]*corev1.ConfigMap
|
expectedConfigMaps map[string]*corev1.ConfigMap
|
||||||
expectCreate bool
|
expectCreate bool
|
||||||
|
uidGate bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "basic",
|
name: "basic",
|
||||||
@ -107,6 +111,32 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
RequestHeaderCA: anotherRandomCAProvider,
|
RequestHeaderCA: anotherRandomCAProvider,
|
||||||
RequestHeaderAllowedNames: headerrequest.StaticStringSlice{"first", "second"},
|
RequestHeaderAllowedNames: headerrequest.StaticStringSlice{"first", "second"},
|
||||||
},
|
},
|
||||||
|
expectedConfigMaps: map[string]*corev1.ConfigMap{
|
||||||
|
"extension-apiserver-authentication": {
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
|
Data: map[string]string{
|
||||||
|
"client-ca-file": string(someRandomCA),
|
||||||
|
"requestheader-username-headers": `["alfa","bravo","charlie"]`,
|
||||||
|
"requestheader-group-headers": `["delta"]`,
|
||||||
|
"requestheader-extra-headers-prefix": `["echo","foxtrot"]`,
|
||||||
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
|
"requestheader-allowed-names": `["first","second"]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectCreate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic with feature gate",
|
||||||
|
clusterAuthInfo: ClusterAuthenticationInfo{
|
||||||
|
ClientCA: someRandomCAProvider,
|
||||||
|
RequestHeaderUsernameHeaders: headerrequest.StaticStringSlice{"alfa", "bravo", "charlie"},
|
||||||
|
RequestHeaderUIDHeaders: headerrequest.StaticStringSlice{"golf", "hotel", "india"},
|
||||||
|
RequestHeaderGroupHeaders: headerrequest.StaticStringSlice{"delta"},
|
||||||
|
RequestHeaderExtraHeaderPrefixes: headerrequest.StaticStringSlice{"echo", "foxtrot"},
|
||||||
|
RequestHeaderCA: anotherRandomCAProvider,
|
||||||
|
RequestHeaderAllowedNames: headerrequest.StaticStringSlice{"first", "second"},
|
||||||
|
},
|
||||||
expectedConfigMaps: map[string]*corev1.ConfigMap{
|
expectedConfigMaps: map[string]*corev1.ConfigMap{
|
||||||
"extension-apiserver-authentication": {
|
"extension-apiserver-authentication": {
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
@ -122,6 +152,7 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectCreate: true,
|
expectCreate: true,
|
||||||
|
uidGate: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "skip extension-apiserver-authentication",
|
name: "skip extension-apiserver-authentication",
|
||||||
@ -134,7 +165,6 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"requestheader-username-headers": `[]`,
|
"requestheader-username-headers": `[]`,
|
||||||
"requestheader-uid-headers": `[]`,
|
|
||||||
"requestheader-group-headers": `[]`,
|
"requestheader-group-headers": `[]`,
|
||||||
"requestheader-extra-headers-prefix": `[]`,
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
"requestheader-client-ca-file": string(anotherRandomCA),
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
@ -169,7 +199,6 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"requestheader-username-headers": `[]`,
|
"requestheader-username-headers": `[]`,
|
||||||
"requestheader-uid-headers": `[]`,
|
|
||||||
"requestheader-group-headers": `[]`,
|
"requestheader-group-headers": `[]`,
|
||||||
"requestheader-extra-headers-prefix": `[]`,
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
"requestheader-client-ca-file": string(anotherRandomCA),
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
@ -205,7 +234,6 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
name: "overwrite extension-apiserver-authentication requestheader",
|
name: "overwrite extension-apiserver-authentication requestheader",
|
||||||
clusterAuthInfo: ClusterAuthenticationInfo{
|
clusterAuthInfo: ClusterAuthenticationInfo{
|
||||||
RequestHeaderUsernameHeaders: headerrequest.StaticStringSlice{},
|
RequestHeaderUsernameHeaders: headerrequest.StaticStringSlice{},
|
||||||
RequestHeaderUIDHeaders: headerrequest.StaticStringSlice{},
|
|
||||||
RequestHeaderGroupHeaders: headerrequest.StaticStringSlice{},
|
RequestHeaderGroupHeaders: headerrequest.StaticStringSlice{},
|
||||||
RequestHeaderExtraHeaderPrefixes: headerrequest.StaticStringSlice{},
|
RequestHeaderExtraHeaderPrefixes: headerrequest.StaticStringSlice{},
|
||||||
RequestHeaderCA: anotherRandomCAProvider,
|
RequestHeaderCA: anotherRandomCAProvider,
|
||||||
@ -216,7 +244,6 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"requestheader-username-headers": `[]`,
|
"requestheader-username-headers": `[]`,
|
||||||
"requestheader-uid-headers": `[]`,
|
|
||||||
"requestheader-group-headers": `[]`,
|
"requestheader-group-headers": `[]`,
|
||||||
"requestheader-extra-headers-prefix": `[]`,
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
"requestheader-client-ca-file": string(someRandomCA),
|
"requestheader-client-ca-file": string(someRandomCA),
|
||||||
@ -229,7 +256,6 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"requestheader-username-headers": `[]`,
|
"requestheader-username-headers": `[]`,
|
||||||
"requestheader-uid-headers": `[]`,
|
|
||||||
"requestheader-group-headers": `[]`,
|
"requestheader-group-headers": `[]`,
|
||||||
"requestheader-extra-headers-prefix": `[]`,
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
"requestheader-client-ca-file": string(someRandomCA) + string(anotherRandomCA),
|
"requestheader-client-ca-file": string(someRandomCA) + string(anotherRandomCA),
|
||||||
@ -260,7 +286,6 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
name: "skip on no change",
|
name: "skip on no change",
|
||||||
clusterAuthInfo: ClusterAuthenticationInfo{
|
clusterAuthInfo: ClusterAuthenticationInfo{
|
||||||
RequestHeaderUsernameHeaders: headerrequest.StaticStringSlice{},
|
RequestHeaderUsernameHeaders: headerrequest.StaticStringSlice{},
|
||||||
RequestHeaderUIDHeaders: headerrequest.StaticStringSlice{},
|
|
||||||
RequestHeaderGroupHeaders: headerrequest.StaticStringSlice{},
|
RequestHeaderGroupHeaders: headerrequest.StaticStringSlice{},
|
||||||
RequestHeaderExtraHeaderPrefixes: headerrequest.StaticStringSlice{},
|
RequestHeaderExtraHeaderPrefixes: headerrequest.StaticStringSlice{},
|
||||||
RequestHeaderCA: anotherRandomCAProvider,
|
RequestHeaderCA: anotherRandomCAProvider,
|
||||||
@ -271,7 +296,6 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"requestheader-username-headers": `[]`,
|
"requestheader-username-headers": `[]`,
|
||||||
"requestheader-uid-headers": `[]`,
|
|
||||||
"requestheader-group-headers": `[]`,
|
"requestheader-group-headers": `[]`,
|
||||||
"requestheader-extra-headers-prefix": `[]`,
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
"requestheader-client-ca-file": string(anotherRandomCA),
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
@ -282,10 +306,126 @@ func TestWriteClientCAs(t *testing.T) {
|
|||||||
expectedConfigMaps: map[string]*corev1.ConfigMap{},
|
expectedConfigMaps: map[string]*corev1.ConfigMap{},
|
||||||
expectCreate: false,
|
expectCreate: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "drop uid without feature gate",
|
||||||
|
clusterAuthInfo: ClusterAuthenticationInfo{
|
||||||
|
RequestHeaderUsernameHeaders: headerrequest.StaticStringSlice{},
|
||||||
|
RequestHeaderUIDHeaders: headerrequest.StaticStringSlice{"panda"},
|
||||||
|
RequestHeaderGroupHeaders: headerrequest.StaticStringSlice{},
|
||||||
|
RequestHeaderExtraHeaderPrefixes: headerrequest.StaticStringSlice{},
|
||||||
|
RequestHeaderCA: anotherRandomCAProvider,
|
||||||
|
RequestHeaderAllowedNames: headerrequest.StaticStringSlice{},
|
||||||
|
},
|
||||||
|
preexistingObjs: []runtime.Object{
|
||||||
|
&corev1.ConfigMap{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
|
Data: map[string]string{
|
||||||
|
"requestheader-username-headers": `[]`,
|
||||||
|
"requestheader-uid-headers": `["snorlax"]`,
|
||||||
|
"requestheader-group-headers": `[]`,
|
||||||
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
|
"requestheader-allowed-names": `[]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedConfigMaps: map[string]*corev1.ConfigMap{
|
||||||
|
"extension-apiserver-authentication": {
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
|
Data: map[string]string{
|
||||||
|
"requestheader-username-headers": `[]`,
|
||||||
|
"requestheader-group-headers": `[]`,
|
||||||
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
|
"requestheader-allowed-names": `[]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectCreate: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add uid with feature gate",
|
||||||
|
clusterAuthInfo: ClusterAuthenticationInfo{
|
||||||
|
RequestHeaderUsernameHeaders: headerrequest.StaticStringSlice{},
|
||||||
|
RequestHeaderUIDHeaders: headerrequest.StaticStringSlice{"panda"},
|
||||||
|
RequestHeaderGroupHeaders: headerrequest.StaticStringSlice{},
|
||||||
|
RequestHeaderExtraHeaderPrefixes: headerrequest.StaticStringSlice{},
|
||||||
|
RequestHeaderCA: anotherRandomCAProvider,
|
||||||
|
RequestHeaderAllowedNames: headerrequest.StaticStringSlice{},
|
||||||
|
},
|
||||||
|
preexistingObjs: []runtime.Object{
|
||||||
|
&corev1.ConfigMap{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
|
Data: map[string]string{
|
||||||
|
"requestheader-username-headers": `[]`,
|
||||||
|
"requestheader-group-headers": `[]`,
|
||||||
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
|
"requestheader-allowed-names": `[]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedConfigMaps: map[string]*corev1.ConfigMap{
|
||||||
|
"extension-apiserver-authentication": {
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
|
Data: map[string]string{
|
||||||
|
"requestheader-username-headers": `[]`,
|
||||||
|
"requestheader-uid-headers": `["panda"]`,
|
||||||
|
"requestheader-group-headers": `[]`,
|
||||||
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
|
"requestheader-allowed-names": `[]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectCreate: false,
|
||||||
|
uidGate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "append uid with feature gate",
|
||||||
|
clusterAuthInfo: ClusterAuthenticationInfo{
|
||||||
|
RequestHeaderUsernameHeaders: headerrequest.StaticStringSlice{},
|
||||||
|
RequestHeaderUIDHeaders: headerrequest.StaticStringSlice{"panda"},
|
||||||
|
RequestHeaderGroupHeaders: headerrequest.StaticStringSlice{},
|
||||||
|
RequestHeaderExtraHeaderPrefixes: headerrequest.StaticStringSlice{},
|
||||||
|
RequestHeaderCA: anotherRandomCAProvider,
|
||||||
|
RequestHeaderAllowedNames: headerrequest.StaticStringSlice{},
|
||||||
|
},
|
||||||
|
preexistingObjs: []runtime.Object{
|
||||||
|
&corev1.ConfigMap{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
|
Data: map[string]string{
|
||||||
|
"requestheader-username-headers": `[]`,
|
||||||
|
"requestheader-uid-headers": `["snorlax"]`,
|
||||||
|
"requestheader-group-headers": `[]`,
|
||||||
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
|
"requestheader-allowed-names": `[]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedConfigMaps: map[string]*corev1.ConfigMap{
|
||||||
|
"extension-apiserver-authentication": {
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
|
Data: map[string]string{
|
||||||
|
"requestheader-username-headers": `[]`,
|
||||||
|
"requestheader-uid-headers": `["snorlax","panda"]`,
|
||||||
|
"requestheader-group-headers": `[]`,
|
||||||
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
|
"requestheader-allowed-names": `[]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectCreate: false,
|
||||||
|
uidGate: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemoteRequestHeaderUID, test.uidGate)
|
||||||
|
|
||||||
client := fake.NewSimpleClientset(test.preexistingObjs...)
|
client := fake.NewSimpleClientset(test.preexistingObjs...)
|
||||||
configMapIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
configMapIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||||
for _, obj := range test.preexistingObjs {
|
for _, obj := range test.preexistingObjs {
|
||||||
@ -341,7 +481,6 @@ func TestWriteConfigMapDeleted(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"requestheader-username-headers": `[]`,
|
"requestheader-username-headers": `[]`,
|
||||||
"requestheader-uid-headers": `[]`,
|
|
||||||
"requestheader-group-headers": `[]`,
|
"requestheader-group-headers": `[]`,
|
||||||
"requestheader-extra-headers-prefix": `[]`,
|
"requestheader-extra-headers-prefix": `[]`,
|
||||||
"requestheader-client-ca-file": string(anotherRandomCA),
|
"requestheader-client-ca-file": string(anotherRandomCA),
|
||||||
|
@ -306,6 +306,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||||||
{Version: version.MustParse("1.29"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
{Version: version.MustParse("1.29"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
genericfeatures.RemoteRequestHeaderUID: {
|
||||||
|
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
|
||||||
genericfeatures.ResilientWatchCacheInitialization: {
|
genericfeatures.ResilientWatchCacheInitialization: {
|
||||||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
||||||
},
|
},
|
||||||
|
@ -149,6 +149,13 @@ const (
|
|||||||
// to a chunking list request.
|
// to a chunking list request.
|
||||||
RemainingItemCount featuregate.Feature = "RemainingItemCount"
|
RemainingItemCount featuregate.Feature = "RemainingItemCount"
|
||||||
|
|
||||||
|
// owner: @stlaz
|
||||||
|
//
|
||||||
|
// Enable kube-apiserver to accept UIDs via request header authentication.
|
||||||
|
// This will also make the kube-apiserver's API aggregator add UIDs via standard
|
||||||
|
// headers when forwarding requests to the servers serving the aggregated API.
|
||||||
|
RemoteRequestHeaderUID featuregate.Feature = "RemoteRequestHeaderUID"
|
||||||
|
|
||||||
// owner: @wojtek-t
|
// owner: @wojtek-t
|
||||||
//
|
//
|
||||||
// Enables resilient watchcache initialization to avoid controlplane
|
// Enables resilient watchcache initialization to avoid controlplane
|
||||||
@ -359,6 +366,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||||||
{Version: version.MustParse("1.29"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
{Version: version.MustParse("1.29"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
RemoteRequestHeaderUID: {
|
||||||
|
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
|
||||||
ResilientWatchCacheInitialization: {
|
ResilientWatchCacheInitialization: {
|
||||||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
||||||
},
|
},
|
||||||
|
@ -29,8 +29,10 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/apis/apiserver"
|
"k8s.io/apiserver/pkg/apis/apiserver"
|
||||||
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
||||||
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/server"
|
"k8s.io/apiserver/pkg/server"
|
||||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@ -68,9 +70,6 @@ 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)
|
||||||
}
|
}
|
||||||
@ -84,10 +83,6 @@ 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")
|
||||||
}
|
}
|
||||||
@ -95,6 +90,20 @@ func (s *RequestHeaderAuthenticationOptions) Validate() []error {
|
|||||||
klog.Warningf("--requestheader-extra-headers-prefix is set without specifying the standard X-Remote-Extra- header prefix - API aggregation will not work")
|
klog.Warningf("--requestheader-extra-headers-prefix is set without specifying the standard X-Remote-Extra- header prefix - API aggregation will not work")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.RemoteRequestHeaderUID) {
|
||||||
|
if len(s.UIDHeaders) > 0 {
|
||||||
|
allErrors = append(allErrors, fmt.Errorf("--requestheader-uid-headers requires the %q feature to be enabled", features.RemoteRequestHeaderUID))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := checkForWhiteSpaceOnly("requestheader-uid-headers", s.UIDHeaders...); err != nil {
|
||||||
|
allErrors = append(allErrors, err)
|
||||||
|
}
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return allErrors
|
return allErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +135,7 @@ func (s *RequestHeaderAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
"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, ""+
|
fs.StringSliceVar(&s.UIDHeaders, "requestheader-uid-headers", s.UIDHeaders, ""+
|
||||||
"List of request headers to inspect for UIDs. X-Remote-Uid is suggested.")
|
"List of request headers to inspect for UIDs. X-Remote-Uid is suggested. Requires the RemoteRequestHeaderUID feature to be enabled.")
|
||||||
|
|
||||||
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.")
|
||||||
|
@ -99,7 +99,7 @@ func SetFeatureGateEmulationVersionDuringTest(tb TB, gate featuregate.FeatureGat
|
|||||||
detectParallelOverrideCleanup := detectParallelOverrideEmulationVersion(tb, ver)
|
detectParallelOverrideCleanup := detectParallelOverrideEmulationVersion(tb, ver)
|
||||||
originalEmuVer := gate.(featuregate.MutableVersionedFeatureGate).EmulationVersion()
|
originalEmuVer := gate.(featuregate.MutableVersionedFeatureGate).EmulationVersion()
|
||||||
if err := gate.(featuregate.MutableVersionedFeatureGate).SetEmulationVersion(ver); err != nil {
|
if err := gate.(featuregate.MutableVersionedFeatureGate).SetEmulationVersion(ver); err != nil {
|
||||||
tb.Fatalf("failed to set emulation version to %s during test", ver.String())
|
tb.Fatalf("failed to set emulation version to %s during test: %v", ver.String(), err)
|
||||||
}
|
}
|
||||||
tb.Cleanup(func() {
|
tb.Cleanup(func() {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
@ -159,7 +159,12 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
proxyRoundTripper := handlingInfo.proxyRoundTripper
|
proxyRoundTripper := handlingInfo.proxyRoundTripper
|
||||||
upgrade := httpstream.IsUpgradeRequest(req)
|
upgrade := httpstream.IsUpgradeRequest(req)
|
||||||
|
|
||||||
proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), user.GetUID(), user.GetGroups(), user.GetExtra(), proxyRoundTripper)
|
var userUID string
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.RemoteRequestHeaderUID) {
|
||||||
|
userUID = user.GetUID()
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), userUID, user.GetGroups(), user.GetExtra(), proxyRoundTripper)
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) && !upgrade {
|
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) && !upgrade {
|
||||||
tracingWrapper := tracing.WrapperFor(r.tracerProvider)
|
tracingWrapper := tracing.WrapperFor(r.tracerProvider)
|
||||||
@ -170,7 +175,7 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
// NOT use the proxyRoundTripper. It's a direct dial that bypasses the proxyRoundTripper. This means that we have to
|
// NOT use the proxyRoundTripper. It's a direct dial that bypasses the proxyRoundTripper. This means that we have to
|
||||||
// attach the "correct" user headers to the request ahead of time.
|
// attach the "correct" user headers to the request ahead of time.
|
||||||
if upgrade {
|
if upgrade {
|
||||||
transport.SetAuthProxyHeaders(newReq, user.GetName(), user.GetUID(), user.GetGroups(), user.GetExtra())
|
transport.SetAuthProxyHeaders(newReq, user.GetName(), userUID, user.GetGroups(), user.GetExtra())
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w})
|
handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w})
|
||||||
|
@ -34,31 +34,34 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/apiserver/pkg/audit"
|
"github.com/google/go-cmp/cmp"
|
||||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
|
||||||
"k8s.io/client-go/transport"
|
|
||||||
|
|
||||||
"golang.org/x/net/websocket"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/propagation"
|
"go.opentelemetry.io/otel/propagation"
|
||||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
"golang.org/x/net/websocket"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||||
"k8s.io/apimachinery/pkg/util/proxy"
|
"k8s.io/apimachinery/pkg/util/proxy"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
"k8s.io/apiserver/pkg/audit"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/endpoints/filters"
|
"k8s.io/apiserver/pkg/endpoints/filters"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
|
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||||
"k8s.io/apiserver/pkg/server/egressselector"
|
"k8s.io/apiserver/pkg/server/egressselector"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
utilflowcontrol "k8s.io/apiserver/pkg/util/flowcontrol"
|
utilflowcontrol "k8s.io/apiserver/pkg/util/flowcontrol"
|
||||||
apiserverproxyutil "k8s.io/apiserver/pkg/util/proxy"
|
apiserverproxyutil "k8s.io/apiserver/pkg/util/proxy"
|
||||||
|
"k8s.io/client-go/transport"
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
"k8s.io/component-base/metrics"
|
"k8s.io/component-base/metrics"
|
||||||
"k8s.io/component-base/metrics/legacyregistry"
|
"k8s.io/component-base/metrics/legacyregistry"
|
||||||
apiregistration "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
|
apiregistration "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
|
||||||
"k8s.io/utils/pointer"
|
|
||||||
"k8s.io/utils/ptr"
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -130,6 +133,8 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
expectedBody string
|
expectedBody string
|
||||||
expectedCalled bool
|
expectedCalled bool
|
||||||
expectedHeaders map[string][]string
|
expectedHeaders map[string][]string
|
||||||
|
|
||||||
|
enableFeatureGates []featuregate.Feature
|
||||||
}{
|
}{
|
||||||
"no target": {
|
"no target": {
|
||||||
expectedStatusCode: http.StatusNotFound,
|
expectedStatusCode: http.StatusNotFound,
|
||||||
@ -138,7 +143,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
apiService: &apiregistration.APIService{
|
apiService: &apiregistration.APIService{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
Spec: apiregistration.APIServiceSpec{
|
Spec: apiregistration.APIServiceSpec{
|
||||||
Service: &apiregistration.ServiceReference{Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Port: ptr.To[int32](443)},
|
||||||
Group: "foo",
|
Group: "foo",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
},
|
},
|
||||||
@ -161,7 +166,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
apiService: &apiregistration.APIService{
|
apiService: &apiregistration.APIService{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
Spec: apiregistration.APIServiceSpec{
|
Spec: apiregistration.APIServiceSpec{
|
||||||
Service: &apiregistration.ServiceReference{Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Port: ptr.To[int32](443)},
|
||||||
Group: "foo",
|
Group: "foo",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
InsecureSkipTLSVerify: true,
|
InsecureSkipTLSVerify: true,
|
||||||
@ -174,6 +179,40 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectedStatusCode: http.StatusOK,
|
expectedStatusCode: http.StatusOK,
|
||||||
expectedCalled: true,
|
expectedCalled: true,
|
||||||
|
expectedHeaders: map[string][]string{
|
||||||
|
"X-Forwarded-Proto": {"https"},
|
||||||
|
"X-Forwarded-Uri": {"/request/path"},
|
||||||
|
"X-Forwarded-For": {"127.0.0.1"},
|
||||||
|
"X-Remote-User": {"username"},
|
||||||
|
"User-Agent": {"Go-http-client/1.1"},
|
||||||
|
"Accept-Encoding": {"gzip"},
|
||||||
|
"X-Remote-Group": {"one", "two"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"[RemoteRequestHeaderUID] proxy with user, insecure": {
|
||||||
|
user: &user.DefaultInfo{
|
||||||
|
Name: "username",
|
||||||
|
UID: "6b60d791-1af9-4513-92e5-e4252a1e0a78",
|
||||||
|
Groups: []string{"one", "two"},
|
||||||
|
},
|
||||||
|
path: "/request/path",
|
||||||
|
apiService: &apiregistration.APIService{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
|
Spec: apiregistration.APIServiceSpec{
|
||||||
|
Service: &apiregistration.ServiceReference{Port: ptr.To[int32](443)},
|
||||||
|
Group: "foo",
|
||||||
|
Version: "v1",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
},
|
||||||
|
Status: apiregistration.APIServiceStatus{
|
||||||
|
Conditions: []apiregistration.APIServiceCondition{
|
||||||
|
{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enableFeatureGates: []featuregate.Feature{features.RemoteRequestHeaderUID},
|
||||||
|
expectedStatusCode: http.StatusOK,
|
||||||
|
expectedCalled: true,
|
||||||
expectedHeaders: map[string][]string{
|
expectedHeaders: map[string][]string{
|
||||||
"X-Forwarded-Proto": {"https"},
|
"X-Forwarded-Proto": {"https"},
|
||||||
"X-Forwarded-Uri": {"/request/path"},
|
"X-Forwarded-Uri": {"/request/path"},
|
||||||
@ -195,7 +234,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
apiService: &apiregistration.APIService{
|
apiService: &apiregistration.APIService{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
Spec: apiregistration.APIServiceSpec{
|
Spec: apiregistration.APIServiceSpec{
|
||||||
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: ptr.To[int32](443)},
|
||||||
Group: "foo",
|
Group: "foo",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
CABundle: testCACrt,
|
CABundle: testCACrt,
|
||||||
@ -208,6 +247,40 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectedStatusCode: http.StatusOK,
|
expectedStatusCode: http.StatusOK,
|
||||||
expectedCalled: true,
|
expectedCalled: true,
|
||||||
|
expectedHeaders: map[string][]string{
|
||||||
|
"X-Forwarded-Proto": {"https"},
|
||||||
|
"X-Forwarded-Uri": {"/request/path"},
|
||||||
|
"X-Forwarded-For": {"127.0.0.1"},
|
||||||
|
"X-Remote-User": {"username"},
|
||||||
|
"User-Agent": {"Go-http-client/1.1"},
|
||||||
|
"Accept-Encoding": {"gzip"},
|
||||||
|
"X-Remote-Group": {"one", "two"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"[RemoteRequestHeaderUID] proxy with user, cabundle": {
|
||||||
|
user: &user.DefaultInfo{
|
||||||
|
Name: "username",
|
||||||
|
UID: "6b60d791-1af9-4513-92e5-e4252a1e0a78",
|
||||||
|
Groups: []string{"one", "two"},
|
||||||
|
},
|
||||||
|
path: "/request/path",
|
||||||
|
apiService: &apiregistration.APIService{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
|
Spec: apiregistration.APIServiceSpec{
|
||||||
|
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: ptr.To[int32](443)},
|
||||||
|
Group: "foo",
|
||||||
|
Version: "v1",
|
||||||
|
CABundle: testCACrt,
|
||||||
|
},
|
||||||
|
Status: apiregistration.APIServiceStatus{
|
||||||
|
Conditions: []apiregistration.APIServiceCondition{
|
||||||
|
{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enableFeatureGates: []featuregate.Feature{features.RemoteRequestHeaderUID},
|
||||||
|
expectedStatusCode: http.StatusOK,
|
||||||
|
expectedCalled: true,
|
||||||
expectedHeaders: map[string][]string{
|
expectedHeaders: map[string][]string{
|
||||||
"X-Forwarded-Proto": {"https"},
|
"X-Forwarded-Proto": {"https"},
|
||||||
"X-Forwarded-Uri": {"/request/path"},
|
"X-Forwarded-Uri": {"/request/path"},
|
||||||
@ -229,7 +302,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
apiService: &apiregistration.APIService{
|
apiService: &apiregistration.APIService{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
Spec: apiregistration.APIServiceSpec{
|
Spec: apiregistration.APIServiceSpec{
|
||||||
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: ptr.To[int32](443)},
|
||||||
Group: "foo",
|
Group: "foo",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
CABundle: testCACrt,
|
CABundle: testCACrt,
|
||||||
@ -253,7 +326,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
apiService: &apiregistration.APIService{
|
apiService: &apiregistration.APIService{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
Spec: apiregistration.APIServiceSpec{
|
Spec: apiregistration.APIServiceSpec{
|
||||||
Service: &apiregistration.ServiceReference{Name: "bad-service", Namespace: "test-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "bad-service", Namespace: "test-ns", Port: ptr.To[int32](443)},
|
||||||
Group: "foo",
|
Group: "foo",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
CABundle: testCACrt,
|
CABundle: testCACrt,
|
||||||
@ -276,7 +349,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
apiService: &apiregistration.APIService{
|
apiService: &apiregistration.APIService{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
Spec: apiregistration.APIServiceSpec{
|
Spec: apiregistration.APIServiceSpec{
|
||||||
Service: &apiregistration.ServiceReference{Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Port: ptr.To[int32](443)},
|
||||||
Group: "foo",
|
Group: "foo",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
},
|
},
|
||||||
@ -298,7 +371,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
apiService: &apiregistration.APIService{
|
apiService: &apiregistration.APIService{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
Spec: apiregistration.APIServiceSpec{
|
Spec: apiregistration.APIServiceSpec{
|
||||||
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: ptr.To[int32](443)},
|
||||||
Group: "foo",
|
Group: "foo",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
CABundle: testCACrt,
|
CABundle: testCACrt,
|
||||||
@ -320,7 +393,11 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
target.Reset()
|
target.Reset()
|
||||||
legacyregistry.Reset()
|
legacyregistry.Reset()
|
||||||
|
|
||||||
func() {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
for _, f := range tc.enableFeatureGates {
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, f, true)
|
||||||
|
}
|
||||||
|
|
||||||
targetServer := httptest.NewUnstartedServer(target)
|
targetServer := httptest.NewUnstartedServer(target)
|
||||||
serviceCert := tc.serviceCertOverride
|
serviceCert := tc.serviceCertOverride
|
||||||
if serviceCert == nil {
|
if serviceCert == nil {
|
||||||
@ -354,37 +431,37 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := http.Get(server.URL + tc.path)
|
resp, err := http.Get(server.URL + tc.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s: %v", name, err)
|
t.Errorf("%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if e, a := tc.expectedStatusCode, resp.StatusCode; e != a {
|
if e, a := tc.expectedStatusCode, resp.StatusCode; e != a {
|
||||||
body, _ := httputil.DumpResponse(resp, true)
|
body, _ := httputil.DumpResponse(resp, true)
|
||||||
t.Logf("%s: %v", name, string(body))
|
t.Logf("%v", string(body))
|
||||||
t.Errorf("%s: expected %v, got %v", name, e, a)
|
t.Errorf("expected %v, got %v", e, a)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bytes, err := io.ReadAll(resp.Body)
|
bytes, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s: %v", name, err)
|
t.Errorf("%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !strings.Contains(string(bytes), tc.expectedBody) {
|
if !strings.Contains(string(bytes), tc.expectedBody) {
|
||||||
t.Errorf("%s: expected %q, got %q", name, tc.expectedBody, string(bytes))
|
t.Errorf("expected %q, got %q", tc.expectedBody, string(bytes))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if e, a := tc.expectedCalled, target.called; e != a {
|
if e, a := tc.expectedCalled, target.called; e != a {
|
||||||
t.Errorf("%s: expected %v, got %v", name, e, a)
|
t.Errorf("expected %v, got %v", e, a)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// this varies every test
|
// this varies every test
|
||||||
delete(target.headers, "X-Forwarded-Host")
|
delete(target.headers, "X-Forwarded-Host")
|
||||||
if e, a := tc.expectedHeaders, target.headers; !reflect.DeepEqual(e, a) {
|
if e, a := tc.expectedHeaders, target.headers; !reflect.DeepEqual(e, a) {
|
||||||
t.Errorf("%s: expected %v, got %v", name, e, a)
|
t.Errorf("expected != got %v", cmp.Diff(e, a))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if e, a := targetServer.Listener.Addr().String(), target.host; tc.expectedCalled && !reflect.DeepEqual(e, a) {
|
if e, a := targetServer.Listener.Addr().String(), target.host; tc.expectedCalled && !reflect.DeepEqual(e, a) {
|
||||||
t.Errorf("%s: expected %v, got %v", name, e, a)
|
t.Errorf("expected %v, got %v", e, a)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +474,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
t.Errorf("expected the x509_missing_san_total to be 1, but it's %d", errorCounter)
|
t.Errorf("expected the x509_missing_san_total to be 1, but it's %d", errorCounter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,6 +509,8 @@ func newBrokenDialerAndSelector() (*mockEgressDialer, *egressselector.EgressSele
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyUpgrade(t *testing.T) {
|
func TestProxyUpgrade(t *testing.T) {
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemoteRequestHeaderUID, true)
|
||||||
|
|
||||||
upgradeUser := "upgradeUser"
|
upgradeUser := "upgradeUser"
|
||||||
upgradeUID := "upgradeUser-UID"
|
upgradeUID := "upgradeUser-UID"
|
||||||
testcases := map[string]struct {
|
testcases := map[string]struct {
|
||||||
@ -446,7 +525,7 @@ func TestProxyUpgrade(t *testing.T) {
|
|||||||
CABundle: testCACrt,
|
CABundle: testCACrt,
|
||||||
Group: "mygroup",
|
Group: "mygroup",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: ptr.To[int32](443)},
|
||||||
},
|
},
|
||||||
Status: apiregistration.APIServiceStatus{
|
Status: apiregistration.APIServiceStatus{
|
||||||
Conditions: []apiregistration.APIServiceCondition{
|
Conditions: []apiregistration.APIServiceCondition{
|
||||||
@ -463,7 +542,7 @@ func TestProxyUpgrade(t *testing.T) {
|
|||||||
InsecureSkipTLSVerify: true,
|
InsecureSkipTLSVerify: true,
|
||||||
Group: "mygroup",
|
Group: "mygroup",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Service: &apiregistration.ServiceReference{Name: "invalid-service", Namespace: "invalid-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "invalid-service", Namespace: "invalid-ns", Port: ptr.To[int32](443)},
|
||||||
},
|
},
|
||||||
Status: apiregistration.APIServiceStatus{
|
Status: apiregistration.APIServiceStatus{
|
||||||
Conditions: []apiregistration.APIServiceCondition{
|
Conditions: []apiregistration.APIServiceCondition{
|
||||||
@ -480,7 +559,7 @@ func TestProxyUpgrade(t *testing.T) {
|
|||||||
CABundle: testCACrt,
|
CABundle: testCACrt,
|
||||||
Group: "mygroup",
|
Group: "mygroup",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Service: &apiregistration.ServiceReference{Name: "invalid-service", Namespace: "invalid-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "invalid-service", Namespace: "invalid-ns", Port: ptr.To[int32](443)},
|
||||||
},
|
},
|
||||||
Status: apiregistration.APIServiceStatus{
|
Status: apiregistration.APIServiceStatus{
|
||||||
Conditions: []apiregistration.APIServiceCondition{
|
Conditions: []apiregistration.APIServiceCondition{
|
||||||
@ -497,7 +576,7 @@ func TestProxyUpgrade(t *testing.T) {
|
|||||||
CABundle: testCACrt,
|
CABundle: testCACrt,
|
||||||
Group: "mygroup",
|
Group: "mygroup",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: ptr.To[int32](443)},
|
||||||
},
|
},
|
||||||
Status: apiregistration.APIServiceStatus{
|
Status: apiregistration.APIServiceStatus{
|
||||||
Conditions: []apiregistration.APIServiceCondition{
|
Conditions: []apiregistration.APIServiceCondition{
|
||||||
@ -515,7 +594,7 @@ func TestProxyUpgrade(t *testing.T) {
|
|||||||
CABundle: testCACrt,
|
CABundle: testCACrt,
|
||||||
Group: "mygroup",
|
Group: "mygroup",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns", Port: ptr.To[int32](443)},
|
||||||
},
|
},
|
||||||
Status: apiregistration.APIServiceStatus{
|
Status: apiregistration.APIServiceStatus{
|
||||||
Conditions: []apiregistration.APIServiceCondition{
|
Conditions: []apiregistration.APIServiceCondition{
|
||||||
@ -996,7 +1075,7 @@ func TestProxyCertReload(t *testing.T) {
|
|||||||
apiService := &apiregistration.APIService{
|
apiService := &apiregistration.APIService{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
Spec: apiregistration.APIServiceSpec{
|
Spec: apiregistration.APIServiceSpec{
|
||||||
Service: &apiregistration.ServiceReference{Name: "test-service2", Namespace: "test-ns", Port: pointer.Int32Ptr(443)},
|
Service: &apiregistration.ServiceReference{Name: "test-service2", Namespace: "test-ns", Port: ptr.To[int32](443)},
|
||||||
Group: "foo",
|
Group: "foo",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
CABundle: backendCaCertificate(), // used to validate backendCertificate()
|
CABundle: backendCaCertificate(), // used to validate backendCertificate()
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
ctx := genericapiserver.SetupSignalContext()
|
ctx := genericapiserver.SetupSignalContext()
|
||||||
options := server.NewWardleServerOptions(os.Stdout, os.Stderr)
|
options := server.NewWardleServerOptions(os.Stdout, os.Stderr)
|
||||||
cmd := server.NewCommandStartWardleServer(ctx, options)
|
cmd := server.NewCommandStartWardleServer(ctx, options, false)
|
||||||
code := cli.Run(cmd)
|
code := cli.Run(cmd)
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
@ -90,12 +90,15 @@ func NewWardleServerOptions(out, errOut io.Writer) *WardleServerOptions {
|
|||||||
|
|
||||||
// NewCommandStartWardleServer provides a CLI handler for 'start master' command
|
// NewCommandStartWardleServer provides a CLI handler for 'start master' command
|
||||||
// with a default WardleServerOptions.
|
// with a default WardleServerOptions.
|
||||||
func NewCommandStartWardleServer(ctx context.Context, defaults *WardleServerOptions) *cobra.Command {
|
func NewCommandStartWardleServer(ctx context.Context, defaults *WardleServerOptions, skipDefaultComponentGlobalsRegistrySet bool) *cobra.Command {
|
||||||
o := *defaults
|
o := *defaults
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Short: "Launch a wardle API server",
|
Short: "Launch a wardle API server",
|
||||||
Long: "Launch a wardle API server",
|
Long: "Launch a wardle API server",
|
||||||
PersistentPreRunE: func(*cobra.Command, []string) error {
|
PersistentPreRunE: func(*cobra.Command, []string) error {
|
||||||
|
if skipDefaultComponentGlobalsRegistrySet {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return featuregate.DefaultComponentGlobalsRegistry.Set()
|
return featuregate.DefaultComponentGlobalsRegistry.Set()
|
||||||
},
|
},
|
||||||
RunE: func(c *cobra.Command, args []string) error {
|
RunE: func(c *cobra.Command, args []string) error {
|
||||||
|
@ -1058,6 +1058,12 @@
|
|||||||
lockToDefault: true
|
lockToDefault: true
|
||||||
preRelease: GA
|
preRelease: GA
|
||||||
version: "1.29"
|
version: "1.29"
|
||||||
|
- name: RemoteRequestHeaderUID
|
||||||
|
versionedSpecs:
|
||||||
|
- default: false
|
||||||
|
lockToDefault: false
|
||||||
|
preRelease: Alpha
|
||||||
|
version: "1.32"
|
||||||
- name: ResilientWatchCacheInitialization
|
- name: ResilientWatchCacheInitialization
|
||||||
versionedSpecs:
|
versionedSpecs:
|
||||||
- default: true
|
- default: true
|
||||||
|
@ -27,11 +27,14 @@ import (
|
|||||||
|
|
||||||
authnv1 "k8s.io/api/authentication/v1"
|
authnv1 "k8s.io/api/authentication/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
"k8s.io/client-go/util/keyutil"
|
"k8s.io/client-go/util/keyutil"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
testutils "k8s.io/kubernetes/test/utils"
|
testutils "k8s.io/kubernetes/test/utils"
|
||||||
@ -39,6 +42,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestAuthnToKAS(t *testing.T) {
|
func TestAuthnToKAS(t *testing.T) {
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemoteRequestHeaderUID, true)
|
||||||
|
|
||||||
tCtx := ktesting.Init(t)
|
tCtx := ktesting.Init(t)
|
||||||
|
|
||||||
frontProxyCA, frontProxyClient, frontProxyKey, err := newTestCAWithClient(
|
frontProxyCA, frontProxyClient, frontProxyKey, err := newTestCAWithClient(
|
||||||
|
@ -45,8 +45,10 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||||
genericapiserveroptions "k8s.io/apiserver/pkg/server/options"
|
genericapiserveroptions "k8s.io/apiserver/pkg/server/options"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
client "k8s.io/client-go/kubernetes"
|
client "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@ -54,6 +56,7 @@ import (
|
|||||||
"k8s.io/client-go/transport"
|
"k8s.io/client-go/transport"
|
||||||
"k8s.io/client-go/util/cert"
|
"k8s.io/client-go/util/cert"
|
||||||
"k8s.io/component-base/featuregate"
|
"k8s.io/component-base/featuregate"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
utilversion "k8s.io/component-base/version"
|
utilversion "k8s.io/component-base/version"
|
||||||
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
|
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
|
||||||
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
|
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
|
||||||
@ -260,6 +263,7 @@ func TestFrontProxyConfig(t *testing.T) {
|
|||||||
testFrontProxyConfig(t, false)
|
testFrontProxyConfig(t, false)
|
||||||
})
|
})
|
||||||
t.Run("WithUID", func(t *testing.T) {
|
t.Run("WithUID", func(t *testing.T) {
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemoteRequestHeaderUID, true)
|
||||||
testFrontProxyConfig(t, true)
|
testFrontProxyConfig(t, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -282,7 +286,7 @@ func testFrontProxyConfig(t *testing.T, withUID bool) {
|
|||||||
kubeBinaryVersion := sampleserver.WardleVersionToKubeVersion(version.MustParse(wardleBinaryVersion)).String()
|
kubeBinaryVersion := sampleserver.WardleVersionToKubeVersion(version.MustParse(wardleBinaryVersion)).String()
|
||||||
|
|
||||||
// start up the KAS and prepare the options for the wardle API server
|
// start up the KAS and prepare the options for the wardle API server
|
||||||
testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, kubeBinaryVersion, wardleBinaryVersion, extraKASFlags)
|
testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, kubeBinaryVersion, wardleBinaryVersion, extraKASFlags, withUID)
|
||||||
kubeConfig := getKubeConfig(testKAS)
|
kubeConfig := getKubeConfig(testKAS)
|
||||||
|
|
||||||
// create the SA that we will use to query the aggregated API
|
// create the SA that we will use to query the aggregated API
|
||||||
@ -337,12 +341,12 @@ func testFrontProxyConfig(t *testing.T, withUID bool) {
|
|||||||
transport.WrapperFunc(func(rt http.RoundTripper) http.RoundTripper {
|
transport.WrapperFunc(func(rt http.RoundTripper) http.RoundTripper {
|
||||||
return roundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
return roundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
gotUser, ok := genericapirequest.UserFrom(req.Context())
|
gotUser, ok := genericapirequest.UserFrom(req.Context())
|
||||||
if !ok || gotUser.GetName() == "system:anonymous" {
|
if !ok {
|
||||||
return nil, fmt.Errorf("got an unauthenticated request")
|
return nil, fmt.Errorf("got an unauthenticated request")
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is likely the KAS checking the OpenAPI endpoints
|
// this is likely the KAS checking the OpenAPI endpoints
|
||||||
if gotUser.GetName() == "system:anonymous" || gotUser.GetName() == "system:aggregator" {
|
if gotUser.GetName() == "system:anonymous" || gotUser.GetName() == "system:aggregator" || gotUser.GetName() == "system:kube-aggregator" {
|
||||||
return rt.RoundTrip(req)
|
return rt.RoundTrip(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,7 +372,7 @@ func testFrontProxyConfig(t *testing.T, withUID bool) {
|
|||||||
wardleCertDir, _ := os.MkdirTemp("", "test-integration-wardle-server")
|
wardleCertDir, _ := os.MkdirTemp("", "test-integration-wardle-server")
|
||||||
defer os.RemoveAll(wardleCertDir)
|
defer os.RemoveAll(wardleCertDir)
|
||||||
|
|
||||||
runPreparedWardleServer(ctx, t, wardleOptions, wardleCertDir, wardlePort, false, true, wardleBinaryVersion, kubeConfig)
|
runPreparedWardleServer(ctx, t, wardleOptions, wardleCertDir, wardlePort, false, true, wardleBinaryVersion, kubeConfig, withUID)
|
||||||
waitForWardleAPIServiceReady(ctx, t, kubeConfig, wardleCertDir, testNamespace)
|
waitForWardleAPIServiceReady(ctx, t, kubeConfig, wardleCertDir, testNamespace)
|
||||||
|
|
||||||
// get the wardle API client using our SA token
|
// get the wardle API client using our SA token
|
||||||
@ -401,13 +405,13 @@ func testAggregatedAPIServer(t *testing.T, setWardleFeatureGate, banFlunder bool
|
|||||||
// each wardle binary is bundled with a specific kube binary.
|
// each wardle binary is bundled with a specific kube binary.
|
||||||
kubeBinaryVersion := sampleserver.WardleVersionToKubeVersion(version.MustParse(wardleBinaryVersion)).String()
|
kubeBinaryVersion := sampleserver.WardleVersionToKubeVersion(version.MustParse(wardleBinaryVersion)).String()
|
||||||
|
|
||||||
testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, kubeBinaryVersion, wardleBinaryVersion, nil)
|
testKAS, wardleOptions, wardlePort := prepareAggregatedWardleAPIServer(ctx, t, testNamespace, kubeBinaryVersion, wardleBinaryVersion, nil, false)
|
||||||
kubeClientConfig := getKubeConfig(testKAS)
|
kubeClientConfig := getKubeConfig(testKAS)
|
||||||
|
|
||||||
wardleCertDir, _ := os.MkdirTemp("", "test-integration-wardle-server")
|
wardleCertDir, _ := os.MkdirTemp("", "test-integration-wardle-server")
|
||||||
defer os.RemoveAll(wardleCertDir)
|
defer os.RemoveAll(wardleCertDir)
|
||||||
|
|
||||||
directWardleClientConfig := runPreparedWardleServer(ctx, t, wardleOptions, wardleCertDir, wardlePort, setWardleFeatureGate, banFlunder, wardleEmulationVersion, kubeClientConfig)
|
directWardleClientConfig := runPreparedWardleServer(ctx, t, wardleOptions, wardleCertDir, wardlePort, setWardleFeatureGate, banFlunder, wardleEmulationVersion, kubeClientConfig, false)
|
||||||
|
|
||||||
// now we're finally ready to test. These are what's run by default now
|
// now we're finally ready to test. These are what's run by default now
|
||||||
wardleDirectClient := client.NewForConfigOrDie(directWardleClientConfig)
|
wardleDirectClient := client.NewForConfigOrDie(directWardleClientConfig)
|
||||||
@ -681,7 +685,7 @@ func TestAggregatedAPIServerRejectRedirectResponse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareAggregatedWardleAPIServer(ctx context.Context, t *testing.T, namespace, kubebinaryVersion, wardleBinaryVersion string, kubeAPIServerFlags []string) (*kastesting.TestServer, *sampleserver.WardleServerOptions, int) {
|
func prepareAggregatedWardleAPIServer(ctx context.Context, t *testing.T, namespace, kubebinaryVersion, wardleBinaryVersion string, kubeAPIServerFlags []string, withUID bool) (*kastesting.TestServer, *sampleserver.WardleServerOptions, int) {
|
||||||
// makes the kube-apiserver very responsive. it's normally a minute
|
// makes the kube-apiserver very responsive. it's normally a minute
|
||||||
dynamiccertificates.FileRefreshDuration = 1 * time.Second
|
dynamiccertificates.FileRefreshDuration = 1 * time.Second
|
||||||
|
|
||||||
@ -693,6 +697,11 @@ func prepareAggregatedWardleAPIServer(ctx context.Context, t *testing.T, namespa
|
|||||||
// endpoints cannot have loopback IPs so we need to override the resolver itself
|
// endpoints cannot have loopback IPs so we need to override the resolver itself
|
||||||
t.Cleanup(app.SetServiceResolverForTests(staticURLServiceResolver(fmt.Sprintf("https://127.0.0.1:%d", wardlePort))))
|
t.Cleanup(app.SetServiceResolverForTests(staticURLServiceResolver(fmt.Sprintf("https://127.0.0.1:%d", wardlePort))))
|
||||||
|
|
||||||
|
// TODO figure out how to actually make BinaryVersion/EmulationVersion work with Wardle and KAS at the same time when Alpha FG are being set
|
||||||
|
if withUID {
|
||||||
|
kubebinaryVersion = ""
|
||||||
|
}
|
||||||
|
|
||||||
testServer := kastesting.StartTestServerOrDie(t,
|
testServer := kastesting.StartTestServerOrDie(t,
|
||||||
&kastesting.TestServerInstanceOptions{
|
&kastesting.TestServerInstanceOptions{
|
||||||
EnableCertAuth: true,
|
EnableCertAuth: true,
|
||||||
@ -751,6 +760,7 @@ func runPreparedWardleServer(
|
|||||||
banFlunder bool,
|
banFlunder bool,
|
||||||
emulationVersion string,
|
emulationVersion string,
|
||||||
kubeConfig *rest.Config,
|
kubeConfig *rest.Config,
|
||||||
|
withUID bool,
|
||||||
) *rest.Config {
|
) *rest.Config {
|
||||||
|
|
||||||
// start the wardle server to prove we can aggregate it
|
// start the wardle server to prove we can aggregate it
|
||||||
@ -769,7 +779,8 @@ func runPreparedWardleServer(
|
|||||||
if flunderBanningFeatureGate {
|
if flunderBanningFeatureGate {
|
||||||
args = append(args, "--feature-gates", fmt.Sprintf("wardle:BanFlunder=%v", banFlunder))
|
args = append(args, "--feature-gates", fmt.Sprintf("wardle:BanFlunder=%v", banFlunder))
|
||||||
}
|
}
|
||||||
wardleCmd := sampleserver.NewCommandStartWardleServer(ctx, wardleOptions)
|
// TODO figure out how to actually make BinaryVersion/EmulationVersion work with Wardle and KAS at the same time when Alpha FG are being set
|
||||||
|
wardleCmd := sampleserver.NewCommandStartWardleServer(ctx, wardleOptions, withUID)
|
||||||
wardleCmd.SetArgs(args)
|
wardleCmd.SetArgs(args)
|
||||||
if err := wardleCmd.Execute(); err != nil {
|
if err := wardleCmd.Execute(); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
Loading…
Reference in New Issue
Block a user