mirror of
				https://github.com/traefik/traefik.git
				synced 2025-11-04 02:11:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			278 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package integration
 | 
						|
 | 
						|
import (
 | 
						|
	"net"
 | 
						|
	"net/http"
 | 
						|
	"net/http/httptest"
 | 
						|
	"os"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/stretchr/testify/assert"
 | 
						|
	"github.com/stretchr/testify/require"
 | 
						|
	"github.com/stretchr/testify/suite"
 | 
						|
	"github.com/traefik/traefik/v3/integration/try"
 | 
						|
)
 | 
						|
 | 
						|
// Headers tests suite.
 | 
						|
type HeadersSuite struct{ BaseSuite }
 | 
						|
 | 
						|
func TestHeadersSuite(t *testing.T) {
 | 
						|
	suite.Run(t, new(HeadersSuite))
 | 
						|
}
 | 
						|
 | 
						|
func (s *HeadersSuite) TearDownTest() {
 | 
						|
	s.displayTraefikLogFile(traefikTestLogFile)
 | 
						|
	_ = os.Remove(traefikTestAccessLogFile)
 | 
						|
}
 | 
						|
 | 
						|
func (s *HeadersSuite) TestSimpleConfiguration() {
 | 
						|
	s.traefikCmd(withConfigFile("fixtures/headers/basic.toml"))
 | 
						|
 | 
						|
	// Expected a 404 as we did not configure anything
 | 
						|
	err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
}
 | 
						|
 | 
						|
func (s *HeadersSuite) TestReverseProxyHeaderRemoved() {
 | 
						|
	file := s.adaptFile("fixtures/headers/remove_reverseproxy_headers.toml", struct{}{})
 | 
						|
	s.traefikCmd(withConfigFile(file))
 | 
						|
 | 
						|
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
						|
		_, found := r.Header["X-Forwarded-Host"]
 | 
						|
		assert.True(s.T(), found)
 | 
						|
		_, found = r.Header["Foo"]
 | 
						|
		assert.False(s.T(), found)
 | 
						|
		_, found = r.Header["X-Forwarded-For"]
 | 
						|
		assert.False(s.T(), found)
 | 
						|
	})
 | 
						|
 | 
						|
	listener, err := net.Listen("tcp", "127.0.0.1:9000")
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
 | 
						|
	ts := &httptest.Server{
 | 
						|
		Listener: listener,
 | 
						|
		Config:   &http.Server{Handler: handler},
 | 
						|
	}
 | 
						|
	ts.Start()
 | 
						|
	defer ts.Close()
 | 
						|
 | 
						|
	req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
	req.Host = "test.localhost"
 | 
						|
	req.Header = http.Header{
 | 
						|
		"Foo": {"bar"},
 | 
						|
	}
 | 
						|
 | 
						|
	err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
}
 | 
						|
 | 
						|
func (s *HeadersSuite) TestConnectionHopByHop() {
 | 
						|
	file := s.adaptFile("fixtures/headers/connection_hop_by_hop_headers.toml", struct{}{})
 | 
						|
	s.traefikCmd(withConfigFile(file))
 | 
						|
 | 
						|
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
						|
		_, found := r.Header["X-Forwarded-For"]
 | 
						|
		assert.True(s.T(), found)
 | 
						|
		xHost, found := r.Header["X-Forwarded-Host"]
 | 
						|
		assert.True(s.T(), found)
 | 
						|
		assert.Equal(s.T(), "localhost", xHost[0])
 | 
						|
 | 
						|
		_, found = r.Header["Foo"]
 | 
						|
		assert.False(s.T(), found)
 | 
						|
		_, found = r.Header["Bar"]
 | 
						|
		assert.False(s.T(), found)
 | 
						|
	})
 | 
						|
 | 
						|
	listener, err := net.Listen("tcp", "127.0.0.1:9000")
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
 | 
						|
	ts := &httptest.Server{
 | 
						|
		Listener: listener,
 | 
						|
		Config:   &http.Server{Handler: handler},
 | 
						|
	}
 | 
						|
	ts.Start()
 | 
						|
	defer ts.Close()
 | 
						|
 | 
						|
	req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
	req.Host = "test.localhost"
 | 
						|
	req.Header = http.Header{
 | 
						|
		"Connection":       {"Foo,Bar,X-Forwarded-For,X-Forwarded-Host"},
 | 
						|
		"Foo":              {"bar"},
 | 
						|
		"Bar":              {"foo"},
 | 
						|
		"X-Forwarded-Host": {"localhost"},
 | 
						|
	}
 | 
						|
 | 
						|
	err = try.Request(req, time.Second, try.StatusCodeIs(http.StatusOK))
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
 | 
						|
	accessLog, err := os.ReadFile(traefikTestAccessLogFile)
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
 | 
						|
	assert.Contains(s.T(), string(accessLog), "\"request_Foo\":\"bar\"")
 | 
						|
	assert.NotContains(s.T(), string(accessLog), "\"request_Bar\":\"\"")
 | 
						|
}
 | 
						|
 | 
						|
