mirror of
https://github.com/traefik/traefik.git
synced 2026-06-18 19:38:23 +03:00
Fix routers with same host, different tlsoptions on different entryPoint
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints.websecure]
|
||||
address = ":4443"
|
||||
|
||||
[entryPoints.websecure2]
|
||||
address = ":4444"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
## dynamic configuration ##
|
||||
|
||||
# --- Same host, same options, same entryPoint: no conflict, the options are applied. ---
|
||||
[http.routers.same-1]
|
||||
rule = "Host(`same.www.snitest.com`)"
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
[http.routers.same-1.tls]
|
||||
options = "tls12"
|
||||
|
||||
[http.routers.same-2]
|
||||
rule = "Host(`same.www.snitest.com`) && PathPrefix(`/same`)"
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
[http.routers.same-2.tls]
|
||||
options = "tls12"
|
||||
|
||||
# --- Same host, different options, same entryPoint: conflict, fallback to default options. ---
|
||||
[http.routers.conflict-1]
|
||||
rule = "Host(`conflict.www.snitest.com`)"
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
[http.routers.conflict-1.tls]
|
||||
options = "tls12"
|
||||
|
||||
[http.routers.conflict-2]
|
||||
rule = "Host(`conflict.www.snitest.com`) && PathPrefix(`/conflict`)"
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
[http.routers.conflict-2.tls]
|
||||
options = "tls13"
|
||||
|
||||
# --- Same host, different options, different entryPoints: no conflict, each entryPoint keeps its own options. ---
|
||||
[http.routers.cross-ep1]
|
||||
rule = "Host(`cross.www.snitest.com`)"
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
[http.routers.cross-ep1.tls]
|
||||
options = "tls12"
|
||||
|
||||
[http.routers.cross-ep2]
|
||||
rule = "Host(`cross.www.snitest.com`)"
|
||||
entryPoints = ["websecure2"]
|
||||
service = "service1"
|
||||
[http.routers.cross-ep2.tls]
|
||||
options = "tls13"
|
||||
|
||||
# --- Domain fronting (Host header != SNI): same options follow the header, different options are rejected. ---
|
||||
[http.routers.df-a]
|
||||
rule = "Host(`df-a.www.snitest.com`)"
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
[http.routers.df-a.tls]
|
||||
options = "tls12"
|
||||
|
||||
[http.routers.df-b]
|
||||
rule = "Host(`df-b.www.snitest.com`)"
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
[http.routers.df-b.tls]
|
||||
options = "tls12"
|
||||
|
||||
[http.routers.df-c]
|
||||
rule = "Host(`df-c.www.snitest.com`)"
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
[http.routers.df-c.tls]
|
||||
options = "tls13"
|
||||
|
||||
[http.services.service1]
|
||||
[[http.services.service1.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:9010"
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "fixtures/https/wildcard.www.snitest.com.cert"
|
||||
keyFile = "fixtures/https/wildcard.www.snitest.com.key"
|
||||
|
||||
[tls.options]
|
||||
[tls.options.tls12]
|
||||
maxVersion = "VersionTLS12"
|
||||
[tls.options.tls13]
|
||||
minVersion = "VersionTLS13"
|
||||
+147
-1
@@ -258,7 +258,6 @@ func (s *HTTPSSuite) TestWithTLSOptions() {
|
||||
}
|
||||
|
||||
// TestWithConflictingTLSOptions checks that routers with same SNI but different TLS options get fallbacked to the default TLS options.
|
||||
|
||||
func (s *HTTPSSuite) TestWithConflictingTLSOptions() {
|
||||
file := s.adaptFile("fixtures/https/https_tls_options.toml", struct{}{})
|
||||
s.traefikCmd(withConfigFile(file))
|
||||
@@ -1173,6 +1172,153 @@ func (s *HTTPSSuite) TestWithDomainFronting() {
|
||||
}
|
||||
}
|
||||
|
||||
// TestWithTLSOptionsConflict checks how TLS options are resolved when several routers
|
||||
// target the same host (SNI), across the different conflict situations:
|
||||
// - same options on the same entryPoint: no conflict, the options are applied;
|
||||
// - different options on the same entryPoint: conflict, fallback to the default options;
|
||||
// - different options on different entryPoints: no conflict, each entryPoint keeps its
|
||||
// own options (they are selected independently on each listener);
|
||||
// - domain fronting (Host header != SNI): allowed when both resolve to the same options,
|
||||
// rejected with a 421 otherwise.
|
||||
//
|
||||
// The effective TLS options are probed through the negotiated TLS version: the "tls12"
|
||||
// options cap the version to TLS 1.2, while the "tls13" options require at least TLS 1.3.
|
||||
func (s *HTTPSSuite) TestWithTLSOptionsConflict() {
|
||||
backend := startTestServer("9010", http.StatusOK, "server1")
|
||||
defer backend.Close()
|
||||
|
||||
file := s.adaptFile("fixtures/https/https_tls_options_conflict.toml", struct{}{})
|
||||
s.traefikCmd(withConfigFile(file))
|
||||
|
||||
// wait for Traefik
|
||||
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`cross.www.snitest.com`)"))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
addr string // entryPoint address to reach
|
||||
hostHeader string
|
||||
serverName string // SNI
|
||||
minVersion uint16 // 0 means the crypto/tls library default
|
||||
maxVersion uint16 // 0 means the crypto/tls library default
|
||||
// expectHandshakeError is set when the TLS handshake itself is expected to fail
|
||||
// (i.e. the probed options reject the client's TLS version). Otherwise
|
||||
// expectedStatusCode is asserted on the HTTP response.
|
||||
expectHandshakeError bool
|
||||
expectedStatusCode int
|
||||
}{
|
||||
// Same host, same options, same entryPoint: no conflict, the "tls12" options are applied.
|
||||
{
|
||||
desc: "same options / same entryPoint: TLS 1.2 client is accepted",
|
||||
addr: "127.0.0.1:4443",
|
||||
hostHeader: "same.www.snitest.com",
|
||||
serverName: "same.www.snitest.com",
|
||||
maxVersion: tls.VersionTLS12,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "same options / same entryPoint: TLS 1.3 client is rejected (maxVersion TLS1.2 enforced)",
|
||||
addr: "127.0.0.1:4443",
|
||||
hostHeader: "same.www.snitest.com",
|
||||
serverName: "same.www.snitest.com",
|
||||
minVersion: tls.VersionTLS13,
|
||||
expectHandshakeError: true,
|
||||
},
|
||||
|
||||
// Same host, different options, same entryPoint: conflict, both routers fall back to the default options.
|
||||
{
|
||||
desc: "conflicting options / same entryPoint: TLS 1.3 client is accepted (default options used)",
|
||||
addr: "127.0.0.1:4443",
|
||||
hostHeader: "conflict.www.snitest.com",
|
||||
serverName: "conflict.www.snitest.com",
|
||||
minVersion: tls.VersionTLS13,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "conflicting options / same entryPoint: TLS 1.2 client is accepted (default options used)",
|
||||
addr: "127.0.0.1:4443",
|
||||
hostHeader: "conflict.www.snitest.com",
|
||||
serverName: "conflict.www.snitest.com",
|
||||
maxVersion: tls.VersionTLS12,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
|
||||
// Same host, different options, different entryPoints: no conflict, each entryPoint keeps its own options.
|
||||
{
|
||||
desc: "different entryPoints: websecure keeps tls12, TLS 1.2 client is accepted",
|
||||
addr: "127.0.0.1:4443",
|
||||
hostHeader: "cross.www.snitest.com",
|
||||
serverName: "cross.www.snitest.com",
|
||||
maxVersion: tls.VersionTLS12,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "different entryPoints: websecure keeps tls12, TLS 1.3 client is rejected",
|
||||
addr: "127.0.0.1:4443",
|
||||
hostHeader: "cross.www.snitest.com",
|
||||
serverName: "cross.www.snitest.com",
|
||||
minVersion: tls.VersionTLS13,
|
||||
expectHandshakeError: true,
|
||||
},
|
||||
{
|
||||
desc: "different entryPoints: websecure2 keeps tls13, TLS 1.3 client is accepted",
|
||||
addr: "127.0.0.1:4444",
|
||||
hostHeader: "cross.www.snitest.com",
|
||||
serverName: "cross.www.snitest.com",
|
||||
minVersion: tls.VersionTLS13,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "different entryPoints: websecure2 keeps tls13, TLS 1.2 client is rejected",
|
||||
addr: "127.0.0.1:4444",
|
||||
hostHeader: "cross.www.snitest.com",
|
||||
serverName: "cross.www.snitest.com",
|
||||
maxVersion: tls.VersionTLS12,
|
||||
expectHandshakeError: true,
|
||||
},
|
||||
|
||||
// Domain fronting (Host header != SNI) on the same entryPoint.
|
||||
{
|
||||
desc: "domain fronting / same options: request follows the Host header (200)",
|
||||
addr: "127.0.0.1:4443",
|
||||
hostHeader: "df-a.www.snitest.com",
|
||||
serverName: "df-b.www.snitest.com",
|
||||
maxVersion: tls.VersionTLS12,
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "domain fronting / different options: request is misdirected (421)",
|
||||
addr: "127.0.0.1:4443",
|
||||
hostHeader: "df-a.www.snitest.com",
|
||||
serverName: "df-c.www.snitest.com",
|
||||
minVersion: tls.VersionTLS13,
|
||||
expectedStatusCode: http.StatusMisdirectedRequest,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: test.serverName,
|
||||
MinVersion: test.minVersion,
|
||||
MaxVersion: test.maxVersion,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://"+test.addr+"/", nil)
|
||||
require.NoError(s.T(), err)
|
||||
req.Host = test.hostHeader
|
||||
|
||||
if test.expectHandshakeError {
|
||||
_, err = (&http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}).Do(req)
|
||||
assert.ErrorContains(s.T(), err, "tls:", "test %q should fail the TLS handshake", test.desc)
|
||||
continue
|
||||
}
|
||||
|
||||
err = try.RequestWithTransport(req, 2*time.Second, &http.Transport{TLSClientConfig: tlsConfig}, try.StatusCodeIs(test.expectedStatusCode))
|
||||
assert.NoError(s.T(), err, "test %q failed with: %v", test.desc, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWithInvalidTLSOption verifies the behavior when using an invalid tlsOption configuration.
|
||||
func (s *HTTPSSuite) TestWithInvalidTLSOption() {
|
||||
backend := startTestServer("9010", http.StatusOK, "server1")
|
||||
|
||||
@@ -675,11 +675,11 @@ func (s *SimpleSuite) TestRouterConfigErrors() {
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// router4 is enabled, but in warning state because its tls options conf was messed up
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router4@file", 1000*time.Millisecond, try.BodyContains(`"status":"warning"`))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/websecure-router4@file", 1000*time.Millisecond, try.BodyContains(`"status":"warning"`))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// router5 is disabled because its middleware conf is broken
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router5@file", 1000*time.Millisecond, try.BodyContains())
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/websecure-router5@file", 1000*time.Millisecond, try.BodyContains())
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user