From 1e44e339ad0b65fe6ee63d2a8ccc719463cf7649 Mon Sep 17 00:00:00 2001 From: NicoMen Date: Thu, 21 Dec 2017 14:16:03 +0100 Subject: [PATCH] Allow deleting dynamically all TLS certificates from an entryPoint --- .../fixtures/https/dynamic_https_sni.toml | 3 + integration/https_test.go | 98 ++++++++++++++++++- server/server.go | 6 +- 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/integration/fixtures/https/dynamic_https_sni.toml b/integration/fixtures/https/dynamic_https_sni.toml index 19471cc8a..3c5b84da4 100644 --- a/integration/fixtures/https/dynamic_https_sni.toml +++ b/integration/fixtures/https/dynamic_https_sni.toml @@ -6,6 +6,9 @@ defaultEntryPoints = ["https"] [entryPoints.https] address = ":4443" [entryPoints.https.tls] + [entryPoints.https02] + address = ":8443" + [entryPoints.https02.tls] [web] address = ":8080" diff --git a/integration/https_test.go b/integration/https_test.go index d657eca14..ab576b973 100644 --- a/integration/https_test.go +++ b/integration/https_test.go @@ -353,7 +353,7 @@ func startTestServer(port string, statusCode int) (ts *httptest.Server) { return ts } -// TestWithSNIConfigRoute involves a client sending HTTPS requests with +// TestWithSNIDynamicConfigRouteWithNoChange involves a client sending HTTPS requests with // SNI hostnames of "snitest.org" and "snitest.com". The test verifies // that traefik routes the requests to the expected backends thanks to given certificate if possible // otherwise thanks to the default one. @@ -424,7 +424,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithNoChange(c *check.C) { c.Assert(resp.StatusCode, checker.Equals, http.StatusNoContent) } -// TestWithSNIConfigRoute involves a client sending HTTPS requests with +// TestWithSNIDynamicConfigRouteWithChange involves a client sending HTTPS requests with // SNI hostnames of "snitest.org" and "snitest.com". The test verifies // that traefik updates its configuration when the HTTPS configuration is modified and // it routes the requests to the expected backends thanks to given certificate if possible @@ -479,7 +479,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithChange(c *check.C) { req.Header.Set("Accept", "*/*") // Change certificates configuration file content - modifyCertificateConfFileContent(c, tr1.TLSClientConfig.ServerName, dynamicConfFileName) + modifyCertificateConfFileContent(c, tr1.TLSClientConfig.ServerName, dynamicConfFileName, "https") var resp *http.Response err = try.Do(30*time.Second, func() error { resp, err = client.Do(req) @@ -525,8 +525,96 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithChange(c *check.C) { c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound) } +// TestWithSNIDynamicConfigRouteWithChangeForEmptyTlsConfiguration involves a client sending HTTPS requests with +// SNI hostnames of "snitest.org" and "snitest.com". The test verifies +// that traefik updates its configuration when the HTTPS configuration is modified, even if it totally deleted, and +// it routes the requests to the expected backends thanks to given certificate if possible +// otherwise thanks to the default one. +func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion(c *check.C) { + dynamicConfFileName := s.adaptFile(c, "fixtures/https/dynamic_https.toml", struct{}{}) + defer os.Remove(dynamicConfFileName) + confFileName := s.adaptFile(c, "fixtures/https/dynamic_https_sni.toml", struct { + DynamicConfFileName string + }{ + DynamicConfFileName: dynamicConfFileName, + }) + defer os.Remove(confFileName) + cmd, display := s.traefikCmd(withConfigFile(confFileName)) + defer display(c) + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + tr2 := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + ServerName: "snitest.org", + }, + } + + // wait for Traefik + err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("Host:"+tr2.TLSClientConfig.ServerName)) + c.Assert(err, checker.IsNil) + + backend2 := startTestServer("9020", http.StatusResetContent) + + defer backend2.Close() + + err = try.GetRequest(backend2.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusResetContent)) + c.Assert(err, checker.IsNil) + + req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil) + client := &http.Client{Transport: tr2} + req.Host = tr2.TLSClientConfig.ServerName + req.Header.Set("Host", tr2.TLSClientConfig.ServerName) + req.Header.Set("Accept", "*/*") + + var resp *http.Response + err = try.Do(30*time.Second, func() error { + resp, err = client.Do(req) + + // /!\ If connection is not closed, SSLHandshake will only be done during the first trial /!\ + req.Close = true + + if err != nil { + return err + } + + cn := resp.TLS.PeerCertificates[0].Subject.CommonName + if cn != tr2.TLSClientConfig.ServerName { + return fmt.Errorf("domain %s found in place of %s", cn, tr2.TLSClientConfig.ServerName) + } + + return nil + }) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, http.StatusResetContent) + // Change certificates configuration file content + modifyCertificateConfFileContent(c, "snitest.com", dynamicConfFileName, "https02") + + err = try.Do(60*time.Second, func() error { + resp, err = client.Do(req) + + // /!\ If connection is not closed, SSLHandshake will only be done during the first trial /!\ + req.Close = true + + if err != nil { + return err + } + + cn := resp.TLS.PeerCertificates[0].Subject.CommonName + if cn == tr2.TLSClientConfig.ServerName { + return fmt.Errorf("domain %s found in place of default one", tr2.TLSClientConfig.ServerName) + } + + return nil + }) + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound) +} + // modifyCertificateConfFileContent replaces the content of a HTTPS configuration file. -func modifyCertificateConfFileContent(c *check.C, certFileName, confFileName string) { +func modifyCertificateConfFileContent(c *check.C, certFileName, confFileName, entryPoint string) { tlsConf := types.Configuration{ TLSConfiguration: []*traefikTls.Configuration{ { @@ -534,7 +622,7 @@ func modifyCertificateConfFileContent(c *check.C, certFileName, confFileName str CertFile: traefikTls.FileOrContent("fixtures/https/" + certFileName + ".cert"), KeyFile: traefikTls.FileOrContent("fixtures/https/" + certFileName + ".key"), }, - EntryPoints: []string{"https"}, + EntryPoints: []string{entryPoint}, }, }, } diff --git a/server/server.go b/server/server.go index b9ab28535..be79edf96 100644 --- a/server/server.go +++ b/server/server.go @@ -439,7 +439,11 @@ func (s *Server) loadConfiguration(configMsg types.ConfigMessage) { if err == nil { for newServerEntryPointName, newServerEntryPoint := range newServerEntryPoints { s.serverEntryPoints[newServerEntryPointName].httpRouter.UpdateHandler(newServerEntryPoint.httpRouter.GetHandler()) - if &newServerEntryPoint.certs != nil { + if s.globalConfiguration.EntryPoints[newServerEntryPointName].TLS == nil { + if newServerEntryPoint.certs.Get() != nil { + log.Debugf("Certificates not added to non-TLS entryPoint %s.", newServerEntryPointName) + } + } else { s.serverEntryPoints[newServerEntryPointName].certs.Set(newServerEntryPoint.certs.Get()) } log.Infof("Server configuration reloaded on %s", s.serverEntryPoints[newServerEntryPointName].httpServer.Addr)