Fix concatenation of IPv6 addresses and ports

This commit is contained in:
Harold Ozouf 2020-12-04 20:56:04 +01:00 committed by GitHub
parent 121eaced49
commit 7403b6fb82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 403 additions and 11 deletions

View file

@ -119,6 +119,35 @@ subsets:
- name: websecure2
port: 8443
---
apiVersion: v1
kind: Service
metadata:
name: whoami-ipv6
namespace: default
spec:
ports:
- name: web
port: 8080
selector:
app: traefiklabs
task: whoami-ipv6
---
kind: Endpoints
apiVersion: v1
metadata:
name: whoami-ipv6
namespace: default
subsets:
- addresses:
- ip: "2001:db8:85a3:8d3:1319:8a2e:370:7348"
ports:
- name: web
port: 8080
---
apiVersion: v1
kind: Service
@ -157,5 +186,16 @@ spec:
protocol: TCP
port: 443
---
apiVersion: v1
kind: Service
metadata:
name: external-svc-with-ipv6
namespace: default
spec:
externalName: "2001:db8:85a3:8d3:1319:8a2e:370:7347"
type: ExternalName
ports:
- name: http
protocol: TCP
port: 8080

View file

@ -132,6 +132,36 @@ subsets:
- name: myapp4
port: 8084
---
apiVersion: v1
kind: Service
metadata:
name: whoamitcp-ipv6
namespace: default
spec:
ports:
- name: myapp-ipv6
port: 8080
selector:
app: traefiklabs
task: whoamitcp-ipv6
---
kind: Endpoints
apiVersion: v1
metadata:
name: whoamitcp-ipv6
namespace: default
subsets:
- addresses:
- ip: "fd00:10:244:0:1::3"
- ip: "2001:db8:85a3:8d3:1319:8a2e:370:7348"
ports:
- name: myapp-ipv6
port: 8080
---
apiVersion: v1
kind: Service
@ -167,4 +197,14 @@ spec:
type: ExternalName
ports:
- name: http
protocol: TCP
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: external.service.with.ipv6
namespace: default
spec:
externalName: "fe80::200:5aee:feaa:20a2"
type: ExternalName

View file

@ -0,0 +1,17 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- match: HostSNI(`*`)
services:
- name: whoamitcp-ipv6
port: 8080
- name: external.service.with.ipv6
port: 8080

View file

@ -101,3 +101,32 @@ subsets:
ports:
- name: myapp4
port: 8084
---
apiVersion: v1
kind: Service
metadata:
name: whoamiudp-ipv6
namespace: default
spec:
ports:
- name: myapp-ipv6
port: 8080
selector:
app: traefiklabs
task: whoamiudp-ipv6
---
kind: Endpoints
apiVersion: v1
metadata:
name: whoamiudp-ipv6
namespace: default
subsets:
- addresses:
- ip: "fd00:10:244:0:1::3"
ports:
- name: myapp-ipv6
port: 8080

View file

@ -0,0 +1,14 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- services:
- name: whoamiudp-ipv6
port: 8080

View file

@ -0,0 +1,18 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- match: Host(`foo.com`) && PathPrefix(`/bar`)
kind: Rule
services:
- name: whoami-ipv6
port: 8080
- name: external-svc-with-ipv6
port: 8080

View file

@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"
"net"
"strconv"
"strings"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
@ -301,8 +303,10 @@ func (c configBuilder) loadServers(fallbackNamespace string, svc v1alpha1.LoadBa
return nil, err
}
hostPort := net.JoinHostPort(service.Spec.ExternalName, strconv.Itoa(int(svcPort.Port)))
return append(servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s:%d", protocol, service.Spec.ExternalName, svcPort.Port),
URL: fmt.Sprintf("%s://%s", protocol, hostPort),
}), nil
}
@ -336,8 +340,10 @@ func (c configBuilder) loadServers(fallbackNamespace string, svc v1alpha1.LoadBa
}
for _, addr := range subset.Addresses {
hostPort := net.JoinHostPort(addr.IP, strconv.Itoa(int(port)))
servers = append(servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s:%d", protocol, addr.IP, port),
URL: fmt.Sprintf("%s://%s", protocol, hostPort),
})
}
}