func (s *HeadersSuite) TestCorsResponses() {
 | 
						|
	file := s.adaptFile("fixtures/headers/cors.toml", struct{}{})
 | 
						|
	s.traefikCmd(withConfigFile(file))
 | 
						|
 | 
						|
	backend := startTestServer("9000", http.StatusOK, "")
 | 
						|
	defer backend.Close()
 | 
						|
 | 
						|
	err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
 | 
						|
	testCase := []struct {
 | 
						|
		desc           string
 | 
						|
		requestHeaders http.Header
 | 
						|
		expected       http.Header
 | 
						|
		reqHost        string
 | 
						|
		method         string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			desc: "simple access control allow origin",
 | 
						|
			requestHeaders: http.Header{
 | 
						|
				"Origin": {"https://foo.bar.org"},
 | 
						|
			},
 | 
						|
			expected: http.Header{
 | 
						|
				"Access-Control-Allow-Origin": {"https://foo.bar.org"},
 | 
						|
				"Vary":                        {"Origin"},
 | 
						|
			},
 | 
						|
			reqHost: "test.localhost",
 | 
						|
			method:  http.MethodGet,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			desc: "simple preflight request",
 | 
						|
			requestHeaders: http.Header{
 | 
						|
				"Access-Control-Request-Headers": {"origin"},
 | 
						|
				"Access-Control-Request-Method":  {"GET", "OPTIONS"},
 | 
						|
				"Origin":                         {"https://foo.bar.org"},
 | 
						|
			},
 | 
						|
			expected: http.Header{
 | 
						|
				"Access-Control-Allow-Origin":  {"https://foo.bar.org"},
 | 
						|
				"Access-Control-Max-Age":       {"100"},
 | 
						|
				"Access-Control-Allow-Methods": {"GET,OPTIONS,PUT"},
 | 
						|
			},
 | 
						|
			reqHost: "test.localhost",
 | 
						|
			method:  http.MethodOptions,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			desc: "preflight Options request with no cors configured",
 | 
						|
			requestHeaders: http.Header{
 | 
						|
				"Access-Control-Request-Headers": {"origin"},
 | 
						|
				"Access-Control-Request-Method":  {"GET", "OPTIONS"},
 | 
						|
				"Origin":                         {"https://foo.bar.org"},
 | 
						|
			},
 | 
						|
			expected: http.Header{
 | 
						|
				"X-Custom-Response-Header": {"True"},
 | 
						|
			},
 | 
						|
			reqHost: "test2.localhost",
 | 
						|
			method:  http.MethodOptions,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			desc: "preflight Get request with no cors configured",
 | 
						|
			requestHeaders: http.Header{
 | 
						|
				"Access-Control-Request-Headers": {"origin"},
 | 
						|
				"Access-Control-Request-Method":  {"GET", "OPTIONS"},
 | 
						|
				"Origin":                         {"https://foo.bar.org"},
 | 
						|
			},
 | 
						|
			expected: http.Header{
 | 
						|
				"X-Custom-Response-Header": {"True"},
 | 
						|
			},
 | 
						|
			reqHost: "test2.localhost",
 | 
						|
			method:  http.MethodGet,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, test := range testCase {
 | 
						|
		req, err := http.NewRequest(test.method, "http://127.0.0.1:8000/", nil)
 | 
						|
		require.NoError(s.T(), err)
 | 
						|
		req.Host = test.reqHost
 | 
						|
		req.Header = test.requestHeaders
 | 
						|
 | 
						|
		err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
 | 
						|
		require.NoError(s.T(), err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (s *HeadersSuite) TestSecureHeadersResponses() {
 | 
						|
	file := s.adaptFile("fixtures/headers/secure.toml", struct{}{})
 | 
						|
	s.traefikCmd(withConfigFile(file))
 | 
						|
 | 
						|
	backend := startTestServer("9000", http.StatusOK, "")
 | 
						|
	defer backend.Close()
 | 
						|
 | 
						|
	err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
 | 
						|
	testCase := []struct {
 | 
						|
		desc            string
 | 
						|
		expected        http.Header
 | 
						|
		reqHost         string
 | 
						|
		internalReqHost string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			desc: "Permissions-Policy Set",
 | 
						|
			expected: http.Header{
 | 
						|
				"Permissions-Policy": {"microphone=(),"},
 | 
						|
			},
 | 
						|
			reqHost:         "test.localhost",
 | 
						|
			internalReqHost: "internal.localhost",
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, test := range testCase {
 | 
						|
		req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
 | 
						|
		require.NoError(s.T(), err)
 | 
						|
		req.Host = test.reqHost
 | 
						|
 | 
						|
		err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected))
 | 
						|
		require.NoError(s.T(), err)
 | 
						|
 | 
						|
		req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/api/rawdata", nil)
 | 
						|
		require.NoError(s.T(), err)
 | 
						|
		req.Host = test.internalReqHost
 | 
						|
 | 
						|
		err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected))
 | 
						|
		require.NoError(s.T(), err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (s *HeadersSuite) TestMultipleSecureHeadersResponses() {
 | 
						|
	file := s.adaptFile("fixtures/headers/secure_multiple.toml", struct{}{})
 | 
						|
	s.traefikCmd(withConfigFile(file))
 | 
						|
 | 
						|
	backend := startTestServer("9000", http.StatusOK, "")
 | 
						|
	defer backend.Close()
 | 
						|
 | 
						|
	err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
 | 
						|
	require.NoError(s.T(), err)
 | 
						|
 | 
						|
	testCase := []struct {
 | 
						|
		desc     string
 | 
						|
		expected http.Header
 | 
						|
		reqHost  string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			desc: "Multiple Secure Headers Set",
 | 
						|
			expected: http.Header{
 | 
						|
				"X-Frame-Options":        {"DENY"},
 | 
						|
				"X-Content-Type-Options": {"nosniff"},
 | 
						|
			},
 | 
						|
			reqHost: "test.localhost",
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, test := range testCase {
 | 
						|
		req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
 | 
						|
		require.NoError(s.T(), err)
 | 
						|
		req.Host = test.reqHost
 | 
						|
 | 
						|
		err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
 | 
						|
		require.NoError(s.T(), err)
 | 
						|
	}
 | 
						|
}
 |