CVE-2022-23652: Improper Authentication (CWE-287)
value: capsule-proxy is a reverse proxy for Capsule Operator which provides multi-tenancy in Kubernetes. In versions prior to 0.2.1 an attacker with a proper authentication mechanism may use a malicious `Connection` header to start a privilege escalation attack towards the Kubernetes API Server. This vulnerability allows for an exploit of the `cluster-admin` Role bound to `capsule-proxy`. There are no known workarounds for this issue.
CWE description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
CVSS v3 base score: 8.8
Published: 2022-02-22T20:15Z
Repository: https://github.com/clastix/capsule-proxy
Language: Other
Files changed: go.mod, internal/webserver/webserver.go
Commit date: 2022-02-20 21:45:49 +0000
Commit message: bug: cve exploiting malicious connection header

Diff: diff --git a/go.mod b/go.mod
index 62bd018..f7925c9 100644
--- a/go.mod
+++ b/go.mod
@@ -9,10 +9,11 @@ require (
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/pflag v1.0.5
 	go.uber.org/zap v1.18.1
+	golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
 	k8s.io/api v0.22.0
 	k8s.io/apimachinery v0.22.1
 	k8s.io/apiserver v0.22.0
 	k8s.io/client-go v0.22.0
 	sigs.k8s.io/controller-runtime v0.9.5
diff --git a/internal/webserver/webserver.go b/internal/webserver/webserver.go
index 174a218..3b5d026 100644
--- a/internal/webserver/webserver.go
+++ b/internal/webserver/webserver.go
@@ -8,18 +8,20 @@ import (
 	"crypto/tls"
 	"encoding/json"
 	"fmt"
 	"net/http"
 	"net/http/httputil"
+	"net/textproto"
 	"strings"
 	"time"
 
 	capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
 	"github.com/go-logr/logr"
 	"github.com/gorilla/handlers"
 	"github.com/gorilla/mux"
 	"github.com/pkg/errors"
+	"golang.org/x/net/http/httpguts"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apiserver/pkg/authentication/serviceaccount"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -176,10 +178,13 @@ func (n kubeFilter) impersonateHandler(writer http.ResponseWriter, request *http
 	n.log.V(4).Info("impersonating for the current request", "username", username, "groups", groups)
 
 	if len(n.bearerToken) > 0 {
 		request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", n.bearerToken))
 	}
+	// Dropping malicious header connection
+	// https://github.com/clastix/capsule-proxy/issues/188
+	n.removingHopByHopHeaders(request)
 
 	request.Header.Add("Impersonate-User", username)
 
 	for _, group := range groups {
 		request.Header.Add("Impersonate-Group", group)
@@ -353,5 +358,30 @@ func (n kubeFilter) getProxyTenantsForOwnerKind(ownerKind capsulev1beta1.OwnerKi
 
 	n.log.V(4).Info("Proxy tenant list", "owner", ownerKind, "name", ownerName, "tenants", tenants)
 
 	return
 }
+
+func (n *kubeFilter) removingHopByHopHeaders(request *http.Request) {
+	connectionHeaderName, upgradeHeaderName, requestUpgradeType := "connection", "upgrade", ""
+
+	if httpguts.HeaderValuesContainsToken(request.Header[connectionHeaderName], upgradeHeaderName) {
+		requestUpgradeType = request.Header.Get(upgradeHeaderName)
+	}
+	// Removing connection headers
+	for _, f := range request.Header.Values(connectionHeaderName) {
+		for _, sf := range strings.Split(f, ",") {
+			if sf = textproto.TrimString(sf); sf != "" {
+				request.Header.Del(sf)
+			}
+		}
+	}
+
+	if requestUpgradeType != "" {
+		request.Header.Set(connectionHeaderName, upgradeHeaderName)
+		request.Header.Set(upgradeHeaderName, requestUpgradeType)
+
+		return
+	}
+
+	request.Header.Del(connectionHeaderName)
+}

Fixed code: 	golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
	"net/textproto"
	"golang.org/x/net/http/httpguts"
	// Dropping malicious header connection
	// https://github.com/clastix/capsule-proxy/issues/188
	n.removingHopByHopHeaders(request)

func (n *kubeFilter) removingHopByHopHeaders(request *http.Request) {
	connectionHeaderName, upgradeHeaderName, requestUpgradeType := "connection", "upgrade", ""

	if httpguts.HeaderValuesContainsToken(request.Header[connectionHeaderName], upgradeHeaderName) {
		requestUpgradeType = request.Header.Get(upgradeHeaderName)
	}
	// Removing connection headers
	for _, f := range request.Header.Values(connectionHeaderName) {
		for _, sf := range strings.Split(f, ",") {
			if sf = textproto.TrimString(sf); sf != "" {
				request.Header.Del(sf)
			}
		}
	}

	if requestUpgradeType != "" {
		request.Header.Set(connectionHeaderName, upgradeHeaderName)
		request.Header.Set(upgradeHeaderName, requestUpgradeType)

		return
	}

	request.Header.Del(connectionHeaderName)
}