package server import ( "net/http" "net/http/httptest" "testing" "time" "github.com/containous/traefik/config" "github.com/containous/traefik/config/static" th "github.com/containous/traefik/testhelpers" "github.com/containous/traefik/tls" "github.com/stretchr/testify/assert" ) // 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 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h var ( localhostCert = tls.FileOrContent(`-----BEGIN CERTIFICATE----- MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4 iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9 tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM fblo6RBxUQ== -----END CERTIFICATE-----`) // LocalhostKey is the private key for localhostCert. localhostKey = tls.FileOrContent(`-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9 SjY1bIw4iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZB l2+XsDulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet 3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj013sovGKUFfYAqVXVlxtIX qyUBnu3X9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFeo f9Oeos0UUothgiDktdQHxdNEwLjQf7lJJBzV+5OtwswCWA== -----END RSA PRIVATE KEY-----`) ) func TestServerLoadCertificateWithTLSEntryPoints(t *testing.T) { staticConfig := static.Configuration{} dynamicConfigs := config.Configurations{ "config": &config.Configuration{ TLS: []*tls.Configuration{ { Certificate: &tls.Certificate{ CertFile: localhostCert, KeyFile: localhostKey, }, }, }, }, } srv := NewServer(staticConfig, nil, EntryPoints{ "https": &EntryPoint{ Certs: tls.NewCertificateStore(), }, "https2": &EntryPoint{ Certs: tls.NewCertificateStore(), }, }) _, mapsCerts := srv.loadConfig(dynamicConfigs) if len(mapsCerts["https"]) == 0 || len(mapsCerts["https2"]) == 0 { t.Fatal("got error: https entryPoint must have TLS certificates.") } } func TestReuseService(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) })) defer testServer.Close() entryPoints := EntryPoints{ "http": &EntryPoint{}, } staticConfig := static.Configuration{} dynamicConfigs := config.Configurations{ "config": th.BuildConfiguration( th.WithRouters( th.WithRouter("foo", th.WithServiceName("bar"), th.WithRule("Path(`/ok`)")), th.WithRouter("foo2", th.WithEntryPoints("http"), th.WithRule("Path(`/unauthorized`)"), th.WithServiceName("bar"), th.WithRouterMiddlewares("basicauth")), ), th.WithMiddlewares(th.WithMiddleware("basicauth", th.WithBasicAuth(&config.BasicAuth{Users: []string{"foo:bar"}}), )), th.WithLoadBalancerServices(th.WithService("bar", th.WithLBMethod("wrr"), th.WithServers(th.WithServer(testServer.URL))), ), ), } srv := NewServer(staticConfig, nil, entryPoints) entrypointsHandlers, _ := srv.loadConfig(dynamicConfigs) // Test that the /ok path returns a status 200. responseRecorderOk := &httptest.ResponseRecorder{} requestOk := httptest.NewRequest(http.MethodGet, testServer.URL+"/ok", nil) entrypointsHandlers["http"].ServeHTTP(responseRecorderOk, requestOk) assert.Equal(t, http.StatusOK, responseRecorderOk.Result().StatusCode, "status code") // Test that the /unauthorized path returns a 401 because of // the basic authentication defined on the frontend. responseRecorderUnauthorized := &httptest.ResponseRecorder{} requestUnauthorized := httptest.NewRequest(http.MethodGet, testServer.URL+"/unauthorized", nil) entrypointsHandlers["http"].ServeHTTP(responseRecorderUnauthorized, requestUnauthorized) assert.Equal(t, http.StatusUnauthorized, responseRecorderUnauthorized.Result().StatusCode, "status code") } func TestThrottleProviderConfigReload(t *testing.T) { throttleDuration := 30 * time.Millisecond publishConfig := make(chan config.Message) providerConfig := make(chan config.Message) stop := make(chan bool) defer func() { stop <- true }() staticConfiguration := static.Configuration{} server := NewServer(staticConfiguration, nil, nil) go server.throttleProviderConfigReload(throttleDuration, publishConfig, providerConfig, stop) publishedConfigCount := 0 stopConsumeConfigs := make(chan bool) go func() { for { select { case <-stop: return case <-stopConsumeConfigs: return case <-publishConfig: publishedConfigCount++ } } }() // publish 5 new configs, one new config each 10 milliseconds for i := 0; i < 5; i++ { providerConfig <- config.Message{} time.Sleep(10 * time.Millisecond) } // after 50 milliseconds 5 new configs were published // with a throttle duration of 30 milliseconds this means, we should have received 2 new configs assert.Equal(t, 2, publishedConfigCount, "times configs were published") stopConsumeConfigs <- true select { case <-publishConfig: // There should be exactly one more message that we receive after ~60 milliseconds since the start of the test. select { case <-publishConfig: t.Error("extra config publication found") case <-time.After(100 * time.Millisecond): return } case <-time.After(100 * time.Millisecond): t.Error("Last config was not published in time") } }