mirror of
				https://github.com/traefik/traefik.git
				synced 2025-11-04 10:21:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			246 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package server
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"context"
 | 
						|
	"crypto/tls"
 | 
						|
	"crypto/x509"
 | 
						|
	"net/http"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/quic-go/quic-go"
 | 
						|
	"github.com/stretchr/testify/assert"
 | 
						|
	"github.com/stretchr/testify/require"
 | 
						|
	"github.com/traefik/traefik/v2/pkg/config/static"
 | 
						|
	tcprouter "github.com/traefik/traefik/v2/pkg/server/router/tcp"
 | 
						|
	traefiktls "github.com/traefik/traefik/v2/pkg/tls"
 | 
						|
)
 | 
						|
 | 
						|
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
 | 
						|
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
 | 
						|
// generated from src/crypto/tls:
 | 
						|
// go run generate_cert.go  --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
 | 
						|
var (
 | 
						|
	localhostCert = traefiktls.FileOrContent(`-----BEGIN CERTIFICATE-----
 | 
						|
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
 | 
						|
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
 | 
						|
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
 | 
						|
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
 | 
						|
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
 | 
						|
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
 | 
						|
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
 | 
						|
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
 | 
						|
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
 | 
						|
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
 | 
						|
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
 | 
						|
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
 | 
						|
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
 | 
						|
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
 | 
						|
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
 | 
						|
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
 | 
						|
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
 | 
						|
WkBKOclmOV2xlTVuPw==
 | 
						|
-----END CERTIFICATE-----`)
 | 
						|
 | 
						|
	// LocalhostKey is the private key for localhostCert.
 | 
						|
	localhostKey = traefiktls.FileOrContent(`-----BEGIN RSA PRIVATE KEY-----
 | 
						|
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDoZtrm0dXV0Aqi
 | 
						|
4Bpc7f95sNRTiu/AJSD8I1onY9PnEsPg3VVxvytsVJbYdcqr4w99V3AgpH/UNzMS
 | 
						|
gAZ/8lZBNbsSDOVesJ3euVqMRfYPvd9pYl6QPRRpSDPm+2tNdn3QFAvta9EgJ3sW
 | 
						|
URnoU85w+W6aLI2bNSq3AaE771p3VbkGolpEjo9h+i42TBHo1rhPNKPkGupR8/QX
 | 
						|
AOLMpInRdeaHyDwb2a3DE5I3dG7VAVzrVfJ6W6Q84YoFX+rpEE2SVM17SAjy6xQy
 | 
						|
VjKgLvK2mk0xbtfa+h0B6VK7bmODHZqeP18NVm6HsBcXn7iclLgAC3SfWU1jucZK
 | 
						|
x1lqzw9tAgMBAAECggEABWzxS1Y2wckblnXY57Z+sl6YdmLV+gxj2r8Qib7g4ZIk
 | 
						|
lIlWR1OJNfw7kU4eryib4fc6nOh6O4AWZyYqAK6tqNQSS/eVG0LQTLTTEldHyVJL
 | 
						|
dvBe+MsUQOj4nTndZW+QvFzbcm2D8lY5n2nBSxU5ypVoKZ1EqQzytFcLZpTN7d89
 | 
						|
EPj0qDyrV4NZlWAwL1AygCwnlwhMQjXEalVF1ylXwU3QzyZ/6MgvF6d3SSUlh+sq
 | 
						|
XefuyigXw484cQQgbzopv6niMOmGP3of+yV4JQqUSb3IDmmT68XjGd2Dkxl4iPki
 | 
						|
6ZwXf3CCi+c+i/zVEcufgZ3SLf8D99kUGE7v7fZ6AQKBgQD1ZX3RAla9hIhxCf+O
 | 
						|
3D+I1j2LMrdjAh0ZKKqwMR4JnHX3mjQI6LwqIctPWTU8wYFECSh9klEclSdCa64s
 | 
						|
uI/GNpcqPXejd0cAAdqHEEeG5sHMDt0oFSurL4lyud0GtZvwlzLuwEweuDtvT9cJ
 | 
						|
Wfvl86uyO36IW8JdvUprYDctrQKBgQDycZ697qutBieZlGkHpnYWUAeImVA878sJ
 | 
						|
w44NuXHvMxBPz+lbJGAg8Cn8fcxNAPqHIraK+kx3po8cZGQywKHUWsxi23ozHoxo
 | 
						|
+bGqeQb9U661TnfdDspIXia+xilZt3mm5BPzOUuRqlh4Y9SOBpSWRmEhyw76w4ZP
 | 
						|
OPxjWYAgwQKBgA/FehSYxeJgRjSdo+MWnK66tjHgDJE8bYpUZsP0JC4R9DL5oiaA
 | 
						|
brd2fI6Y+SbyeNBallObt8LSgzdtnEAbjIH8uDJqyOmknNePRvAvR6mP4xyuR+Bv
 | 
						|
m+Lgp0DMWTw5J9CKpydZDItc49T/mJ5tPhdFVd+am0NAQnmr1MCZ6nHxAoGABS3Y
 | 
						|
LkaC9FdFUUqSU8+Chkd/YbOkuyiENdkvl6t2e52jo5DVc1T7mLiIrRQi4SI8N9bN
 | 
						|
/3oJWCT+uaSLX2ouCtNFunblzWHBrhxnZzTeqVq4SLc8aESAnbslKL4i8/+vYZlN
 | 
						|
s8xtiNcSvL+lMsOBORSXzpj/4Ot8WwTkn1qyGgECgYBKNTypzAHeLE6yVadFp3nQ
 | 
						|
Ckq9yzvP/ib05rvgbvrne00YeOxqJ9gtTrzgh7koqJyX1L4NwdkEza4ilDWpucn0
 | 
						|
xiUZS4SoaJq6ZvcBYS62Yr1t8n09iG47YL8ibgtmH3L+svaotvpVxVK+d7BLevA/
 | 
						|
ZboOWVe3icTy64BT3OQhmg==
 | 
						|
-----END RSA PRIVATE KEY-----`)
 | 
						|
)
 | 
						|
 | 
						|
