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
|
@ -9,6 +9,7 @@
|
|||
Rule = "foobar"
|
||||
priority = 42
|
||||
[HTTP.Routers.Router0.tls]
|
||||
options = "TLS0"
|
||||
|
||||
[HTTP.Middlewares]
|
||||
|
||||
|
@ -206,6 +207,7 @@
|
|||
Rule = "foobar"
|
||||
[TCP.Routers.TCPRouter0.tls]
|
||||
passthrough = true
|
||||
options = "TLS1"
|
||||
|
||||
[TCP.Services]
|
||||
|
||||
|
|
|
@ -109,6 +109,7 @@ labels:
|
|||
- "traefik.HTTP.Routers.Router0.Rule=foobar"
|
||||
- "traefik.HTTP.Routers.Router0.Service=foobar"
|
||||
- "traefik.HTTP.Routers.Router0.TLS=true"
|
||||
- "traefik.HTTP.Routers.Router0.TLS.options=foo"
|
||||
- "traefik.HTTP.Routers.Router1.EntryPoints=foobar, fiibar"
|
||||
- "traefik.HTTP.Routers.Router1.Middlewares=foobar, fiibar"
|
||||
- "traefik.HTTP.Routers.Router1.Priority=42"
|
||||
|
@ -143,9 +144,11 @@ labels:
|
|||
- "traefik.TCP.Routers.Router0.EntryPoints=foobar, fiibar"
|
||||
- "traefik.TCP.Routers.Router0.Service=foobar"
|
||||
- "traefik.TCP.Routers.Router0.TLS.Passthrough=false"
|
||||
- "traefik.TCP.Routers.Router0.TLS.options=bar"
|
||||
- "traefik.TCP.Routers.Router1.Rule=foobar"
|
||||
- "traefik.TCP.Routers.Router1.EntryPoints=foobar, fiibar"
|
||||
- "traefik.TCP.Routers.Router1.Service=foobar"
|
||||
- "traefik.TCP.Routers.Router1.TLS.Passthrough=false"
|
||||
- "traefik.TCP.Routers.Router1.TLS.options=foobar"
|
||||
- "traefik.TCP.Services.Service0.LoadBalancer.server.Port=42"
|
||||
- "traefik.TCP.Services.Service1.LoadBalancer.server.Port=42"
|
||||
|
|
|
@ -156,7 +156,9 @@ Services are the target for the router.
|
|||
|
||||
### TLS
|
||||
|
||||
When specifying a TLS section, you tell Traefik that the current router is dedicated to HTTPS requests only (and that the router should ignore HTTP (non tls) requests).
|
||||
#### General
|
||||
|
||||
When a TLS section is specified, it instructs Traefik that the current router is dedicated to HTTPS requests only (and that the router should ignore HTTP (non TLS) requests).
|
||||
Traefik will terminate the SSL connections (meaning that it will send decrypted data to the services).
|
||||
|
||||
??? example "Configuring the router to accept HTTPS requests only"
|
||||
|
@ -172,8 +174,7 @@ Traefik will terminate the SSL connections (meaning that it will send decrypted
|
|||
!!! note "HTTPS & ACME"
|
||||
|
||||
In the current version, with [ACME](../../https-tls/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
|
||||
In the near future, options will be available to enable fine-grain control of the TLS parameters.
|
||||
|
||||
|
||||
!!! note "Passthrough"
|
||||
|
||||
On TCP routers, you can configure a passthrough option so that Traefik doesn't terminate the TLS connection.
|
||||
|
@ -196,6 +197,31 @@ Traefik will terminate the SSL connections (meaning that it will send decrypted
|
|||
service = "service-id"
|
||||
```
|
||||
|
||||
#### `Options`
|
||||
|
||||
The `Options` field enables fine-grained control of the TLS parameters.
|
||||
It refers to a [tlsOptions](../../https-tls/overview/#configuration-options) and will be applied only if a `Host` rule is defined.
|
||||
|
||||
??? example "Configuring the tls options"
|
||||
|
||||
```toml
|
||||
[http.routers]
|
||||
[http.routers.Router-1]
|
||||
rule = "Host(`foo-domain`) && Path(`/foo-path/`)"
|
||||
service = "service-id"
|
||||
[http.routers.Router-1.tls] # will terminate the TLS request
|
||||
options = "foo"
|
||||
|
||||
|
||||
[tlsOptions]
|
||||
[tlsOptions.foo]
|
||||
minVersion = "VersionTLS12"
|
||||
cipherSuites = [
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384"
|
||||
]
|
||||
```
|
||||
|
||||
## Configuring TCP Routers
|
||||
|
||||
### General
|
||||
|
@ -269,8 +295,10 @@ Services are the target for the router.
|
|||
|
||||
### TLS
|
||||
|
||||
When specifying a TLS section, you tell Traefik that the current router is dedicated to TLS requests only (and that the router should ignore non-tls requests).
|
||||
By default, Traefik will terminate the SSL connections (meaning that it will send decrypted data to the services), but you can tell Traefik that the request should pass through (keeping the encrypted data) and be forwarded to the service "as is".
|
||||
#### General
|
||||
|
||||
When a TLS section is specified, it instructs Traefik that the current router is dedicated to TLS requests only (and that the router should ignore non-TLS requests).
|
||||
By default, Traefik will terminate the SSL connections (meaning that it will send decrypted data to the services), but Traefik can be configured in order to let the requests pass through (keeping the data encrypted), and be forwarded to the service "as is".
|
||||
|
||||
??? example "Configuring TLS Termination"
|
||||
|
||||
|
@ -296,4 +324,28 @@ By default, Traefik will terminate the SSL connections (meaning that it will sen
|
|||
!!! note "TLS & ACME"
|
||||
|
||||
In the current version, with [ACME](../../https-tls/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
|
||||
In the near future, options will be available to enable fine-grain control of the TLS parameters.
|
||||
|
||||
#### `Options`
|
||||
|
||||
The `Options` field enables fine-grained control of the TLS parameters.
|
||||
It refers to a [tlsOptions](../../https-tls/overview/#configuration-options) and will be applied only if a `HostSNI` rule is defined.
|
||||
|
||||
??? example "Configuring the tls options"
|
||||
|
||||
```toml
|
||||
[tcp.routers]
|
||||
[tcp.routers.Router-1]
|
||||
rule = "Host(`foo-domain`) && Path(`/foo-path/`)"
|
||||
service = "service-id"
|
||||
[tcp.routers.Router-1.tls] # will terminate the TLS request
|
||||
options = "foo"
|
||||
|
||||
|
||||
[tlsOptions]
|
||||
[tlsOptions.foo]
|
||||
minVersion = "VersionTLS12"
|
||||
cipherSuites = [
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384"
|
||||
]
|
||||
```
|
||||
|
|
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 {
|
||||
|
|
|
@ -58,7 +58,7 @@ func Test_doOnJSON(t *testing.T) {
|
|||
"DNSProvider": "",
|
||||
"DelayDontCheckDNS": 0,
|
||||
"ACMELogging": false,
|
||||
"TLSOptions": null
|
||||
"Options": null
|
||||
},
|
||||
"DefaultEntryPoints": [
|
||||
"https",
|
||||
|
@ -141,7 +141,7 @@ func Test_doOnJSON(t *testing.T) {
|
|||
"DNSProvider": "",
|
||||
"DelayDontCheckDNS": 0,
|
||||
"ACMELogging": false,
|
||||
"TLSOptions": null
|
||||
"Options": null
|
||||
},
|
||||
"DefaultEntryPoints": [
|
||||
"https",
|
||||
|
|
|
@ -22,7 +22,9 @@ type Router struct {
|
|||
}
|
||||
|
||||
// RouterTLSConfig holds the TLS configuration for a router
|
||||
type RouterTLSConfig struct{}
|
||||
type RouterTLSConfig struct {
|
||||
Options string `json:"options,omitempty" toml:"options,omitzero"`
|
||||
}
|
||||
|
||||
// TCPRouter holds the router configuration.
|
||||
type TCPRouter struct {
|
||||
|
@ -34,7 +36,8 @@ type TCPRouter struct {
|
|||
|
||||
// RouterTCPTLSConfig holds the TLS configuration for a router
|
||||
type RouterTCPTLSConfig struct {
|
||||
Passthrough bool `json:"passthrough" toml:"passthrough,omitzero"`
|
||||
Passthrough bool `json:"passthrough" toml:"passthrough,omitzero"`
|
||||
Options string `json:"options,omitempty" toml:"options,omitzero"`
|
||||
}
|
||||
|
||||
// LoadBalancerService holds the LoadBalancerService configuration.
|
||||
|
|
|
@ -162,9 +162,11 @@ func TestDecodeConfiguration(t *testing.T) {
|
|||
"traefik.tcp.routers.Router0.entrypoints": "foobar, fiibar",
|
||||
"traefik.tcp.routers.Router0.service": "foobar",
|
||||
"traefik.tcp.routers.Router0.tls.passthrough": "false",
|
||||
"traefik.tcp.routers.Router0.tls.options": "foo",
|
||||
"traefik.tcp.routers.Router1.rule": "foobar",
|
||||
"traefik.tcp.routers.Router1.entrypoints": "foobar, fiibar",
|
||||
"traefik.tcp.routers.Router1.service": "foobar",
|
||||
"traefik.tcp.routers.Router1.tls.options": "foo",
|
||||
"traefik.tcp.routers.Router1.tls.passthrough": "false",
|
||||
"traefik.tcp.services.Service0.loadbalancer.server.Port": "42",
|
||||
"traefik.tcp.services.Service1.loadbalancer.server.Port": "42",
|
||||
|
@ -185,6 +187,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
|||
Rule: "foobar",
|
||||
TLS: &config.RouterTCPTLSConfig{
|
||||
Passthrough: false,
|
||||
Options: "foo",
|
||||
},
|
||||
},
|
||||
"Router1": {
|
||||
|
@ -196,6 +199,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
|||
Rule: "foobar",
|
||||
TLS: &config.RouterTCPTLSConfig{
|
||||
Passthrough: false,
|
||||
Options: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -580,6 +584,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
|||
Rule: "foobar",
|
||||
TLS: &config.RouterTCPTLSConfig{
|
||||
Passthrough: false,
|
||||
Options: "foo",
|
||||
},
|
||||
},
|
||||
"Router1": {
|
||||
|
@ -591,6 +596,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
|||
Rule: "foobar",
|
||||
TLS: &config.RouterTCPTLSConfig{
|
||||
Passthrough: false,
|
||||
Options: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1110,10 +1116,12 @@ func TestEncodeConfiguration(t *testing.T) {
|
|||
"traefik.TCP.Routers.Router0.EntryPoints": "foobar, fiibar",
|
||||
"traefik.TCP.Routers.Router0.Service": "foobar",
|
||||
"traefik.TCP.Routers.Router0.TLS.Passthrough": "false",
|
||||
"traefik.TCP.Routers.Router0.TLS.Options": "foo",
|
||||
"traefik.TCP.Routers.Router1.Rule": "foobar",
|
||||
"traefik.TCP.Routers.Router1.EntryPoints": "foobar, fiibar",
|
||||
"traefik.TCP.Routers.Router1.Service": "foobar",
|
||||
"traefik.TCP.Routers.Router1.TLS.Passthrough": "false",
|
||||
"traefik.TCP.Routers.Router1.TLS.Options": "foo",
|
||||
"traefik.TCP.Services.Service0.LoadBalancer.server.Port": "42",
|
||||
"traefik.TCP.Services.Service1.LoadBalancer.server.Port": "42",
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -128,6 +129,74 @@ func (r *RuntimeConfiguration) PopulateUsedBy() {
|
|||
}
|
||||
}
|
||||
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetRoutersByEntrypoints returns all the http routers by entrypoints name and routers name
|
||||
func (r *RuntimeConfiguration) GetRoutersByEntrypoints(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*RouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*RouterInfo)
|
||||
|
||||
for rtName, rt := range r.Routers {
|
||||
if (tls && rt.TLS == nil) || (!tls && rt.TLS != nil) {
|
||||
continue
|
||||
}
|
||||
|
||||
eps := rt.EntryPoints
|
||||
if len(eps) == 0 {
|
||||
eps = entryPoints
|
||||
}
|
||||
for _, entryPointName := range eps {
|
||||
if !contains(entryPoints, entryPointName) {
|
||||
log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
|
||||
Errorf("entryPoint %q doesn't exist", entryPointName)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||
entryPointsRouters[entryPointName] = make(map[string]*RouterInfo)
|
||||
}
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
}
|
||||
}
|
||||
|
||||
return entryPointsRouters
|
||||
}
|
||||
|
||||
// GetTCPRoutersByEntrypoints returns all the tcp routers by entrypoints name and routers name
|
||||
func (r *RuntimeConfiguration) GetTCPRoutersByEntrypoints(ctx context.Context, entryPoints []string) map[string]map[string]*TCPRouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*TCPRouterInfo)
|
||||
|
||||
for rtName, rt := range r.TCPRouters {
|
||||
eps := rt.EntryPoints
|
||||
if len(eps) == 0 {
|
||||
eps = entryPoints
|
||||
}
|
||||
|
||||
for _, entryPointName := range eps {
|
||||
if !contains(entryPoints, entryPointName) {
|
||||
log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
|
||||
Errorf("entryPoint %q doesn't exist", entryPointName)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||
entryPointsRouters[entryPointName] = make(map[string]*TCPRouterInfo)
|
||||
}
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
}
|
||||
}
|
||||
|
||||
return entryPointsRouters
|
||||
}
|
||||
|
||||
// RouterInfo holds information about a currently running HTTP router
|
||||
type RouterInfo struct {
|
||||
*Router // dynamic configuration
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package config_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
|
@ -688,3 +689,399 @@ func TestPopulateUsedby(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetTCPRoutersByEntrypoints(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
conf config.Configuration
|
||||
entryPoints []string
|
||||
expected map[string]map[string]*config.TCPRouterInfo
|
||||
}{
|
||||
{
|
||||
desc: "Empty Configuration without entrypoint",
|
||||
conf: config.Configuration{},
|
||||
entryPoints: []string{""},
|
||||
expected: map[string]map[string]*config.TCPRouterInfo{},
|
||||
},
|
||||
{
|
||||
desc: "Empty Configuration with unknown entrypoints",
|
||||
conf: config.Configuration{},
|
||||
entryPoints: []string{"foo"},
|
||||
expected: map[string]map[string]*config.TCPRouterInfo{},
|
||||
},
|
||||
{
|
||||
desc: "Valid configuration with an unknown entrypoint",
|
||||
conf: config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
entryPoints: []string{"foo"},
|
||||
expected: map[string]map[string]*config.TCPRouterInfo{},
|
||||
},
|
||||
{
|
||||
desc: "Valid configuration with a known entrypoint",
|
||||
conf: config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
entryPoints: []string{"web"},
|
||||
expected: map[string]map[string]*config.TCPRouterInfo{
|
||||
"web": {
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Valid configuration with multiple known entrypoints",
|
||||
conf: config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
entryPoints: []string{"web", "webs"},
|
||||
expected: map[string]map[string]*config.TCPRouterInfo{
|
||||
"web": {
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
"webs": {
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runtimeConfig := config.NewRuntimeConfig(test.conf)
|
||||
actual := runtimeConfig.GetTCPRoutersByEntrypoints(context.Background(), test.entryPoints)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRoutersByEntrypoints(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
conf config.Configuration
|
||||
entryPoints []string
|
||||
expected map[string]map[string]*config.RouterInfo
|
||||
}{
|
||||
{
|
||||
desc: "Empty Configuration without entrypoint",
|
||||
conf: config.Configuration{},
|
||||
entryPoints: []string{""},
|
||||
expected: map[string]map[string]*config.RouterInfo{},
|
||||
},
|
||||
{
|
||||
desc: "Empty Configuration with unknown entrypoints",
|
||||
conf: config.Configuration{},
|
||||
entryPoints: []string{"foo"},
|
||||
expected: map[string]map[string]*config.RouterInfo{},
|
||||
},
|
||||
{
|
||||
desc: "Valid configuration with an unknown entrypoint",
|
||||
conf: config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
entryPoints: []string{"foo"},
|
||||
expected: map[string]map[string]*config.RouterInfo{},
|
||||
},
|
||||
{
|
||||
desc: "Valid configuration with a known entrypoint",
|
||||
conf: config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
entryPoints: []string{"web"},
|
||||
expected: map[string]map[string]*config.RouterInfo{
|
||||
"web": {
|
||||
"foo": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Valid configuration with multiple known entrypoints",
|
||||
conf: config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
TCP: &config.TCPConfiguration{
|
||||
Routers: map[string]*config.TCPRouter{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
"foobar": {
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
entryPoints: []string{"web", "webs"},
|
||||
expected: map[string]map[string]*config.RouterInfo{
|
||||
"web": {
|
||||
"foo": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
"webs": {
|
||||
"bar": {
|
||||
Router: &config.Router{
|
||||
|
||||
EntryPoints: []string{"webs"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
"foobar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web", "webs"},
|
||||
Service: "myprovider.foobar-service",
|
||||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runtimeConfig := config.NewRuntimeConfig(test.conf)
|
||||
actual := runtimeConfig.GetRoutersByEntrypoints(context.Background(), test.entryPoints, false)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2164,7 +2164,7 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
Name: "Test",
|
||||
Labels: map[string]string{
|
||||
"traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)",
|
||||
"traefik.tcp.routers.foo.tls": "true",
|
||||
"traefik.tcp.routers.foo.tls.options": "foo",
|
||||
"traefik.tcp.services.foo.loadbalancer.server.port": "8080",
|
||||
},
|
||||
NetworkSettings: networkSettings{
|
||||
|
@ -2186,7 +2186,9 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
"foo": {
|
||||
Service: "foo",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
TLS: &config.RouterTCPTLSConfig{},
|
||||
TLS: &config.RouterTCPTLSConfig{
|
||||
Options: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*config.TCPService{
|
||||
|
|
|
@ -580,7 +580,6 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
Name: "Test",
|
||||
Labels: map[string]string{
|
||||
"traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)",
|
||||
"traefik.tcp.routers.foo.tls": "true",
|
||||
"traefik.tcp.services.foo.loadbalancer.server.port": "8080",
|
||||
},
|
||||
Port: "80/tcp",
|
||||
|
@ -595,7 +594,6 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
"foo": {
|
||||
Service: "foo",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
TLS: &config.RouterTCPTLSConfig{},
|
||||
},
|
||||
},
|
||||
Services: map[string]*config.TCPService{
|
||||
|
|
|
@ -2,7 +2,6 @@ package router
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/alice"
|
||||
|
@ -23,33 +22,42 @@ const (
|
|||
)
|
||||
|
||||
// NewManager Creates a new Manager
|
||||
func NewManager(routers map[string]*config.RouterInfo,
|
||||
serviceManager *service.Manager, middlewaresBuilder *middleware.Builder, modifierBuilder *responsemodifiers.Builder,
|
||||
func NewManager(conf *config.RuntimeConfiguration,
|
||||
serviceManager *service.Manager,
|
||||
middlewaresBuilder *middleware.Builder,
|
||||
modifierBuilder *responsemodifiers.Builder,
|
||||
) *Manager {
|
||||
return &Manager{
|
||||
routerHandlers: make(map[string]http.Handler),
|
||||
configs: routers,
|
||||
serviceManager: serviceManager,
|
||||
middlewaresBuilder: middlewaresBuilder,
|
||||
modifierBuilder: modifierBuilder,
|
||||
conf: conf,
|
||||
}
|
||||
}
|
||||
|
||||
// Manager A route/router manager
|
||||
type Manager struct {
|
||||
routerHandlers map[string]http.Handler
|
||||
configs map[string]*config.RouterInfo
|
||||
serviceManager *service.Manager
|
||||
middlewaresBuilder *middleware.Builder
|
||||
modifierBuilder *responsemodifiers.Builder
|
||||
conf *config.RuntimeConfiguration
|
||||
}
|
||||
|
||||
func (m *Manager) getHTTPRouters(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*config.RouterInfo {
|
||||
if m.conf != nil {
|
||||
return m.conf.GetRoutersByEntrypoints(ctx, entryPoints, tls)
|
||||
}
|
||||
|
||||
return make(map[string]map[string]*config.RouterInfo)
|
||||
}
|
||||
|
||||
// BuildHandlers Builds handler for all entry points
|
||||
func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, tls bool) map[string]http.Handler {
|
||||
entryPointsRouters := m.filteredRouters(rootCtx, entryPoints, tls)
|
||||
|
||||
entryPointHandlers := make(map[string]http.Handler)
|
||||
for entryPointName, routers := range entryPointsRouters {
|
||||
|
||||
for entryPointName, routers := range m.getHTTPRouters(rootCtx, entryPoints, tls) {
|
||||
entryPointName := entryPointName
|
||||
ctx := log.With(rootCtx, log.Str(log.EntryPointName, entryPointName))
|
||||
|
||||
|
@ -75,45 +83,6 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
|
|||
return entryPointHandlers
|
||||
}
|
||||
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*config.RouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*config.RouterInfo)
|
||||
|
||||
for rtName, rt := range m.configs {
|
||||
if (tls && rt.TLS == nil) || (!tls && rt.TLS != nil) {
|
||||
continue
|
||||
}
|
||||
|
||||
eps := rt.EntryPoints
|
||||
if len(eps) == 0 {
|
||||
eps = entryPoints
|
||||
}
|
||||
for _, entryPointName := range eps {
|
||||
if !contains(entryPoints, entryPointName) {
|
||||
log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
|
||||
Errorf("entryPoint %q doesn't exist", entryPointName)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.RouterInfo)
|
||||
}
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
}
|
||||
}
|
||||
|
||||
return entryPointsRouters
|
||||
}
|
||||
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.RouterInfo) (http.Handler, error) {
|
||||
router, err := rules.NewRouter()
|
||||
if err != nil {
|
||||
|
@ -124,7 +93,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
ctxRouter := log.With(internal.AddProviderInContext(ctx, routerName), log.Str(log.RouterName, routerName))
|
||||
logger := log.FromContext(ctxRouter)
|
||||
|
||||
handler, err := m.buildRouterHandler(ctxRouter, routerName)
|
||||
handler, err := m.buildRouterHandler(ctxRouter, routerName, routerConfig)
|
||||
if err != nil {
|
||||
routerConfig.Err = err.Error()
|
||||
logger.Error(err)
|
||||
|
@ -149,17 +118,12 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
return chain.Then(router)
|
||||
}
|
||||
|
||||
func (m *Manager) buildRouterHandler(ctx context.Context, routerName string) (http.Handler, error) {
|
||||
func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, routerConfig *config.RouterInfo) (http.Handler, error) {
|
||||
if handler, ok := m.routerHandlers[routerName]; ok {
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
configRouter, ok := m.configs[routerName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no configuration for %s", routerName)
|
||||
}
|
||||
|
||||
handler, err := m.buildHTTPHandler(ctx, configRouter, routerName)
|
||||
handler, err := m.buildHTTPHandler(ctx, routerConfig, routerName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -308,7 +308,7 @@ func TestRouterManager_Get(t *testing.T) {
|
|||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||
|
||||
|
@ -409,7 +409,7 @@ func TestAccessLog(t *testing.T) {
|
|||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||
|
||||
|
@ -695,7 +695,7 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(map[string]*config.MiddlewareInfo{})
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
|
@ -769,7 +769,7 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res})
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package tcp
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
|
@ -12,6 +11,7 @@ import (
|
|||
"github.com/containous/traefik/pkg/server/internal"
|
||||
tcpservice "github.com/containous/traefik/pkg/server/service/tcp"
|
||||
"github.com/containous/traefik/pkg/tcp"
|
||||
"github.com/containous/traefik/pkg/tls"
|
||||
)
|
||||
|
||||
// NewManager Creates a new Manager
|
||||
|
@ -19,29 +19,46 @@ func NewManager(conf *config.RuntimeConfiguration,
|
|||
serviceManager *tcpservice.Manager,
|
||||
httpHandlers map[string]http.Handler,
|
||||
httpsHandlers map[string]http.Handler,
|
||||
tlsConfig *tls.Config,
|
||||
tlsManager *tls.Manager,
|
||||
) *Manager {
|
||||
return &Manager{
|
||||
configs: conf.TCPRouters,
|
||||
serviceManager: serviceManager,
|
||||
httpHandlers: httpHandlers,
|
||||
httpsHandlers: httpsHandlers,
|
||||
tlsConfig: tlsConfig,
|
||||
tlsManager: tlsManager,
|
||||
conf: conf,
|
||||
}
|
||||
}
|
||||
|
||||
// Manager is a route/router manager
|
||||
type Manager struct {
|
||||
configs map[string]*config.TCPRouterInfo
|
||||
serviceManager *tcpservice.Manager
|
||||
httpHandlers map[string]http.Handler
|
||||
httpsHandlers map[string]http.Handler
|
||||
tlsConfig *tls.Config
|
||||
tlsManager *tls.Manager
|
||||
conf *config.RuntimeConfiguration
|
||||
}
|
||||
|
||||
func (m *Manager) getTCPRouters(ctx context.Context, entryPoints []string) map[string]map[string]*config.TCPRouterInfo {
|
||||
if m.conf != nil {
|
||||
return m.conf.GetTCPRoutersByEntrypoints(ctx, entryPoints)
|
||||
}
|
||||
|
||||
return make(map[string]map[string]*config.TCPRouterInfo)
|
||||
}
|
||||
|
||||
func (m *Manager) getHTTPRouters(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*config.RouterInfo {
|
||||
if m.conf != nil {
|
||||
return m.conf.GetRoutersByEntrypoints(ctx, entryPoints, tls)
|
||||
}
|
||||
|
||||
return make(map[string]map[string]*config.RouterInfo)
|
||||
}
|
||||
|
||||
// BuildHandlers builds the handlers for the given entrypoints
|
||||
func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) map[string]*tcp.Router {
|
||||
entryPointsRouters := m.filteredRouters(rootCtx, entryPoints)
|
||||
entryPointsRouters := m.getTCPRouters(rootCtx, entryPoints)
|
||||
entryPointsRoutersHTTP := m.getHTTPRouters(rootCtx, entryPoints, true)
|
||||
|
||||
entryPointHandlers := make(map[string]*tcp.Router)
|
||||
for _, entryPointName := range entryPoints {
|
||||
|
@ -51,7 +68,7 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m
|
|||
|
||||
ctx := log.With(rootCtx, log.Str(log.EntryPointName, entryPointName))
|
||||
|
||||
handler, err := m.buildEntryPointHandler(ctx, routers, m.httpHandlers[entryPointName], m.httpsHandlers[entryPointName])
|
||||
handler, err := m.buildEntryPointHandler(ctx, routers, entryPointsRoutersHTTP[entryPointName], m.httpHandlers[entryPointName], m.httpsHandlers[entryPointName])
|
||||
if err != nil {
|
||||
log.FromContext(ctx).Error(err)
|
||||
continue
|
||||
|
@ -61,10 +78,50 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m
|
|||
return entryPointHandlers
|
||||
}
|
||||
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.TCPRouterInfo, handlerHTTP http.Handler, handlerHTTPS http.Handler) (*tcp.Router, error) {
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.TCPRouterInfo, configsHTTP map[string]*config.RouterInfo, handlerHTTP http.Handler, handlerHTTPS http.Handler) (*tcp.Router, error) {
|
||||
router := &tcp.Router{}
|
||||
router.HTTPHandler(handlerHTTP)
|
||||
router.HTTPSHandler(handlerHTTPS, m.tlsConfig)
|
||||
|
||||
defaultTLSConf, err := m.tlsManager.Get("default", "default")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
router.HTTPSHandler(handlerHTTPS, defaultTLSConf)
|
||||
|
||||
for routerHTTPName, routerHTTPConfig := range configsHTTP {
|
||||
if len(routerHTTPConfig.TLS.Options) == 0 || routerHTTPConfig.TLS.Options == "default" {
|
||||
continue
|
||||
}
|
||||
|
||||
ctxRouter := log.With(internal.AddProviderInContext(ctx, routerHTTPName), log.Str(log.RouterName, routerHTTPName))
|
||||
logger := log.FromContext(ctxRouter)
|
||||
|
||||
domains, err := rules.ParseDomains(routerHTTPConfig.Rule)
|
||||
if err != nil {
|
||||
routerErr := fmt.Errorf("invalid rule %s, error: %v", routerHTTPConfig.Rule, err)
|
||||
routerHTTPConfig.Err = routerErr.Error()
|
||||
logger.Debug(routerErr)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(domains) == 0 {
|
||||
logger.Warnf("The 'default' TLS options will be applied instead of %q as no domain has been found in the rule", routerHTTPConfig.TLS.Options)
|
||||
}
|
||||
|
||||
for _, domain := range domains {
|
||||
if routerHTTPConfig.TLS != nil {
|
||||
tlsConf, err := m.tlsManager.Get("default", routerHTTPConfig.TLS.Options)
|
||||
if err != nil {
|
||||
routerHTTPConfig.Err = err.Error()
|
||||
logger.Debug(err)
|
||||
continue
|
||||
}
|
||||
|
||||
router.AddRouteHTTPTLS(domain, tlsConf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for routerName, routerConfig := range configs {
|
||||
ctxRouter := log.With(internal.AddProviderInContext(ctx, routerName), log.Str(log.RouterName, routerName))
|
||||
|
@ -92,7 +149,19 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
if routerConfig.TLS.Passthrough {
|
||||
router.AddRoute(domain, handler)
|
||||
} else {
|
||||
router.AddRouteTLS(domain, handler, m.tlsConfig)
|
||||
configName := "default"
|
||||
if len(routerConfig.TLS.Options) > 0 {
|
||||
configName = routerConfig.TLS.Options
|
||||
}
|
||||
|
||||
tlsConf, err := m.tlsManager.Get("default", configName)
|
||||
if err != nil {
|
||||
routerConfig.Err = err.Error()
|
||||
logger.Debug(err)
|
||||
continue
|
||||
}
|
||||
|
||||
router.AddRouteTLS(domain, handler, tlsConf)
|
||||
}
|
||||
case domain == "*":
|
||||
router.AddCatchAllNoTLS(handler)
|
||||
|
@ -104,39 +173,3 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
|
||||
return router, nil
|
||||
}
|
||||
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map[string]map[string]*config.TCPRouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*config.TCPRouterInfo)
|
||||
|
||||
for rtName, rt := range m.configs {
|
||||
eps := rt.EntryPoints
|
||||
if len(eps) == 0 {
|
||||
eps = entryPoints
|
||||
}
|
||||
|
||||
for _, entryPointName := range eps {
|
||||
if !contains(entryPoints, entryPointName) {
|
||||
log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
|
||||
Errorf("entryPoint %q doesn't exist", entryPointName)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.TCPRouterInfo)
|
||||
}
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
}
|
||||
}
|
||||
|
||||
return entryPointsRouters
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/server/service/tcp"
|
||||
"github.com/containous/traefik/pkg/tls"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -42,6 +43,10 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
TLS: &config.RouterTCPTLSConfig{
|
||||
Passthrough: false,
|
||||
Options: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
|
@ -50,6 +55,10 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
TLS: &config.RouterTCPTLSConfig{
|
||||
Passthrough: false,
|
||||
Options: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -191,8 +200,21 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
TCPRouters: test.routerConfig,
|
||||
}
|
||||
serviceManager := tcp.NewManager(conf)
|
||||
tlsManager := tls.NewManager()
|
||||
tlsManager.UpdateConfigs(
|
||||
map[string]tls.Store{},
|
||||
map[string]tls.TLS{
|
||||
"foo": {
|
||||
MinVersion: "VersionTLS12",
|
||||
},
|
||||
"bar": {
|
||||
MinVersion: "VersionTLS11",
|
||||
},
|
||||
},
|
||||
[]*tls.Configuration{})
|
||||
|
||||
routerManager := NewManager(conf, serviceManager,
|
||||
nil, nil, nil)
|
||||
nil, nil, tlsManager)
|
||||
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints)
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
@ -71,20 +70,21 @@ func (s *Server) loadConfigurationTCP(configurations config.Configurations) map[
|
|||
|
||||
rtConf := config.NewRuntimeConfig(conf)
|
||||
handlersNonTLS, handlersTLS := s.createHTTPHandlers(ctx, rtConf, entryPoints)
|
||||
routersTCP := s.createTCPRouters(ctx, rtConf, entryPoints, handlersNonTLS, handlersTLS, s.tlsManager.Get("default", "default"))
|
||||
routersTCP := s.createTCPRouters(ctx, rtConf, entryPoints, handlersNonTLS, handlersTLS)
|
||||
rtConf.PopulateUsedBy()
|
||||
|
||||
return routersTCP
|
||||
}
|
||||
|
||||
// the given configuration must not be nil. its fields will get mutated.
|
||||
func (s *Server) createTCPRouters(ctx context.Context, configuration *config.RuntimeConfiguration, entryPoints []string, handlers map[string]http.Handler, handlersTLS map[string]http.Handler, tlsConfig *tls.Config) map[string]*tcpCore.Router {
|
||||
func (s *Server) createTCPRouters(ctx context.Context, configuration *config.RuntimeConfiguration, entryPoints []string, handlers map[string]http.Handler, handlersTLS map[string]http.Handler) map[string]*tcpCore.Router {
|
||||
if configuration == nil {
|
||||
return make(map[string]*tcpCore.Router)
|
||||
}
|
||||
|
||||
serviceManager := tcp.NewManager(configuration)
|
||||
routerManager := routertcp.NewManager(configuration, serviceManager, handlers, handlersTLS, tlsConfig)
|
||||
|
||||
routerManager := routertcp.NewManager(configuration, serviceManager, handlers, handlersTLS, s.tlsManager)
|
||||
|
||||
return routerManager.BuildHandlers(ctx, entryPoints)
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func (s *Server) createHTTPHandlers(ctx context.Context, configuration *config.R
|
|||
serviceManager := service.NewManager(configuration.Services, s.defaultRoundTripper)
|
||||
middlewaresBuilder := middleware.NewBuilder(configuration.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(configuration.Middlewares)
|
||||
routerManager := router.NewManager(configuration.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
routerManager := router.NewManager(configuration, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlersNonTLS := routerManager.BuildHandlers(ctx, entryPoints, false)
|
||||
handlersTLS := routerManager.BuildHandlers(ctx, entryPoints, true)
|
||||
|
|
|
@ -14,13 +14,14 @@ import (
|
|||
|
||||
// Router is a TCP router
|
||||
type Router struct {
|
||||
routingTable map[string]Handler
|
||||
httpForwarder Handler
|
||||
httpsForwarder Handler
|
||||
httpHandler http.Handler
|
||||
httpsHandler http.Handler
|
||||
httpsTLSConfig *tls.Config
|
||||
catchAllNoTLS Handler
|
||||
routingTable map[string]Handler
|
||||
httpForwarder Handler
|
||||
httpsForwarder Handler
|
||||
httpHandler http.Handler
|
||||
httpsHandler http.Handler
|
||||
httpsTLSConfig *tls.Config // default TLS config
|
||||
catchAllNoTLS Handler
|
||||
hostHTTPTLSConfig map[string]*tls.Config // TLS configs keyed by SNI
|
||||
}
|
||||
|
||||
// ServeTCP forwards the connection to the right TCP/HTTP handler
|
||||
|
@ -84,6 +85,15 @@ func (r *Router) AddRouteTLS(sniHost string, target Handler, config *tls.Config)
|
|||
})
|
||||
}
|
||||
|
||||
// AddRouteHTTPTLS defines a handler for a given sniHost and sets the matching tlsConfig
|
||||
func (r *Router) AddRouteHTTPTLS(sniHost string, config *tls.Config) {
|
||||
if r.hostHTTPTLSConfig == nil {
|
||||
r.hostHTTPTLSConfig = map[string]*tls.Config{}
|
||||
}
|
||||
log.Debugf("adding route %s with minversion %d", sniHost, config.MinVersion)
|
||||
r.hostHTTPTLSConfig[sniHost] = config
|
||||
}
|
||||
|
||||
// AddCatchAllNoTLS defines the fallback tcp handler
|
||||
func (r *Router) AddCatchAllNoTLS(handler Handler) {
|
||||
r.catchAllNoTLS = handler
|
||||
|
@ -116,6 +126,10 @@ func (r *Router) HTTPForwarder(handler Handler) {
|
|||
|
||||
// HTTPSForwarder sets the tcp handler that will forward the TLS connections to an http handler
|
||||
func (r *Router) HTTPSForwarder(handler Handler) {
|
||||
for sniHost, tlsConf := range r.hostHTTPTLSConfig {
|
||||
r.AddRouteTLS(sniHost, handler, tlsConf)
|
||||
}
|
||||
|
||||
r.httpsForwarder = &TLSHandler{
|
||||
Next: handler,
|
||||
Config: r.httpsTLSConfig,
|
||||
|
|
|
@ -67,14 +67,19 @@ func (m *Manager) UpdateConfigs(stores map[string]Store, configs map[string]TLS,
|
|||
}
|
||||
}
|
||||
|
||||
// Get gets the tls configuration to use for a given store / configuration
|
||||
func (m *Manager) Get(storeName string, configName string) *tls.Config {
|
||||
// Get gets the TLS configuration to use for a given store / configuration
|
||||
func (m *Manager) Get(storeName string, configName string) (*tls.Config, error) {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
|
||||
config, ok := m.configs[configName]
|
||||
if !ok && configName != "default" {
|
||||
return nil, fmt.Errorf("unknown TLS options: %s", configName)
|
||||
}
|
||||
|
||||
store := m.getStore(storeName)
|
||||
|
||||
tlsConfig, err := buildTLSConfig(m.configs[configName])
|
||||
tlsConfig, err := buildTLSConfig(config)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
tlsConfig = &tls.Config{}
|
||||
|
@ -106,7 +111,7 @@ func (m *Manager) Get(storeName string, configName string) *tls.Config {
|
|||
log.WithoutContext().Debugf("Serving default certificate for request: %q", domainToCheck)
|
||||
return store.DefaultCertificate, nil
|
||||
}
|
||||
return tlsConfig
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
func (m *Manager) getStore(storeName string) *CertificateStore {
|
||||
|
|
Loading…
Reference in a new issue