Define TLS options on the Router configuration
Co-authored-by: juliens <julien@containo.us>
This commit is contained in:
parent
d306c8fd50
commit
85ce16b34f
24 changed files with 958 additions and 148 deletions
62
integration/fixtures/https/https_tls_options.toml
Normal file
62
integration/fixtures/https/https_tls_options.toml
Normal file
|
@ -0,0 +1,62 @@
|
|||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.web-secure]
|
||||
address = ":4443"
|
||||
|
||||
[api]
|
||||
|
||||
[providers]
|
||||
[providers.file]
|
||||
|
||||
[http.routers]
|
||||
[http.routers.router1]
|
||||
Service = "service1"
|
||||
Rule = "Host(`snitest.com`)"
|
||||
[http.routers.router1.tls]
|
||||
options = "foo"
|
||||
|
||||
[http.routers.router2]
|
||||
Service = "service2"
|
||||
Rule = "Host(`snitest.org`)"
|
||||
[http.routers.router2.tls]
|
||||
options = "bar"
|
||||
|
||||
[http.routers.router3]
|
||||
Service = "service2"
|
||||
Rule = "Host(`snitest.org`)"
|
||||
[http.routers.router3.tls]
|
||||
options = "unknown"
|
||||
|
||||
[http.services]
|
||||
[http.services.service1]
|
||||
[http.services.service1.LoadBalancer]
|
||||
[[http.services.service1.LoadBalancer.Servers]]
|
||||
URL = "http://127.0.0.1:9010"
|
||||
|
||||
[http.services.service2]
|
||||
[http.services.service2.LoadBalancer]
|
||||
[[http.services.service2.LoadBalancer.Servers]]
|
||||
URL = "http://127.0.0.1:9020"
|
||||
|
||||
|
||||
[[tls]]
|
||||
[tls.certificate]
|
||||
certFile = "fixtures/https/snitest.com.cert"
|
||||
keyFile = "fixtures/https/snitest.com.key"
|
||||
|
||||
[[tls]]
|
||||
[tls.certificate]
|
||||
certFile = "fixtures/https/snitest.org.cert"
|
||||
keyFile = "fixtures/https/snitest.org.key"
|
||||
|
||||
[tlsoptions.foo]
|
||||
minversion = "VersionTLS11"
|
||||
|
||||
[tlsoptions.bar]
|
||||
minversion = "VersionTLS12"
|
|
@ -2,12 +2,19 @@
|
|||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[api]
|
||||
entrypoint="api"
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":80"
|
||||
address = ":8081"
|
||||
|
||||
|
||||
[entryPoints.api]
|
||||
address = ":8080"
|
||||
|
||||
[providers]
|
||||
[providers.file]
|
||||
|
|
|
@ -38,4 +38,3 @@ level = "DEBUG"
|
|||
[http.services.whoami.loadbalancer]
|
||||
[[http.services.whoami.loadbalancer.servers]]
|
||||
url = "http://localhost:8085"
|
||||
weight=1
|
||||
|
|
42
integration/fixtures/tcp/multi-tls-options.toml
Normal file
42
integration/fixtures/tcp/multi-tls-options.toml
Normal file
|
@ -0,0 +1,42 @@
|
|||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.tcp]
|
||||
address = ":8093"
|
||||
|
||||
[api]
|
||||
|
||||
[providers.file]
|
||||
|
||||
[tcp]
|
||||
[tcp.routers]
|
||||
[tcp.routers.to-whoami-no-cert]
|
||||
rule = "HostSNI(`whoami-c.test`)"
|
||||
service = "whoami-no-cert"
|
||||
entryPoints = [ "tcp" ]
|
||||
[tcp.routers.to-whoami-no-cert.tls]
|
||||
options = "foo"
|
||||
|
||||
[tcp.routers.to-whoami-sni-strict]
|
||||
rule = "HostSNI(`whoami-d.test`)"
|
||||
service = "whoami-no-cert"
|
||||
entryPoints = [ "tcp" ]
|
||||
[tcp.routers.to-whoami-sni-strict.tls]
|
||||
options = "bar"
|
||||
|
||||
[tcp.services.whoami-no-cert]
|
||||
[tcp.services.whoami-no-cert.loadbalancer]
|
||||
method = "wrr"
|
||||
[[tcp.services.whoami-no-cert.loadbalancer.servers]]
|
||||
address = "localhost:8083"
|
||||
|
||||
[tlsoptions.foo]
|
||||
minversion = "VersionTLS11"
|
||||
|
||||
[tlsoptions.bar]
|
||||
minversion = "VersionTLS12"
|
|
@ -111,6 +111,90 @@ func (s *HTTPSSuite) TestWithSNIConfigRoute(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// TestWithTLSOptions verifies that traefik routes the requests with the associated tls options.
|
||||
func (s *HTTPSSuite) TestWithTLSOptions(c *check.C) {
|
||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/https/https_tls_options.toml"))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`snitest.org`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend1 := startTestServer("9010", http.StatusNoContent)
|
||||
backend2 := startTestServer("9020", http.StatusResetContent)
|
||||
defer backend1.Close()
|
||||
defer backend2.Close()
|
||||
|
||||
err = try.GetRequest(backend1.URL, 1*time.Second, try.StatusCodeIs(http.StatusNoContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = try.GetRequest(backend2.URL, 1*time.Second, try.StatusCodeIs(http.StatusResetContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tr1 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
MaxVersion: tls.VersionTLS11,
|
||||
ServerName: "snitest.com",
|
||||
},
|
||||
}
|
||||
|
||||
tr2 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
MaxVersion: tls.VersionTLS12,
|
||||
ServerName: "snitest.org",
|
||||
},
|
||||
}
|
||||
|
||||
tr3 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
MaxVersion: tls.VersionTLS11,
|
||||
ServerName: "snitest.org",
|
||||
},
|
||||
}
|
||||
|
||||
// With valid TLS options and request
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr1, try.HasCn(tr1.TLSClientConfig.ServerName), try.StatusCodeIs(http.StatusNoContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// With a valid TLS version
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
|
||||
err = try.RequestWithTransport(req, 3*time.Second, tr2, try.HasCn(tr2.TLSClientConfig.ServerName), try.StatusCodeIs(http.StatusResetContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// With a bad TLS version
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = tr3.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr3.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
client := http.Client{
|
||||
Transport: tr3,
|
||||
}
|
||||
_, err = client.Do(req)
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "protocol version not supported")
|
||||
|
||||
// with unknown tls option
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("unknown TLS options: unknown"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// TestWithSNIStrictNotMatchedRequest involves a client sending a SNI hostname of
|
||||
// "snitest.org", which does not match the CN of 'snitest.com.crt'. The test
|
||||
// verifies that traefik closes the connection.
|
||||
|
|
|
@ -28,34 +28,38 @@ func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) {
|
|||
}{s.ServerIP})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, _ := s.cmdTraefik(withConfigFile(file))
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:80/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("ratelimit"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = try.GetRequest("http://127.0.0.1:80/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = try.GetRequest("http://127.0.0.1:80/", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
|
||||
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// sleep for 4 seconds to be certain the configured time period has elapsed
|
||||
// then test another request and verify a 200 status code
|
||||
time.Sleep(4 * time.Second)
|
||||
err = try.GetRequest("http://127.0.0.1:80/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// continue requests at 3 second intervals to test the other rate limit time period
|
||||
time.Sleep(3 * time.Second)
|
||||
err = try.GetRequest("http://127.0.0.1:80/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
err = try.GetRequest("http://127.0.0.1:80/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
err = try.GetRequest("http://127.0.0.1:80/", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
|
||||
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
|
|
@ -70,6 +70,36 @@ func (s *TCPSuite) TestMixed(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *TCPSuite) TestTLSOptions(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/tcp/multi-tls-options.toml", struct{}{})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-c.test`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Check that we can use a client tls version <= 1.1 with hostSNI 'whoami-c.test'
|
||||
out, err := guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-c.test", true, tls.VersionTLS11)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Contains, "whoami-no-cert")
|
||||
|
||||
// Check that we can use a client tls version <= 1.2 with hostSNI 'whoami-d.test'
|
||||
out, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS12)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Contains, "whoami-no-cert")
|
||||
|
||||
// Check that we cannot use a client tls version <= 1.1 with hostSNI 'whoami-d.test'
|
||||
_, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS11)
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "protocol version not supported")
|
||||
}
|
||||
|
||||
func (s *TCPSuite) TestNonTLSFallback(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/tcp/non-tls-fallback.toml", struct{}{})
|
||||
defer os.Remove(file)
|
||||
|
@ -191,11 +221,21 @@ func welcome(addr string) (string, error) {
|
|||
}
|
||||
|
||||
func guessWho(addr, serverName string, tlsCall bool) (string, error) {
|
||||
return guessWhoTLSMaxVersion(addr, serverName, tlsCall, 0)
|
||||
}
|
||||
|
||||
func guessWhoTLSMaxVersion(addr, serverName string, tlsCall bool, tlsMaxVersion uint16) (string, error) {
|
||||
var conn net.Conn
|
||||
var err error
|
||||
|
||||
if tlsCall {
|
||||
conn, err = tls.Dial("tcp", addr, &tls.Config{ServerName: serverName, InsecureSkipVerify: true})
|
||||
|
||||
conn, err = tls.Dial("tcp", addr, &tls.Config{
|
||||
ServerName: serverName,
|
||||
InsecureSkipVerify: true,
|
||||
MinVersion: 0,
|
||||
MaxVersion: tlsMaxVersion,
|
||||
})
|
||||
} else {
|
||||
tcpAddr, err2 := net.ResolveTCPAddr("tcp", addr)
|
||||
if err2 != nil {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue