tcp stream (und damit: tls passthrough) ist implementiert
This commit is contained in:
1 parent
6a5cb95f92
commit
fdcd06c93f
16 files changed
+1008
-75
No files matched your search
+123
-17
@@ -46,6 +46,8 @@ func FromLabels(host string, labels map[string]string) (*Configuration, error) {
|
||||
routers: map[string]*Router{},
|
||||
services: map[string]*serviceSpec{},
|
||||
middlewares: map[string]Middleware{},
|
||||
tcpRouters: map[string]*TCPRouter{},
|
||||
tcpServices: map[string]*serviceSpec{},
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(labels))
|
||||
@@ -93,6 +95,8 @@ type builder struct {
|
||||
routers map[string]*Router
|
||||
services map[string]*serviceSpec
|
||||
middlewares map[string]Middleware
|
||||
tcpRouters map[string]*TCPRouter
|
||||
tcpServices map[string]*serviceSpec
|
||||
errs []error
|
||||
}
|
||||
|
||||
@@ -101,7 +105,7 @@ func (b *builder) apply(key, value string) error {
|
||||
switch strings.ToLower(parts[0]) {
|
||||
case "enable", "docker":
|
||||
return nil
|
||||
case "http":
|
||||
case "http", "tcp":
|
||||
default:
|
||||
return ErrUnsupportedLabel
|
||||
}
|
||||
@@ -112,6 +116,9 @@ func (b *builder) apply(key, value string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("%w: leerer Name", ErrInvalidLabel)
|
||||
}
|
||||
if strings.EqualFold(parts[0], "tcp") {
|
||||
return b.applyTCP(kind, name, strings.ToLower(strings.Join(attr, ".")), value)
|
||||
}
|
||||
switch kind {
|
||||
case "routers":
|
||||
r, ok := b.routers[name]
|
||||
@@ -173,6 +180,65 @@ func setRouterAttr(r *Router, attr, value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyTCP übersetzt traefik.tcp.*. Unterstützt wird nur, was für
|
||||
// TLS-Passthrough nötig ist; TCP-Middlewares und TLS-Terminierung nicht.
|
||||
func (b *builder) applyTCP(kind, name, attr, value string) error {
|
||||
switch kind {
|
||||
case "routers":
|
||||
r, ok := b.tcpRouters[name]
|
||||
if !ok {
|
||||
r = &TCPRouter{}
|
||||
b.tcpRouters[name] = r
|
||||
}
|
||||
return setTCPRouterAttr(r, attr, value)
|
||||
case "services":
|
||||
if attr != "loadbalancer.server.port" {
|
||||
return ErrUnsupportedLabel
|
||||
}
|
||||
s, ok := b.tcpServices[name]
|
||||
if !ok {
|
||||
s = &serviceSpec{}
|
||||
b.tcpServices[name] = s
|
||||
}
|
||||
return setServiceAttr(s, attr, value)
|
||||
default:
|
||||
return ErrUnsupportedLabel
|
||||
}
|
||||
}
|
||||
|
||||
func setTCPRouterAttr(r *TCPRouter, attr, value string) error {
|
||||
switch attr {
|
||||
case "rule":
|
||||
r.Rule = strings.TrimSpace(value)
|
||||
case "entrypoints":
|
||||
r.EntryPoints = splitList(value)
|
||||
case "service":
|
||||
r.Service = strings.TrimSpace(value)
|
||||
case "priority":
|
||||
p, err := strconv.Atoi(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %q ist keine Zahl", ErrInvalidLabel, value)
|
||||
}
|
||||
r.Priority = p
|
||||
case "tls", "tls.passthrough":
|
||||
on, err := strconv.ParseBool(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %q ist kein Bool", ErrInvalidLabel, value)
|
||||
}
|
||||
if attr == "tls.passthrough" {
|
||||
if r.TLS == nil {
|
||||
r.TLS = &TCPRouterTLS{}
|
||||
}
|
||||
r.TLS.Passthrough = on
|
||||
} else if on && r.TLS == nil {
|
||||
r.TLS = &TCPRouterTLS{}
|
||||
}
|
||||
default:
|
||||
return ErrUnsupportedLabel
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureTLS(r *Router) *RouterTLS {
|
||||
if r.TLS == nil {
|
||||
r.TLS = &RouterTLS{}
|
||||
@@ -239,15 +305,20 @@ func setMiddlewareAttr(m Middleware, attr []string, value string) error {
|
||||
}
|
||||
|
||||
func (b *builder) build(host string) *Configuration {
|
||||
cfg := &Configuration{HTTP: b.buildHTTP(host), TCP: b.buildTCP(host)}
|
||||
if (len(b.services) > 0 || len(b.tcpServices) > 0) && strings.TrimSpace(host) == "" {
|
||||
b.errs = append(b.errs, fmt.Errorf("%w: keine Backend-Adresse (host) angegeben", ErrIncomplete))
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (b *builder) buildHTTP(host string) *HTTPConfiguration {
|
||||
if len(b.routers) == 0 && len(b.services) == 0 && len(b.middlewares) == 0 {
|
||||
return &Configuration{}
|
||||
return nil
|
||||
}
|
||||
http := &HTTPConfiguration{}
|
||||
|
||||
if len(b.services) > 0 {
|
||||
if strings.TrimSpace(host) == "" {
|
||||
b.errs = append(b.errs, fmt.Errorf("%w: keine Backend-Adresse (host) angegeben", ErrIncomplete))
|
||||
}
|
||||
http.Services = map[string]*Service{}
|
||||
for name, s := range b.services {
|
||||
if s.port == "" {
|
||||
@@ -271,17 +342,8 @@ func (b *builder) build(host string) *Configuration {
|
||||
if r.Rule == "" {
|
||||
b.errs = append(b.errs, fmt.Errorf("router %q: %w: rule fehlt", name, ErrIncomplete))
|
||||
}
|
||||
if r.Service != "" {
|
||||
continue
|
||||
}
|
||||
// Wie Traefiks Docker-Provider: ohne explizite Angabe wird der
|
||||
// Router an den einzigen Service des Containers gebunden.
|
||||
if len(b.services) != 1 {
|
||||
b.errs = append(b.errs, fmt.Errorf("router %q: %w: service fehlt und ist bei %d Services nicht eindeutig", name, ErrIncomplete, len(b.services)))
|
||||
continue
|
||||
}
|
||||
for svc := range b.services {
|
||||
r.Service = svc
|
||||
if r.Service == "" {
|
||||
r.Service = b.defaultService("router", name, b.services)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,7 +351,51 @@ func (b *builder) build(host string) *Configuration {
|
||||
if len(b.middlewares) > 0 {
|
||||
http.Middlewares = b.middlewares
|
||||
}
|
||||
return &Configuration{HTTP: http}
|
||||
return http
|
||||
}
|
||||
|
||||
func (b *builder) buildTCP(host string) *TCPConfiguration {
|
||||
if len(b.tcpRouters) == 0 && len(b.tcpServices) == 0 {
|
||||
return nil
|
||||
}
|
||||
tcp := &TCPConfiguration{}
|
||||
if len(b.tcpServices) > 0 {
|
||||
tcp.Services = map[string]*TCPService{}
|
||||
for name, s := range b.tcpServices {
|
||||
if s.port == "" {
|
||||
b.errs = append(b.errs, fmt.Errorf("tcp service %q: %w: loadbalancer.server.port fehlt", name, ErrIncomplete))
|
||||
continue
|
||||
}
|
||||
tcp.Services[name] = &TCPService{LoadBalancer: &TCPServersLoadBalancer{
|
||||
Servers: []TCPServer{{Address: net.JoinHostPort(host, s.port)}},
|
||||
}}
|
||||
}
|
||||
}
|
||||
if len(b.tcpRouters) > 0 {
|
||||
tcp.Routers = b.tcpRouters
|
||||
for name, r := range b.tcpRouters {
|
||||
if r.Rule == "" {
|
||||
b.errs = append(b.errs, fmt.Errorf("tcp router %q: %w: rule fehlt", name, ErrIncomplete))
|
||||
}
|
||||
if r.Service == "" {
|
||||
r.Service = b.defaultService("tcp router", name, b.tcpServices)
|
||||
}
|
||||
}
|
||||
}
|
||||
return tcp
|
||||
}
|
||||
|
||||
// defaultService bindet wie Traefiks Docker-Provider einen Router ohne
|
||||
// explizite Angabe an den einzigen Service des Containers.
|
||||
func (b *builder) defaultService(kind, router string, services map[string]*serviceSpec) string {
|
||||
if len(services) != 1 {
|
||||
b.errs = append(b.errs, fmt.Errorf("%s %q: %w: service fehlt und ist bei %d Services nicht eindeutig", kind, router, ErrIncomplete, len(services)))
|
||||
return ""
|
||||
}
|
||||
for svc := range services {
|
||||
return svc
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func splitList(value string) []string {
|
||||
|
||||
Reference in new issue
Block a user