From 66cb58f2c908a098d61cd1021707705d5c0cb15d Mon Sep 17 00:00:00 2001 From: muphoff Date: Wed, 6 Dec 2023 14:48:45 +0100 Subject: [PATCH] core: pin request host --- pkg/annotations/annotations_test.go | 29 +++++--------- pkg/limitHandler/limiter_test.go | 44 ++++++++++----------- pkg/router/router_test.go | 4 +- pkg/server/server.go | 6 +-- pkg/server/server_test.go | 61 ++++++++++++++++++++++++++++- 5 files changed, 97 insertions(+), 47 deletions(-) diff --git a/pkg/annotations/annotations_test.go b/pkg/annotations/annotations_test.go index a533c60..ad59551 100644 --- a/pkg/annotations/annotations_test.go +++ b/pkg/annotations/annotations_test.go @@ -16,86 +16,77 @@ func TestAnnotations(t *testing.T) { "guardgress/limit-ip-whitelist": "127.0.0.1,127.0.0.2", } - assert.Equal( + assert.True( t, IsTlsFingerprintBlacklisted( mockAnnotations, models.ClientHelloParsed{Ja3: "d41d8cd98f00b204e9800998ecf8427a"}, ), - true, ) - assert.Equal( + assert.False( t, IsTlsFingerprintBlacklisted( mockAnnotations, models.ClientHelloParsed{Ja3: "d41d8cd98f00b204e9800998ecf8427a_false"}, ), - false, ) - assert.Equal( + assert.True( t, IsTlsFingerprintBlacklisted( mockAnnotations, models.ClientHelloParsed{Ja4: "t13d1715h2_5b57614c22b0_93c746dc12af"}, ), - true, ) - assert.Equal( + assert.False( t, IsTlsFingerprintBlacklisted( mockAnnotations, models.ClientHelloParsed{Ja4: "t13d1715h2_5b57614c22b0_93c746dc12af_false"}, ), - false, ) - assert.Equal( + assert.True( t, IsUserAgentBlacklisted( mockAnnotations, "curl/7.64.1", ), - true, ) - assert.Equal( + assert.False( t, IsUserAgentBlacklisted( mockAnnotations, "curl/7.64.1_false", ), - false, ) - assert.Equal( + assert.True( t, IsIpWhitelisted( mockAnnotations, "127.0.0.1", ), - true, ) - assert.Equal( + assert.True( t, IsIpWhitelisted( mockAnnotations, "127.0.0.2", ), - true, ) - assert.Equal( + assert.False( t, IsIpWhitelisted( mockAnnotations, "127.0.0.1_false", ), - false, ) - assert.Equal(t, AddJa3Header(mockAnnotations), true) + assert.True(t, AddJa3Header(mockAnnotations)) } diff --git a/pkg/limitHandler/limiter_test.go b/pkg/limitHandler/limiter_test.go index f5959f5..d3dd9ea 100644 --- a/pkg/limitHandler/limiter_test.go +++ b/pkg/limitHandler/limiter_test.go @@ -32,9 +32,9 @@ func TestLimiterModule5H(t *testing.T) { } if i <= 5 { - assert.Equal(t, increment.Reached, false) + assert.False(t, increment.Reached) } else { - assert.Equal(t, increment.Reached, true) + assert.True(t, increment.Reached) } } } @@ -54,14 +54,14 @@ func TestLimiterModule1S(t *testing.T) { for i := 1; i <= 3; i++ { increment, _ := instance.Increment(ctx, key, 1) - assert.Equal(t, increment.Reached, false) + assert.False(t, increment.Reached) time.Sleep(time.Second * 1) } _, _ = instance.Increment(ctx, key, 1) for i := 1; i <= 5; i++ { increment, _ := instance.Increment(ctx, key, 1) - assert.Equal(t, increment.Reached, true) + assert.True(t, increment.Reached) } } @@ -74,9 +74,9 @@ func TestIsIpLimited(t *testing.T) { } ingressLimiter := GetIngressLimiter(ingressExactPathMock) - assert.Equal(t, false, IpIsLimited(ingressLimiter, map[string]string{}, ip)) - assert.Equal(t, true, IpIsLimited(ingressLimiter, map[string]string{}, ip)) - assert.Equal(t, false, IpIsLimited(ingressLimiter, map[string]string{ + assert.False(t, IpIsLimited(ingressLimiter, map[string]string{}, ip)) + assert.True(t, IpIsLimited(ingressLimiter, map[string]string{}, ip)) + assert.False(t, IpIsLimited(ingressLimiter, map[string]string{ "guardgress/limit-ip-whitelist": "127.0.0.1", }, ip)) @@ -88,8 +88,8 @@ func TestGetIngressLimiter(t *testing.T) { "guardgress/limit-period": "20-S", } ingressLimiter := GetIngressLimiter(ingressExactPathMock) - assert.Equal(t, ingressLimiter.Rate.Limit, int64(20)) - assert.Equal(t, ingressLimiter.Rate.Period, time.Second) + assert.Equal(t, int64(20), ingressLimiter.Rate.Limit) + assert.Equal(t, time.Second, ingressLimiter.Rate.Period) ingressExactPathMock = mocks.IngressExactPathTypeMock() ingressExactPathMock.Annotations = map[string]string{ @@ -97,8 +97,8 @@ func TestGetIngressLimiter(t *testing.T) { } ingressLimiter = GetIngressLimiter(ingressExactPathMock) - assert.Equal(t, ingressLimiter.Rate.Limit, int64(20)) - assert.Equal(t, ingressLimiter.Rate.Period, time.Hour) + assert.Equal(t, int64(20), ingressLimiter.Rate.Limit) + assert.Equal(t, time.Hour, ingressLimiter.Rate.Period) ingressExactPathMock = mocks.IngressExactPathTypeMock() ingressExactPathMock.Annotations = map[string]string{} @@ -113,16 +113,16 @@ func TestRateLimitTriggeredRPS1(t *testing.T) { "guardgress/limit-period": "1-S", } ingressLimiter := GetIngressLimiter(ingressExactPathMock) - assert.Equal(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp), false) - assert.Equal(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp), true) - assert.Equal(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp), true) + assert.False(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) + assert.True(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) + assert.True(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) time.Sleep(time.Second * 1) - assert.Equal(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp), false) - assert.Equal(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp), true) + assert.False(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) + assert.True(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) time.Sleep(time.Millisecond * 500) - assert.Equal(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp), true) + assert.True(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) time.Sleep(time.Millisecond * 500) - assert.Equal(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp), false) + assert.False(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) } func TestRateLimitTriggeredRPS10(t *testing.T) { @@ -144,13 +144,13 @@ func TestRateLimitTriggeredRPS10(t *testing.T) { go func(wg *sync.WaitGroup) { defer wg.Done() log.Info("simulating request") - assert.Equal(t, false, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) + assert.False(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) }(&wg) } wg.Wait() // The IP should be limited after reaching the specified limit - assert.Equal(t, true, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) + assert.True(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) } func TestRateLimitTriggeredRPM10(t *testing.T) { @@ -164,9 +164,9 @@ func TestRateLimitTriggeredRPM10(t *testing.T) { // range 10 for i := 1; i <= 10; i++ { if i <= 5 { - assert.Equal(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp), false) + assert.False(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) } else { - assert.Equal(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp), true) + assert.True(t, IpIsLimited(ingressLimiter, ingressExactPathMock.Annotations, mockIp)) } } diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index 6c368be..8b49d93 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -35,11 +35,11 @@ func TestGetTlsCertificateForceLocalhost(t *testing.T) { } _, ok := os.LookupEnv("FORCE_LOCALHOST_CERT") - assert.Equal(t, ok, false) + assert.False(t, ok) err := os.Setenv("FORCE_LOCALHOST_CERT", "true") assert.NoError(t, err) _, ok = os.LookupEnv("FORCE_LOCALHOST_CERT") - assert.Equal(t, ok, true) + assert.True(t, ok) // sni does not matter should return localhost cert _, err = routingTable.GetTlsCertificate("www.foo.com") diff --git a/pkg/server/server.go b/pkg/server/server.go index c891248..d0c2adc 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -65,7 +65,7 @@ func (s Server) Run(ctx context.Context) { wg.Add(1) go func() { - log.Info("Starting HTTPS-Server on ", s.Config.Port) + log.Info("Starting HTTPS-Server on ", s.Config.TlsPort) handle := gin.Default() handle.Any("/*path", s.ServeHttps) err := fp.Server( @@ -151,7 +151,7 @@ func (s Server) ServeHttps(ctx *gin.Context) { proxy.Director = func(req *http.Request) { req.Header = ctx.Request.Header - req.Host = svcUrl.Host + req.Host = ctx.Request.Host req.URL.Scheme = svcUrl.Scheme req.URL.Host = svcUrl.Host req.URL.Path = svcUrl.Path @@ -190,7 +190,7 @@ func (s Server) ServeHTTP(ctx *gin.Context) { proxy.Director = func(req *http.Request) { req.Header = ctx.Request.Header - req.Host = svcUrl.Host + req.Host = ctx.Request.Host req.URL.Scheme = svcUrl.Scheme req.URL.Host = svcUrl.Host req.URL.Path = svcUrl.Path diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index ad0d5e6..b37aa52 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -695,6 +695,59 @@ func TestHealthzRoute(t *testing.T) { assert.Equal(t, string(bs), "ok") } +func TestProxyDirectorParams(t *testing.T) { + mockServerPort := freePort() + mockServerAddress := fmt.Sprintf("127.0.0.1:%d", mockServerPort) + testReverseProxyConfig.Port = freePort() + testReverseProxyConfig.TlsPort = freePort() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ingressExactPathMock := mocks.IngressExactPathTypeMock() + ingressExactPathMock.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number = int32(mockServerPort) + ingressExactPathMock.Spec.Rules[0].HTTP.Paths[0].Path = "/bar" + ingressExactPathMock.Spec.Rules[0].Host = "www.guardgress.com" + ingressLimiterPathExact := limitHandler.GetIngressLimiter(ingressExactPathMock) + + startMockServer(mockServerAddress, ctx) + + srv := New(testReverseProxyConfig) + + srv.RoutingTable = &router.RoutingTable{ + Ingresses: &v1.IngressList{ + TypeMeta: v12.TypeMeta{}, + ListMeta: v12.ListMeta{}, + Items: []v1.Ingress{ + ingressExactPathMock, + }, + }, + TlsCertificates: mocks.TlsCertificatesMock(), + IngressLimiters: []*limiter.Limiter{ingressLimiterPathExact}, + } + + go func() { + srv.Run(ctx) + }() + + waitForServer(ctx, testReverseProxyConfig.Port) + + // check if proxy router params are correct + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + req, err := http.NewRequest( + "GET", + fmt.Sprintf("https://%s:%d/bar", testReverseProxyConfig.Host, testReverseProxyConfig.TlsPort), + nil, + ) + req.Host = ingressExactPathMock.Spec.Rules[0].Host + assert.NoError(t, err) + res, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + assert.Equal(t, 200, res.StatusCode) + bs, err := io.ReadAll(res.Body) + assert.Equal(t, mockServerResponse, string(bs)) + assert.Equal(t, res.Header.Get("X-Requested-With-Host"), ingressExactPathMock.Spec.Rules[0].Host) +} + func startMockServer(addr string, ctx context.Context) *http.Server { mockSrv := &http.Server{ Addr: addr, @@ -704,6 +757,7 @@ func startMockServer(addr string, ctx context.Context) *http.Server { w.Header().Set(v, r.Header.Get(v)) } } + w.Header().Set("X-Requested-With-Host", r.Host) _, _ = io.WriteString(w, mockServerResponse) }), } @@ -745,7 +799,12 @@ func waitForServer(ctx context.Context, port int) bool { func freePort() int { // Listen on a random port listener, _ := net.Listen("tcp", ":0") - defer listener.Close() + defer func(listener net.Listener) { + err := listener.Close() + if err != nil { + log.Error(err) + } + }(listener) // Retrieve the address information address := listener.Addr().(*net.TCPAddr)