func TestHTTP3AdvertisedPort(t *testing.T) {
 | 
						|
	certContent, err := localhostCert.Read()
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	keyContent, err := localhostKey.Read()
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	tlsCert, err := tls.X509KeyPair(certContent, keyContent)
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	epConfig := &static.EntryPointsTransport{}
 | 
						|
	epConfig.SetDefaults()
 | 
						|
 | 
						|
	entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
 | 
						|
		Address:          "127.0.0.1:0",
 | 
						|
		Transport:        epConfig,
 | 
						|
		ForwardedHeaders: &static.ForwardedHeaders{},
 | 
						|
		HTTP2:            &static.HTTP2Config{},
 | 
						|
		HTTP3: &static.HTTP3Config{
 | 
						|
			AdvertisedPort: 8080,
 | 
						|
		},
 | 
						|
	}, nil)
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	router, err := tcprouter.NewRouter()
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	router.AddHTTPTLSConfig("*", &tls.Config{
 | 
						|
		Certificates: []tls.Certificate{tlsCert},
 | 
						|
	})
 | 
						|
	router.SetHTTPSHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
 | 
						|
		rw.WriteHeader(http.StatusOK)
 | 
						|
	}), nil)
 | 
						|
 | 
						|
	ctx := context.Background()
 | 
						|
	go entryPoint.Start(ctx)
 | 
						|
	entryPoint.SwitchRouter(router)
 | 
						|
 | 
						|
	conn, err := tls.Dial("tcp", entryPoint.listener.Addr().String(), &tls.Config{
 | 
						|
		InsecureSkipVerify: true,
 | 
						|
	})
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	t.Cleanup(func() {
 | 
						|
		_ = conn.Close()
 | 
						|
		entryPoint.Shutdown(ctx)
 | 
						|
	})
 | 
						|
 | 
						|
	// We are racing with the http3Server readiness happening in the goroutine starting the entrypoint
 | 
						|
	time.Sleep(time.Second)
 | 
						|
 | 
						|
	request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8090", nil)
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	err = request.Write(conn)
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	r, err := http.ReadResponse(bufio.NewReader(conn), nil)
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	assert.NotContains(t, r.Header.Get("Alt-Svc"), ":8090")
 | 
						|
	assert.Contains(t, r.Header.Get("Alt-Svc"), ":8080")
 | 
						|
}
 | 
						|
 | 
						|