View file

@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"
"net"
"strconv"
"strings"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
@ -165,7 +167,7 @@ func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([
var servers []dynamic.TCPServer
if service.Spec.Type == corev1.ServiceTypeExternalName {
servers = append(servers, dynamic.TCPServer{
Address: fmt.Sprintf("%s:%d", service.Spec.ExternalName, svcPort.Port),
Address: net.JoinHostPort(service.Spec.ExternalName, strconv.Itoa(int(svcPort.Port))),
})
} else {
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
@ -196,7 +198,7 @@ func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([
for _, addr := range subset.Addresses {
servers = append(servers, dynamic.TCPServer{
Address: fmt.Sprintf("%s:%d", addr.IP, port),
Address: net.JoinHostPort(addr.IP, strconv.Itoa(int(port))),
})
}
}

View file

@ -987,6 +987,128 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "Ingress Route with IPv6 backends",
paths: []string{
"services.yml", "with_ipv6.yml",
"tcp/services.yml", "tcp/with_ipv6.yml",
"udp/services.yml", "udp/with_ipv6.yml",
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{
"default-test.route-0": {
EntryPoints: []string{"foo"},
Service: "default-test.route-0",
},
},
Services: map[string]*dynamic.UDPService{
"default-test.route-0": {
LoadBalancer: &dynamic.UDPServersLoadBalancer{
Servers: []dynamic.UDPServer{
{
Address: "[fd00:10:244:0:1::3]:8080",
},
},
},
},
},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"default-test.route-673acf455cb2dab0b43a": {
EntryPoints: []string{"foo"},
Service: "default-test.route-673acf455cb2dab0b43a",
Rule: "HostSNI(`*`)",
},
},
Services: map[string]*dynamic.TCPService{
"default-test.route-673acf455cb2dab0b43a": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "default-test.route-673acf455cb2dab0b43a-whoamitcp-ipv6-8080",
Weight: func(i int) *int { return &i }(1),
},
{
Name: "default-test.route-673acf455cb2dab0b43a-external.service.with.ipv6-8080",
Weight: func(i int) *int { return &i }(1),
},
},
},
},
"default-test.route-673acf455cb2dab0b43a-whoamitcp-ipv6-8080": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "[fd00:10:244:0:1::3]:8080",
},
{
Address: "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8080",
},
},
},
},
"default-test.route-673acf455cb2dab0b43a-external.service.with.ipv6-8080": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "[fe80::200:5aee:feaa:20a2]:8080",
},
},
},
},
},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"foo"},
Service: "default-test-route-6b204d94623b3df4370c",
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-whoami-ipv6-8080": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8080",
},
},
PassHostHeader: func(i bool) *bool { return &i }(true),
},
},
"default-external-svc-with-ipv6-8080": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://[2001:db8:85a3:8d3:1319:8a2e:370:7347]:8080",
},
},
PassHostHeader: func(i bool) *bool { return &i }(true),
},
},
"default-test-route-6b204d94623b3df4370c": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "default-whoami-ipv6-8080",
Weight: func(i int) *int { return &i }(1),
},
{
Name: "default-external-svc-with-ipv6-8080",
Weight: func(i int) *int { return &i }(1),
},
},
},
},
},
},
TLS: &dynamic.TLSConfiguration{},
},
},
}
for _, test := range testCases {

View file

@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"
"net"
"strconv"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
"github.com/traefik/traefik/v2/pkg/log"
@ -121,7 +123,7 @@ func loadUDPServers(client Client, namespace string, svc v1alpha1.ServiceUDP) ([
var servers []dynamic.UDPServer
if service.Spec.Type == corev1.ServiceTypeExternalName {
servers = append(servers, dynamic.UDPServer{
Address: fmt.Sprintf("%s:%d", service.Spec.ExternalName, portSpec.Port),
Address: net.JoinHostPort(service.Spec.ExternalName, strconv.Itoa(int(portSpec.Port))),
})
} else {
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
@ -152,7 +154,7 @@ func loadUDPServers(client Client, namespace string, svc v1alpha1.ServiceUDP) ([
for _, addr := range subset.Addresses {
servers = append(servers, dynamic.UDPServer{
Address: fmt.Sprintf("%s:%d", addr.IP, port),
Address: net.JoinHostPort(addr.IP, strconv.Itoa(int(port))),
})
}
}

View file

@ -0,0 +1,12 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service-bar
namespace: testing
subsets:
- addresses:
- ip: "2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b"
ports:
- name: http
port: 8080

View file

@ -0,0 +1,18 @@
kind: Ingress
apiVersion: networking.k8s.io/v1beta1
metadata:
name: example.com
namespace: testing
spec:
rules:
- http:
paths:
- path: /bar
backend:
serviceName: service-bar
servicePort: 8080
- path: /foo
backend:
serviceName: service-foo
servicePort: 8080

View file

@ -0,0 +1,26 @@
kind: Service
apiVersion: v1
metadata:
name: service-bar
namespace: testing
spec:
ports:
- name: http
port: 8080
clusterIp: "fc00:f853:ccd:e793::1"
type: ClusterIP
---
kind: Service
apiVersion: v1
metadata:
name: service-foo
namespace: testing
spec:
ports:
- name: http
port: 8080
type: ExternalName
externalName: "2001:0db8:3c4d:0015:0000:0000:1a2f:2a3b"

View file

@ -5,8 +5,10 @@ import (
"errors"
"fmt"
"math"
"net"
"os"
"sort"
"strconv"
"strings"
"time"
@ -479,9 +481,10 @@ func loadService(client Client, namespace string, backend networkingv1beta1.Ingr
if service.Spec.Type == corev1.ServiceTypeExternalName {
protocol := getProtocol(portSpec, portSpec.Name, svcConfig)
hostPort := net.JoinHostPort(service.Spec.ExternalName, strconv.Itoa(int(portSpec.Port)))
svc.LoadBalancer.Servers = []dynamic.Server{
{URL: fmt.Sprintf("%s://%s:%d", protocol, service.Spec.ExternalName, portSpec.Port)},
{URL: fmt.Sprintf("%s://%s", protocol, hostPort)},
}
return svc, nil
@ -516,8 +519,10 @@ func loadService(client Client, namespace string, backend networkingv1beta1.Ingr
protocol := getProtocol(portSpec, portName, svcConfig)
for _, addr := range subset.Addresses {
hostPort := net.JoinHostPort(addr.IP, strconv.Itoa(int(port)))
svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s:%d", protocol, addr.IP, port),
URL: fmt.Sprintf("%s://%s", protocol, hostPort),
})
}
}

View file

@ -661,6 +661,47 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
},
},
},
{
desc: "Ingress with IPv6 endpoints",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"example-com-testing-bar": {
Rule: "PathPrefix(`/bar`)",
Service: "testing-service-bar-8080",
},
"example-com-testing-foo": {
Rule: "PathPrefix(`/foo`)",
Service: "testing-service-foo-8080",
},
},
Services: map[string]*dynamic.Service{
"testing-service-bar-8080": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://[2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b]:8080",
},
},
PassHostHeader: Bool(true),
},
},
"testing-service-foo-8080": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://[2001:0db8:3c4d:0015:0000:0000:1a2f:2a3b]:8080",
},
},
PassHostHeader: Bool(true),
},
},
},
},
},
},
{
desc: "TLS support",
expected: &dynamic.Configuration{