From 3b4c8ba43978cf5ad095bb04d3ad6e90e4d152e4 Mon Sep 17 00:00:00 2001 From: Daniel Tomcej Date: Tue, 25 Feb 2020 01:12:04 -0800 Subject: [PATCH] Use consistent protocol determination --- .../routing/providers/kubernetes-crd.md | 10 +++ .../kubernetes/crd/kubernetes_http.go | 36 ++++++---- .../kubernetes/crd/kubernetes_test.go | 69 +++++++++++++++++++ 3 files changed, 101 insertions(+), 14 deletions(-) diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index e1ea957dd..523d38a22 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -313,6 +313,16 @@ Register the `IngressRoute` kind in the Kubernetes cluster before creating `Ingr tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= ``` +!!! important "Configuring Backend Protocol" + + There are 3 ways to configure the backend protocol for communication between Traefik and your pods: + + - Setting the scheme explicitly (http/https/h2c) + - Configuring the name of the kubernetes service port to start with https (https) + - Setting the kubernetes service port to use port 443 (https) + + If you do not configure the above, Traefik will assume an http connection. + ### Kind: `Middleware` `Middleware` is the CRD implementation of a [Traefik middleware](../../middlewares/overview.md). diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 7dbcf2a5f..3582951a1 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -307,9 +307,9 @@ func (c configBuilder) loadServers(fallbackNamespace string, svc v1alpha1.LoadBa var servers []dynamic.Server if service.Spec.Type == corev1.ServiceTypeExternalName { - protocol := "http" - if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") { - protocol = "https" + protocol, err := parseServiceProtocol(svc.Scheme, portSpec.Name, portSpec.Port) + if err != nil { + return nil, err } return append(servers, dynamic.Server{ @@ -341,17 +341,9 @@ func (c configBuilder) loadServers(fallbackNamespace string, svc v1alpha1.LoadBa return nil, fmt.Errorf("cannot define a port for %s/%s", namespace, sanitizedName) } - protocol := httpProtocol - scheme := svc.Scheme - switch scheme { - case httpProtocol, httpsProtocol, "h2c": - protocol = scheme - case "": - if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, httpsProtocol) { - protocol = httpsProtocol - } - default: - return nil, fmt.Errorf("invalid scheme %q specified", scheme) + protocol, err := parseServiceProtocol(svc.Scheme, portSpec.Name, portSpec.Port) + if err != nil { + return nil, err } for _, addr := range subset.Addresses { @@ -448,3 +440,19 @@ func getTLSHTTP(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, k8sCli return nil } + +// parseServiceProtocol parses the scheme, port name, and number to determine the correct protocol. +// an error is returned if the scheme provided is invalid. +func parseServiceProtocol(providedScheme string, portName string, portNumber int32) (string, error) { + switch providedScheme { + case httpProtocol, httpsProtocol, "h2c": + return providedScheme, nil + case "": + if portNumber == 443 || strings.HasPrefix(portName, httpsProtocol) { + return httpsProtocol, nil + } + return httpProtocol, nil + } + + return "", fmt.Errorf("invalid scheme %q specified", providedScheme) +} diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 7444dd353..f80aae884 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -2311,3 +2311,72 @@ func TestLoadIngressRoutes(t *testing.T) { }) } } + +func TestParseServiceProtocol(t *testing.T) { + testCases := []struct { + desc string + scheme string + portName string + portNumber int32 + expected string + expectedError bool + }{ + { + desc: "Empty scheme and name", + scheme: "", + portName: "", + portNumber: 1000, + expected: "http", + }, + { + desc: "h2c scheme and emptyname", + scheme: "h2c", + portName: "", + portNumber: 1000, + expected: "h2c", + }, + { + desc: "invalid scheme", + scheme: "foo", + portName: "", + portNumber: 1000, + expectedError: true, + }, + { + desc: "Empty scheme and https name", + scheme: "", + portName: "https-secure", + portNumber: 1000, + expected: "https", + }, + { + desc: "Empty scheme and port number", + scheme: "", + portName: "", + portNumber: 443, + expected: "https", + }, + { + desc: "https scheme", + scheme: "https", + portName: "", + portNumber: 1000, + expected: "https", + }, + } + + for _, test := range testCases { + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + protocol, err := parseServiceProtocol(test.scheme, test.portName, test.portNumber) + if test.expectedError { + assert.Error(t, err) + } else { + assert.Equal(t, test.expected, protocol) + } + }) + } +}