func TestHTTP30RTT(t *testing.T) {
 | 
						|
	certContent, err := localhostCert.Read()
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	keyContent, err := localhostKey.Read()
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	tlsCert, err := tls.X509KeyPair(certContent, keyContent)
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	epConfig := &static.EntryPointsTransport{}
 | 
						|
	epConfig.SetDefaults()
 | 
						|
 | 
						|
	entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
 | 
						|
		Address:          "127.0.0.1:8090",
 | 
						|
		Transport:        epConfig,
 | 
						|
		ForwardedHeaders: &static.ForwardedHeaders{},
 | 
						|
		HTTP2:            &static.HTTP2Config{},
 | 
						|
		HTTP3:            &static.HTTP3Config{},
 | 
						|
	}, nil)
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	router, err := tcprouter.NewRouter()
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	router.AddHTTPTLSConfig("example.com", &tls.Config{
 | 
						|
		Certificates: []tls.Certificate{tlsCert},
 | 
						|
	})
 | 
						|
	router.SetHTTPSHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
 | 
						|
		rw.WriteHeader(http.StatusOK)
 | 
						|
	}), nil)
 | 
						|
 | 
						|
	ctx := context.Background()
 | 
						|
	go entryPoint.Start(ctx)
 | 
						|
	entryPoint.SwitchRouter(router)
 | 
						|
 | 
						|
	// We are racing with the http3Server readiness happening in the goroutine starting the entrypoint.
 | 
						|
	time.Sleep(time.Second)
 | 
						|
 | 
						|
	certPool := x509.NewCertPool()
 | 
						|
	certPool.AppendCertsFromPEM(certContent)
 | 
						|
 | 
						|
	tlsConf := &tls.Config{
 | 
						|
		RootCAs:    certPool,
 | 
						|
		ServerName: "example.com",
 | 
						|
		NextProtos: []string{"h3"},
 | 
						|
	}
 | 
						|
 | 
						|
	// Define custom cache.
 | 
						|
	gets := make(chan string, 100)
 | 
						|
	puts := make(chan string, 100)
 | 
						|
	cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts)
 | 
						|
	tlsConf.ClientSessionCache = cache
 | 
						|
 | 
						|
	// This first DialAddrEarly connection is here to populate the cache.
 | 
						|
	earlyConnection, err := quic.DialAddrEarly(context.Background(), "127.0.0.1:8090", tlsConf, &quic.Config{})
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	t.Cleanup(func() {
 | 
						|
		_ = earlyConnection.CloseWithError(0, "")
 | 
						|
		entryPoint.Shutdown(ctx)
 | 
						|
	})
 | 
						|
 | 
						|
	// wait for the handshake complete.
 | 
						|
	<-earlyConnection.HandshakeComplete()
 | 
						|
 | 
						|
	// 0RTT is always false on the first connection.
 | 
						|
	require.False(t, earlyConnection.ConnectionState().Used0RTT)
 | 
						|
 | 
						|
	earlyConnection, err = quic.DialAddrEarly(context.Background(), "127.0.0.1:8090", tlsConf, &quic.Config{})
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	<-earlyConnection.HandshakeComplete()
 | 
						|
 | 
						|
	// 0RTT need to be false.
 | 
						|
	assert.False(t, earlyConnection.ConnectionState().Used0RTT)
 | 
						|
}
 | 
						|
 | 
						|
type clientSessionCache struct {
 | 
						|
	cache tls.ClientSessionCache
 | 
						|
 | 
						|
	gets chan<- string
 | 
						|
	puts chan<- string
 | 
						|
}
 | 
						|
 | 
						|
func newClientSessionCache(cache tls.ClientSessionCache, gets, puts chan<- string) *clientSessionCache {
 | 
						|
	return &clientSessionCache{
 | 
						|
		cache: cache,
 | 
						|
		gets:  gets,
 | 
						|
		puts:  puts,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
var _ tls.ClientSessionCache = &clientSessionCache{}
 | 
						|
 | 
						|
func (c *clientSessionCache) Get(sessionKey string) (*tls.ClientSessionState, bool) {
 | 
						|
	session, ok := c.cache.Get(sessionKey)
 | 
						|
	c.gets <- sessionKey
 | 
						|
	return session, ok
 | 
						|
}
 | 
						|
 | 
						|
func (c *clientSessionCache) Put(sessionKey string, cs *tls.ClientSessionState) {
 | 
						|
	c.cache.Put(sessionKey, cs)
 | 
						|
	c.puts <- sessionKey
 | 
						|
}
 |