tcp stream (und damit: tls passthrough) ist implementiert

This commit is contained in:
br committed 2026-10-11 01:04:45 +02:00
1 parent 6a5cb95f92
commit fdcd06c93f
16 files changed
+1008 -75

No files matched your search

+159
View File
@@ -0,0 +1,159 @@
package traefik
import (
"errors"
"reflect"
"testing"
)
// TLS-Passthrough für eigene CAs (z. B. tinyCA im Kundenlabor).
func caLabels() map[string]string {
return map[string]string{
"traefik.enable": "true",
"traefik.tcp.routers.ca.rule": "HostSNI(`ca.lab.kunde.de`)",
"traefik.tcp.routers.ca.entrypoints": "websecure",
"traefik.tcp.routers.ca.tls.passthrough": "true",
"traefik.tcp.services.ca.loadbalancer.server.port": "9000",
}
}
func TestFromLabels_TCPPassthrough(t *testing.T) {
got, err := FromLabels("100.64.0.42", caLabels())
if err != nil {
t.Fatalf("unerwarteter Fehler: %v", err)
}
want := &Configuration{TCP: &TCPConfiguration{
Routers: map[string]*TCPRouter{
"ca": {
EntryPoints: []string{"websecure"},
Service: "ca",
Rule: "HostSNI(`ca.lab.kunde.de`)",
TLS: &TCPRouterTLS{Passthrough: true},
},
},
Services: map[string]*TCPService{
"ca": {LoadBalancer: &TCPServersLoadBalancer{Servers: []TCPServer{{Address: "100.64.0.42:9000"}}}},
},
}}
if !reflect.DeepEqual(got, want) {
t.Errorf("Konfiguration weicht ab\n got: %s\nwant: %s", mustJSON(t, got), mustJSON(t, want))
}
}
func TestFromLabels_TCPAttributes(t *testing.T) {
l := caLabels()
delete(l, "traefik.tcp.routers.ca.tls.passthrough")
l["traefik.TCP.Routers.ca.TLS"] = "true"
l["traefik.tcp.routers.ca.priority"] = "7"
l["traefik.tcp.routers.ca.service"] = "ca"
got, err := FromLabels("2001:db8::1", l)
if err != nil {
t.Fatalf("unerwarteter Fehler: %v", err)
}
r := got.TCP.Routers["ca"]
if r.Priority != 7 || r.TLS == nil || r.TLS.Passthrough {
t.Errorf("Router = %+v", r)
}
if addr := got.TCP.Services["ca"].LoadBalancer.Servers[0].Address; addr != "[2001:db8::1]:9000" {
t.Errorf("Adresse = %q", addr)
}
}
// Ohne service-Label bindet sich der Router an den einzigen TCP-Service.
func TestFromLabels_TCPDefaultServiceBinding(t *testing.T) {
got, err := FromLabels("10.0.0.1", caLabels())
if err != nil {
t.Fatal(err)
}
if got.TCP.Routers["ca"].Service != "ca" {
t.Errorf("Service = %q", got.TCP.Routers["ca"].Service)
}
}
// HTTP und TCP im selben Container: getrennte Namensräume, wie bei Traefik.
func TestFromLabels_HTTPAndTCPTogether(t *testing.T) {
l := caLabels()
l["traefik.http.routers.ca.rule"] = "Host(`ca.lab.kunde.de`)"
l["traefik.http.routers.ca.entrypoints"] = "web"
l["traefik.http.services.ca.loadbalancer.server.port"] = "80"
got, err := FromLabels("10.0.0.1", l)
if err != nil {
t.Fatal(err)
}
if got.HTTP.Routers["ca"].Service != "ca" || got.TCP.Routers["ca"].Service != "ca" {
t.Errorf("Bindung falsch: %s", mustJSON(t, got))
}
}
func TestFromLabels_TCPErrors(t *testing.T) {
tests := map[string]struct {
key, value string
want error
}{
"certresolver": {"traefik.tcp.routers.ca.tls.certresolver", "le", ErrUnsupportedLabel},
"middlewares": {"traefik.tcp.routers.ca.middlewares", "x", ErrUnsupportedLabel},
"tcp middlewares": {"traefik.tcp.middlewares.x.ipallowlist.sourcerange", "10.0.0.0/8", ErrUnsupportedLabel},
"proxyprotocol": {"traefik.tcp.services.ca.loadbalancer.proxyprotocol.version", "2", ErrUnsupportedLabel},
"udp": {"traefik.udp.routers.dns.entrypoints", "dns", ErrUnsupportedLabel},
"passthrough kaputt": {"traefik.tcp.routers.ca.tls.passthrough", "vielleicht", ErrInvalidLabel},
"port kaputt": {"traefik.tcp.services.ca.loadbalancer.server.port", "0", ErrInvalidLabel},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
l := caLabels()
l[tt.key] = tt.value
if _, err := FromLabels("10.0.0.1", l); !errors.Is(err, tt.want) {
t.Errorf("erwartet %v, bekommen %v", tt.want, err)
}
})
}
l := caLabels()
delete(l, "traefik.tcp.routers.ca.rule")
if _, err := FromLabels("10.0.0.1", l); !errors.Is(err, ErrIncomplete) {
t.Errorf("fehlende Regel: %v", err)
}
}
func TestMerge_TCP(t *testing.T) {
a, err := FromLabels("10.0.0.1", caLabels())
if err != nil {
t.Fatal(err)
}
b, err := FromLabels("10.0.0.2", map[string]string{
"traefik.enable": "true",
"traefik.tcp.routers.db.rule": "HostSNI(`db.kunde.de`)",
"traefik.tcp.routers.db.tls.passthrough": "true",
"traefik.tcp.services.db.loadbalancer.server.port": "5432",
})
if err != nil {
t.Fatal(err)
}
got, err := Merge(a, b, nil)
if err != nil {
t.Fatalf("Merge: %v", err)
}
if len(got.TCP.Routers) != 2 || len(got.TCP.Services) != 2 || got.HTTP != nil {
t.Errorf("Ergebnis: %s", mustJSON(t, got))
}
c, _ := FromLabels("10.0.0.3", caLabels()) // gleicher Name, andere Adresse
if _, err := Merge(a, c); !errors.Is(err, ErrConflict) {
t.Errorf("erwartet ErrConflict, bekommen %v", err)
}
}
func TestMarshal_TCP(t *testing.T) {
c, err := FromLabels("10.0.0.1", caLabels())
if err != nil {
t.Fatal(err)
}
got, err := c.Marshal(FormatJSON)
if err != nil {
t.Fatal(err)
}
want := `{"tcp":{"routers":{"ca":{"entryPoints":["websecure"],"service":"ca","rule":"HostSNI(` + "`ca.lab.kunde.de`" + `)","tls":{"passthrough":true}}},"services":{"ca":{"loadBalancer":{"servers":[{"address":"10.0.0.1:9000"}]}}}}}`
if string(got) != want {
t.Errorf("JSON\n got: %s\nwant: %s", got, want)
}
}