diff --git a/internal/concierge/impersonator/impersonator.go b/internal/concierge/impersonator/impersonator.go index 615e6ea8..f3a7372d 100644 --- a/internal/concierge/impersonator/impersonator.go +++ b/internal/concierge/impersonator/impersonator.go @@ -19,6 +19,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/httpstream" + utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authorization/authorizer" @@ -315,6 +316,12 @@ func newImpersonationReverseProxyFunc(restConfig *rest.Config) (func(*genericapi "isUpgradeRequest", isUpgradeRequest, ) + // do not allow the client to cause log confusion by spoofing this header + if len(r.Header.Values("X-Forwarded-For")) > 0 { + r = utilnet.CloneRequest(r) + r.Header.Del("X-Forwarded-For") + } + reverseProxy := httputil.NewSingleHostReverseProxy(serverURL) reverseProxy.Transport = rt reverseProxy.FlushInterval = 200 * time.Millisecond // the "watch" verb will not work without this line diff --git a/internal/concierge/impersonator/impersonator_test.go b/internal/concierge/impersonator/impersonator_test.go index 2999236b..7633c9df 100644 --- a/internal/concierge/impersonator/impersonator_test.go +++ b/internal/concierge/impersonator/impersonator_test.go @@ -118,6 +118,40 @@ func TestImpersonator(t *testing.T) { "Upgrade": {"spdy/3.1"}, }, }, + { + name: "happy path ignores forwarded header", + clientCert: newClientCert(t, ca, "test-username2", []string{"test-group3", "test-group4"}), + kubeAPIServerClientBearerTokenFile: "required-to-be-set", + clientMutateHeaders: func(header http.Header) { + header.Add("X-Forwarded-For", "example.com") + }, + wantKubeAPIServerRequestHeaders: http.Header{ + "Impersonate-User": {"test-username2"}, + "Impersonate-Group": {"test-group3", "test-group4", "system:authenticated"}, + "Authorization": {"Bearer some-service-account-token"}, + "User-Agent": {"test-agent"}, + "Accept": {"application/vnd.kubernetes.protobuf,application/json"}, + "Accept-Encoding": {"gzip"}, + "X-Forwarded-For": {"127.0.0.1"}, + }, + }, + { + name: "happy path ignores forwarded header canonicalization", + clientCert: newClientCert(t, ca, "test-username2", []string{"test-group3", "test-group4"}), + kubeAPIServerClientBearerTokenFile: "required-to-be-set", + clientMutateHeaders: func(header http.Header) { + header.Add("x-FORWARDED-for", "example.com") + }, + wantKubeAPIServerRequestHeaders: http.Header{ + "Impersonate-User": {"test-username2"}, + "Impersonate-Group": {"test-group3", "test-group4", "system:authenticated"}, + "Authorization": {"Bearer some-service-account-token"}, + "User-Agent": {"test-agent"}, + "Accept": {"application/vnd.kubernetes.protobuf,application/json"}, + "Accept-Encoding": {"gzip"}, + "X-Forwarded-For": {"127.0.0.1"}, + }, + }, { name: "user is authenticated but the kube API request returns an error", kubeAPIServerStatusCode: http.StatusNotFound,